summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/account.cc2
-rw-r--r--src/amount.cc54
-rw-r--r--src/amount.h9
-rw-r--r--src/annotate.cc4
-rw-r--r--src/balance.cc9
-rw-r--r--src/balance.h13
-rw-r--r--src/chain.cc12
-rw-r--r--src/commodity.cc14
-rw-r--r--src/commodity.h12
-rw-r--r--src/draft.cc3
-rw-r--r--src/draft.h2
-rw-r--r--src/expr.h2
-rw-r--r--src/exprbase.h3
-rw-r--r--src/filters.cc39
-rw-r--r--src/format.h2
-rw-r--r--src/item.cc60
-rw-r--r--src/item.h10
-rw-r--r--src/journal.cc21
-rw-r--r--src/journal.h3
-rw-r--r--src/op.cc2
-rw-r--r--src/output.cc9
-rw-r--r--src/pool.cc27
-rw-r--r--src/pool.h10
-rw-r--r--src/post.cc26
-rw-r--r--src/post.h17
-rw-r--r--src/precmd.cc2
-rw-r--r--src/precmd.h2
-rw-r--r--src/predicate.h2
-rw-r--r--src/py_account.cc8
-rw-r--r--src/py_amount.cc83
-rw-r--r--src/py_balance.cc49
-rw-r--r--src/py_commodity.cc217
-rw-r--r--src/py_journal.cc8
-rw-r--r--src/py_utils.cc9
-rw-r--r--src/py_value.cc85
-rw-r--r--src/pyutils.h29
-rw-r--r--src/query.cc2
-rw-r--r--src/query.h2
-rw-r--r--src/quotes.cc16
-rw-r--r--src/report.cc61
-rw-r--r--src/report.h11
-rw-r--r--src/scope.cc24
-rw-r--r--src/scope.h2
-rw-r--r--src/session.cc4
-rw-r--r--src/textual.cc402
-rw-r--r--src/times.cc27
-rw-r--r--src/value.cc26
-rw-r--r--src/value.h2
-rw-r--r--src/xact.cc13
49 files changed, 923 insertions, 528 deletions
diff --git a/src/account.cc b/src/account.cc
index e6c7af56..f8729409 100644
--- a/src/account.cc
+++ b/src/account.cc
@@ -512,7 +512,7 @@ void account_t::xdata_t::details_t::update(post_t& post,
if (post.has_flags(POST_VIRTUAL))
posts_virtuals_count++;
- if (gather_all)
+ if (gather_all && post.pos)
filenames.insert(post.pos->pathname);
date_t date = post.date();
diff --git a/src/amount.cc b/src/amount.cc
index 82b93931..eddbca18 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -106,8 +106,6 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
-shared_ptr<commodity_pool_t> amount_t::current_pool;
-
bool amount_t::is_initialized = false;
namespace {
@@ -203,7 +201,7 @@ namespace {
}
}
-void amount_t::initialize(shared_ptr<commodity_pool_t> pool)
+void amount_t::initialize()
{
if (! is_initialized) {
mpz_init(temp);
@@ -211,26 +209,35 @@ void amount_t::initialize(shared_ptr<commodity_pool_t> pool)
mpfr_init(tempf);
mpfr_init(tempfb);
+ commodity_pool_t::current_pool.reset(new commodity_pool_t);
+
+ // Add time commodity conversions, so that timelog's may be parsed
+ // in terms of seconds, but reported as minutes or hours.
+ if (commodity_t * commodity = commodity_pool_t::current_pool->create("s"))
+ commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
+ else
+ assert(false);
+
+ // Add a "percentile" commodity
+ if (commodity_t * commodity = commodity_pool_t::current_pool->create("%"))
+ commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
+ else
+ assert(false);
+
is_initialized = true;
}
- current_pool = pool;
-}
-
-void amount_t::initialize()
-{
- initialize(shared_ptr<commodity_pool_t>(new commodity_pool_t));
}
void amount_t::shutdown()
{
- current_pool.reset();
-
if (is_initialized) {
mpz_clear(temp);
mpq_clear(tempq);
mpfr_clear(tempf);
mpfr_clear(tempfb);
+ commodity_pool_t::current_pool.reset();
+
is_initialized = false;
}
}
@@ -670,7 +677,7 @@ amount_t::value(const bool primary_only,
if (in_terms_of && commodity() == *in_terms_of) {
return *this;
}
- else if (is_annotated() && annotation().price &&
+ else if (has_annotation() && annotation().price &&
annotation().has_flags(ANNOTATION_PRICE_FIXATED)) {
return (*annotation().price * number()).rounded();
}
@@ -696,7 +703,7 @@ amount_t::value(const bool primary_only,
amount_t amount_t::price() const
{
- if (is_annotated() && annotation().price) {
+ if (has_annotation() && annotation().price) {
amount_t temp(*annotation().price);
temp *= *this;
DEBUG("amount.price", "Returning price of " << *this << " = " << temp);
@@ -776,7 +783,8 @@ bool amount_t::fits_in_long() const
commodity_t& amount_t::commodity() const
{
- return has_commodity() ? *commodity_ : *current_pool->null_commodity;
+ return (has_commodity() ?
+ *commodity_ : *commodity_pool_t::current_pool->null_commodity);
}
bool amount_t::has_commodity() const
@@ -794,7 +802,7 @@ void amount_t::annotate(const annotation_t& details)
else if (! has_commodity())
return; // ignore attempt to annotate a "bare commodity
- if (commodity().is_annotated()) {
+ if (commodity().has_annotation()) {
this_ann = &as_annotated_commodity(commodity());
this_base = &this_ann->referent();
} else {
@@ -816,15 +824,15 @@ void amount_t::annotate(const annotation_t& details)
DEBUG("amounts.commodities", "Annotated amount is " << *this);
}
-bool amount_t::is_annotated() const
+bool amount_t::has_annotation() const
{
if (! quantity)
throw_(amount_error,
_("Cannot determine if an uninitialized amount's commodity is annotated"));
- assert(! has_commodity() || ! commodity().is_annotated() ||
+ assert(! has_commodity() || ! commodity().has_annotation() ||
as_annotated_commodity(commodity()).details);
- return has_commodity() && commodity().is_annotated();
+ return has_commodity() && commodity().has_annotation();
}
annotation_t& amount_t::annotation()
@@ -833,7 +841,7 @@ annotation_t& amount_t::annotation()
throw_(amount_error,
_("Cannot return commodity annotation details of an uninitialized amount"));
- if (! commodity().is_annotated())
+ if (! commodity().has_annotation())
throw_(amount_error,
_("Request for annotation details from an unannotated amount"));
@@ -963,15 +971,16 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
if (symbol.empty()) {
commodity_ = NULL;
} else {
- commodity_ = current_pool->find(symbol);
+ commodity_ = commodity_pool_t::current_pool->find(symbol);
if (! commodity_) {
- commodity_ = current_pool->create(symbol);
+ commodity_ = commodity_pool_t::current_pool->create(symbol);
newly_created = true;
}
assert(commodity_);
if (details)
- commodity_ = current_pool->find_or_create(*commodity_, 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
@@ -1206,7 +1215,6 @@ void to_xml(std::ostream& out, const amount_t& amt, bool commodity_details)
template<class Archive>
void amount_t::serialize(Archive& ar, const unsigned int /* version */)
{
- ar & current_pool;
ar & is_initialized;
ar & quantity;
ar & commodity_;
diff --git a/src/amount.h b/src/amount.h
index c75370e3..a37efdb8 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -97,12 +97,8 @@ class amount_t
ordered_field_operators<amount_t, long> > > >
{
public:
- /** Indicates which commodity pool should be used. */
- static shared_ptr<commodity_pool_t> current_pool;
-
/** Ready the amount subsystem for use.
@note Normally called by session_t::initialize(). */
- static void initialize(shared_ptr<commodity_pool_t> pool);
static void initialize();
/** Shutdown the amount subsystem and free all resources.
@note Normally called by session_t::shutdown(). */
@@ -500,6 +496,9 @@ public:
long to_long() const;
bool fits_in_long() const;
+ operator string() const {
+ return to_string();
+ }
string to_string() const;
string to_fullstring() const;
string quantity_string() const;
@@ -577,7 +576,7 @@ public:
been stripped.
*/
void annotate(const annotation_t& details);
- bool is_annotated() const;
+ bool has_annotation() const;
annotation_t& annotation();
const annotation_t& annotation() const {
diff --git a/src/annotate.cc b/src/annotate.cc
index bd5a8ef8..146a7afd 100644
--- a/src/annotate.cc
+++ b/src/annotate.cc
@@ -135,13 +135,13 @@ void annotation_t::print(std::ostream& out, bool keep_base) const
bool keep_details_t::keep_all(const commodity_t& comm) const
{
- return (! comm.is_annotated() ||
+ return (! comm.has_annotation() ||
(keep_price && keep_date && keep_tag && ! only_actuals));
}
bool keep_details_t::keep_any(const commodity_t& comm) const
{
- return comm.is_annotated() && (keep_price || keep_date || keep_tag);
+ return comm.has_annotation() && (keep_price || keep_date || keep_tag);
}
bool annotated_commodity_t::operator==(const commodity_t& comm) const
diff --git a/src/balance.cc b/src/balance.cc
index 59eb4d92..4fcc83fa 100644
--- a/src/balance.cc
+++ b/src/balance.cc
@@ -43,21 +43,21 @@ balance_t::balance_t(const double val)
{
TRACE_CTOR(balance_t, "const double");
amounts.insert
- (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
+ (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
}
balance_t::balance_t(const unsigned long val)
{
TRACE_CTOR(balance_t, "const unsigned long");
amounts.insert
- (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
+ (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
}
balance_t::balance_t(const long val)
{
TRACE_CTOR(balance_t, "const long");
amounts.insert
- (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
+ (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
}
balance_t& balance_t::operator+=(const balance_t& bal)
@@ -271,7 +271,8 @@ void balance_t::print(std::ostream& out,
if (pair.second)
sorted.push_back(&pair.second);
- std::stable_sort(sorted.begin(), sorted.end(), compare_amount_commodities());
+ std::stable_sort(sorted.begin(), sorted.end(),
+ commodity_t::compare_by_commodity());
foreach (const amount_t * amount, sorted) {
int width;
diff --git a/src/balance.h b/src/balance.h
index 8a40dea9..826de134 100644
--- a/src/balance.h
+++ b/src/balance.h
@@ -461,6 +461,15 @@ public:
* Conversion methods. A balance can be converted to an amount, but
* only if contains a single component amount.
*/
+ operator string() const {
+ return to_string();
+ }
+ string to_string() const {
+ std::ostringstream buf;
+ print(buf);
+ return buf.str();
+ }
+
amount_t to_amount() const {
if (is_empty())
throw_(balance_error, _("Cannot convert an empty balance to an amount"));
@@ -532,8 +541,8 @@ public:
void print(std::ostream& out,
const int first_width = -1,
const int latter_width = -1,
- const bool right_justify = true,
- const bool colorize = true) const;
+ const bool right_justify = false,
+ const bool colorize = false) const;
/**
* Debugging methods. There are two methods defined to help with
diff --git a/src/chain.cc b/src/chain.cc
index 113a71d8..ecb39e0b 100644
--- a/src/chain.cc
+++ b/src/chain.cc
@@ -158,11 +158,21 @@ post_handler_ptr chain_post_handlers(report_t& report,
report.session.journal->master,
report.HANDLER(date_).str(),
report));
- if (report.HANDLED(account_))
+
+ if (report.HANDLED(account_)) {
handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT,
report.session.journal->master,
report.HANDLER(account_).str(),
report));
+ }
+ else if (report.HANDLED(pivot_)) {
+ string pivot = report.HANDLER(pivot_).str();
+ pivot = string("\"") + pivot + ":\" + tag(/" + pivot + "/)";
+ handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT,
+ report.session.journal->master, pivot,
+ report));
+ }
+
if (report.HANDLED(payee_))
handler.reset(new transfer_details(handler, transfer_details::SET_PAYEE,
report.session.journal->master,
diff --git a/src/commodity.cc b/src/commodity.cc
index b76c7896..24016830 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -587,8 +587,8 @@ bool commodity_t::valid() const
return true;
}
-bool compare_amount_commodities::operator()(const amount_t * left,
- const amount_t * right) const
+bool commodity_t::compare_by_commodity::operator()(const amount_t * left,
+ const amount_t * right) const
{
commodity_t& leftcomm(left->commodity());
commodity_t& rightcomm(right->commodity());
@@ -600,11 +600,11 @@ bool compare_amount_commodities::operator()(const amount_t * left,
if (cmp != 0)
return cmp < 0;
- if (! leftcomm.is_annotated()) {
- return rightcomm.is_annotated();
+ if (! leftcomm.has_annotation()) {
+ return rightcomm.has_annotation();
}
- else if (! rightcomm.is_annotated()) {
- return ! leftcomm.is_annotated();
+ else if (! rightcomm.has_annotation()) {
+ return ! leftcomm.has_annotation();
}
else {
annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
@@ -675,7 +675,7 @@ void to_xml(std::ostream& out, const commodity_t& comm,
}
if (commodity_details) {
- if (comm.is_annotated())
+ if (comm.has_annotation())
to_xml(out, as_annotated_commodity(comm).details);
if (comm.varied_history()) {
diff --git a/src/commodity.h b/src/commodity.h
index 42cc6d8f..d2d8af21 100644
--- a/src/commodity.h
+++ b/src/commodity.h
@@ -185,7 +185,7 @@ protected:
symbol(_symbol), precision(0), searched(false) {
TRACE_CTOR(base_t, "const string&");
}
- ~base_t() {
+ virtual ~base_t() {
TRACE_DTOR(base_t);
}
@@ -251,7 +251,7 @@ public:
return *this;
}
- bool is_annotated() const {
+ bool has_annotation() const {
return annotated;
}
@@ -386,6 +386,10 @@ public:
bool valid() const;
+ struct compare_by_commodity {
+ bool operator()(const amount_t * left, const amount_t * right) const;
+ };
+
#if defined(HAVE_BOOST_SERIALIZATION)
private:
supports_flags<uint_least16_t> temp_flags;
@@ -419,10 +423,6 @@ inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
return out;
}
-struct compare_amount_commodities {
- bool operator()(const amount_t * left, const amount_t * right) const;
-};
-
void to_xml(std::ostream& out, const commodity_t& comm,
bool commodity_details = false);
diff --git a/src/draft.cc b/src/draft.cc
index 8478a31d..fc53e956 100644
--- a/src/draft.cc
+++ b/src/draft.cc
@@ -458,6 +458,9 @@ xact_t * draft_t::insert(journal_t& journal)
*post.cost *= new_post->amount;
post.cost->set_commodity(cost_commodity);
}
+ else if (new_post->amount.sign() < 0) {
+ new_post->cost->in_place_negate();
+ }
new_post->cost = *post.cost;
DEBUG("derive.xact", "Copied over posting cost");
diff --git a/src/draft.h b/src/draft.h
index 003dcefa..93e98ff5 100644
--- a/src/draft.h
+++ b/src/draft.h
@@ -86,7 +86,7 @@ public:
if (! args.empty())
parse_args(args);
}
- ~draft_t() {
+ virtual ~draft_t() {
TRACE_DTOR(draft_t);
}
diff --git a/src/expr.h b/src/expr.h
index 97e8c948..638855b1 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -87,7 +87,7 @@ public:
parse(in, flags);
}
- ~expr_t() throw() {
+ virtual ~expr_t() {
TRACE_DTOR(expr_t);
}
diff --git a/src/exprbase.h b/src/exprbase.h
index d2bf5a6d..0b3466b0 100644
--- a/src/exprbase.h
+++ b/src/exprbase.h
@@ -92,8 +92,7 @@ public:
{
TRACE_CTOR(expr_base_t, "scope_t *");
}
-
- ~expr_base_t() throw() {
+ virtual ~expr_base_t() {
TRACE_DTOR(expr_base_t);
}
diff --git a/src/filters.cc b/src/filters.cc
index 39097c58..2926eb08 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -430,12 +430,22 @@ changed_value_posts::changed_value_posts(post_handler_ptr handler,
display_total_expr = report.HANDLER(display_total_).expr;
changed_values_only = report.HANDLED(revalued_only);
+ string gains_equity_account_name;
+ if (report.HANDLED(unrealized_gains_))
+ gains_equity_account_name = report.HANDLER(unrealized_gains_).str();
+ else
+ gains_equity_account_name = _("Equity:Unrealized Gains");
gains_equity_account =
- report.session.journal->master->find_account(_("Equity:Unrealized Gains"));
+ report.session.journal->master->find_account(gains_equity_account_name);
gains_equity_account->add_flags(ACCOUNT_GENERATED);
+ string losses_equity_account_name;
+ if (report.HANDLED(unrealized_losses_))
+ losses_equity_account_name = report.HANDLER(unrealized_losses_).str();
+ else
+ losses_equity_account_name = _("Equity:Unrealized Losses");
losses_equity_account =
- report.session.journal->master->find_account(_("Equity:Unrealized Losses"));
+ report.session.journal->master->find_account(losses_equity_account_name);
losses_equity_account->add_flags(ACCOUNT_GENERATED);
}
@@ -783,12 +793,25 @@ void transfer_details::operator()(post_t& post)
break;
case SET_ACCOUNT: {
- std::list<string> account_names;
- temp.account->remove_post(&temp);
- split_string(substitute.to_string(), ':', account_names);
- temp.account = create_temp_account_from_path(account_names, temps,
- xact.journal->master);
- temp.account->add_post(&temp);
+ string account_name = substitute.to_string();
+ if (! account_name.empty() &&
+ account_name[account_name.length() - 1] != ':') {
+ account_t * prev_account = temp.account;
+ temp.account->remove_post(&temp);
+
+ account_name += ':';
+ account_name += prev_account->fullname();
+
+ std::list<string> account_names;
+ split_string(account_name, ':', account_names);
+ temp.account = create_temp_account_from_path(account_names, temps,
+ xact.journal->master);
+ temp.account->add_post(&temp);
+
+ temp.account->add_flags(prev_account->flags());
+ if (prev_account->has_xdata())
+ temp.account->xdata().add_flags(prev_account->xdata().flags());
+ }
break;
}
diff --git a/src/format.h b/src/format.h
index fc1272aa..a2bf1015 100644
--- a/src/format.h
+++ b/src/format.h
@@ -132,7 +132,7 @@ public:
if (! _str.empty())
parse_format(_str);
}
- ~format_t() {
+ virtual ~format_t() {
TRACE_DTOR(format_t);
}
diff --git a/src/item.cc b/src/item.cc
index da6429ed..8d1ba34f 100644
--- a/src/item.cc
+++ b/src/item.cc
@@ -115,7 +115,8 @@ void item_t::set_tag(const string& tag,
assert(result.second);
}
-void item_t::parse_tags(const char * p, optional<date_t::year_type> current_year)
+void item_t::parse_tags(const char * p,
+ optional<date_t::year_type> current_year)
{
if (const char * b = std::strchr(p, '[')) {
if (*(b + 1) != '\0' &&
@@ -164,7 +165,8 @@ void item_t::parse_tags(const char * p, optional<date_t::year_type> current_year
}
}
-void item_t::append_note(const char * p, optional<date_t::year_type> current_year)
+void item_t::append_note(const char * p,
+ optional<date_t::year_type> current_year)
{
if (note) {
*note += '\n';
@@ -197,6 +199,9 @@ namespace {
value_t get_date(item_t& item) {
return item.date();
}
+ value_t get_actual_date(item_t& item) {
+ return item.actual_date();
+ }
value_t get_effective_date(item_t& item) {
if (optional<date_t> effective = item.effective_date())
return *effective;
@@ -236,11 +241,39 @@ namespace {
return false;
}
- value_t get_tag(call_scope_t& scope) {
- in_context_t<item_t> env(scope, "s");
- if (optional<string> value = env->get_tag(env.get<string>(0)))
- return string_value(*value);
- return string_value(empty_string);
+ value_t get_tag(call_scope_t& args) {
+ item_t& item(find_scope<item_t>(args));
+ optional<string> str;
+
+ if (args.size() == 1) {
+ if (args[0].is_string())
+ str = item.get_tag(args[0].as_string());
+ else if (args[0].is_mask())
+ str = item.get_tag(args[0].as_mask());
+ else
+ throw_(std::runtime_error,
+ _("Expected string or mask for argument 1, but received %1")
+ << args[0].label());
+ }
+ else if (args.size() == 2) {
+ if (args[0].is_mask() && args[1].is_mask())
+ str = item.get_tag(args[0].to_mask(), args[1].to_mask());
+ else
+ throw_(std::runtime_error,
+ _("Expected masks for arguments 1 and 2, but received %1 and %2")
+ << args[0].label() << args[1].label());
+ }
+ else if (args.size() == 0) {
+ throw_(std::runtime_error, _("Too few arguments to function"));
+ }
+ else {
+ throw_(std::runtime_error, _("Too many arguments to function"));
+ }
+
+ if (str)
+ return string_value(*str);
+ else
+ return string_value(empty_string);
}
value_t get_pathname(item_t& item) {
@@ -266,6 +299,10 @@ namespace {
return item.pos ? long(item.pos->end_line) : 0L;
}
+ value_t get_seq(item_t& item) {
+ return item.pos ? long(item.pos->sequence) : 0L;
+ }
+
value_t get_depth(item_t&) {
return 0L;
}
@@ -317,6 +354,8 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind,
case 'a':
if (name == "actual")
return WRAP_FUNCTOR(get_wrapper<&get_actual>);
+ else if (name == "actual_date")
+ return WRAP_FUNCTOR(get_wrapper<&get_actual_date>);
break;
case 'b':
@@ -386,6 +425,8 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind,
case 's':
if (name == "status")
return WRAP_FUNCTOR(get_wrapper<&get_status>);
+ else if (name == "seq")
+ return WRAP_FUNCTOR(get_wrapper<&get_seq>);
break;
case 't':
@@ -435,9 +476,12 @@ void print_item(std::ostream& out, const item_t& item, const string& prefix)
string item_context(const item_t& item, const string& desc)
{
+ if (! item.pos)
+ return empty_string;
+
std::streamoff len = item.pos->end_pos - item.pos->beg_pos;
if (! len)
- return _("<no item context>");
+ return empty_string;
assert(len > 0);
assert(len < 2048);
diff --git a/src/item.h b/src/item.h
index 84385eb7..f23350fc 100644
--- a/src/item.h
+++ b/src/item.h
@@ -53,8 +53,10 @@ struct position_t
std::size_t beg_line;
istream_pos_type end_pos;
std::size_t end_line;
+ std::size_t sequence;
- position_t() : beg_pos(0), beg_line(0), end_pos(0), end_line(0) {
+ position_t()
+ : beg_pos(0), beg_line(0), end_pos(0), end_line(0), sequence(0) {
TRACE_CTOR(position_t, "");
}
position_t(const position_t& pos) {
@@ -72,6 +74,7 @@ struct position_t
beg_line = pos.beg_line;
end_pos = pos.end_pos;
end_line = pos.end_line;
+ sequence = pos.sequence;
}
return *this;
}
@@ -89,6 +92,7 @@ private:
ar & beg_line;
ar & end_pos;
ar & end_line;
+ ar & sequence;
}
#endif // HAVE_BOOST_SERIALIZATION
};
@@ -169,6 +173,10 @@ public:
return *effective;
return *_date;
}
+ virtual date_t actual_date() const {
+ assert(_date);
+ return *_date;
+ }
virtual optional<date_t> effective_date() const {
return _date_eff;
}
diff --git a/src/journal.cc b/src/journal.cc
index 2366ce30..5aa2e7f7 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -64,9 +64,8 @@ journal_t::~journal_t()
{
TRACE_DTOR(journal_t);
- // Don't bother unhooking each xact's posts from the
- // accounts they refer to, because all accounts are about to
- // be deleted.
+ // Don't bother unhooking each xact's posts from the accounts they refer to,
+ // because all accounts are about to be deleted.
foreach (xact_t * xact, xacts)
checked_delete(xact);
@@ -77,7 +76,6 @@ journal_t::~journal_t()
checked_delete(xact);
checked_delete(master);
- commodity_pool.reset();
}
void journal_t::initialize()
@@ -85,21 +83,6 @@ void journal_t::initialize()
master = new account_t;
bucket = NULL;
was_loaded = false;
-
- commodity_pool.reset(new commodity_pool_t);
-
- // Add time commodity conversions, so that timelog's may be parsed
- // in terms of seconds, but reported as minutes or hours.
- if (commodity_t * commodity = commodity_pool->create("s"))
- commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
- else
- assert(false);
-
- // Add a "percentile" commodity
- if (commodity_t * commodity = commodity_pool->create("%"))
- commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
- else
- assert(false);
}
void journal_t::add_account(account_t * acct)
diff --git a/src/journal.h b/src/journal.h
index f7124736..8d59e3b4 100644
--- a/src/journal.h
+++ b/src/journal.h
@@ -47,7 +47,6 @@
namespace ledger {
-class commodity_pool_t;
class xact_base_t;
class xact_t;
class auto_xact_t;
@@ -112,8 +111,6 @@ public:
std::list<fileinfo_t> sources;
bool was_loaded;
- shared_ptr<commodity_pool_t> commodity_pool;
-
journal_t();
journal_t(const path& pathname);
journal_t(const string& str);
diff --git a/src/op.cc b/src/op.cc
index 2815ac1a..f38fc86b 100644
--- a/src/op.cc
+++ b/src/op.cc
@@ -624,7 +624,7 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
}
if (! symbol.empty()) {
- if (amount_t::current_pool->find(symbol))
+ if (commodity_pool_t::current_pool->find(symbol))
out << '@';
out << symbol;
}
diff --git a/src/output.cc b/src/output.cc
index 71ec6d88..bb7eff5c 100644
--- a/src/output.cc
+++ b/src/output.cc
@@ -152,13 +152,12 @@ format_accounts::format_accounts(report_t& _report,
std::size_t format_accounts::post_account(account_t& account, const bool flat)
{
+ if (! flat && account.parent)
+ post_account(*account.parent, flat);
+
if (account.xdata().has_flags(ACCOUNT_EXT_TO_DISPLAY) &&
! account.xdata().has_flags(ACCOUNT_EXT_DISPLAYED)) {
- if (! flat && account.parent &&
- account.parent->xdata().has_flags(ACCOUNT_EXT_TO_DISPLAY) &&
- ! account.parent->xdata().has_flags(ACCOUNT_EXT_DISPLAYED))
- post_account(*account.parent, flat);
-
+ DEBUG("account.display", "Displaying account: " << account.fullname());
account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
bind_scope_t bound_scope(report, account);
diff --git a/src/pool.cc b/src/pool.cc
index b08c8fad..f895a8bc 100644
--- a/src/pool.cc
+++ b/src/pool.cc
@@ -39,6 +39,8 @@
namespace ledger {
+shared_ptr<commodity_pool_t> commodity_pool_t::current_pool;
+
commodity_pool_t::commodity_pool_t()
: default_commodity(NULL), keep_base(false),
quote_leeway(86400), get_quotes(false),
@@ -245,7 +247,7 @@ commodity_pool_t::exchange(const amount_t& amount,
current_annotation = &as_annotated_commodity(commodity).details;
amount_t per_unit_cost =
- (is_per_unit || amount.is_realzero() ? cost : cost / amount).abs();
+ (is_per_unit || amount.is_realzero()) ? cost.abs() : (cost / amount).abs();
DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost);
@@ -253,7 +255,7 @@ commodity_pool_t::exchange(const amount_t& amount,
exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
cost_breakdown_t breakdown;
- breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
+ breakdown.final_cost = ! is_per_unit ? cost : cost * amount.abs();
DEBUG("commodity.prices.add",
"exchange: final-cost = " << breakdown.final_cost);
@@ -284,7 +286,8 @@ commodity_pool_t::exchange(const amount_t& amount,
return breakdown;
}
-optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
+optional<std::pair<commodity_t *, price_point_t> >
+commodity_pool_t::parse_price_directive(char * line, bool do_not_add_price)
{
char * date_field_ptr = line;
char * time_field_ptr = next_element(date_field_ptr);
@@ -293,6 +296,7 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
char * symbol_and_price;
datetime_t datetime;
+ string symbol;
if (std::isdigit(time_field_ptr[0])) {
symbol_and_price = next_element(time_field_ptr);
@@ -305,26 +309,27 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
datetime = datetime_t(parse_date(date_field));
}
else {
- symbol_and_price = date_field_ptr;
+ symbol = date_field_ptr;
+ symbol_and_price = time_field_ptr;
datetime = CURRENT_TIME();
}
- string symbol;
- commodity_t::parse_symbol(symbol_and_price, symbol);
+ if (symbol.empty())
+ commodity_t::parse_symbol(symbol_and_price, symbol);
price_point_t point;
point.when = datetime;
- point.price.parse(symbol_and_price);
+ point.price.parse(symbol_and_price, PARSE_NO_MIGRATE);
VERIFY(point.price.valid());
DEBUG("commodity.download", "Looking up symbol: " << symbol);
- if (commodity_t * commodity =
- amount_t::current_pool->find_or_create(symbol)) {
+ if (commodity_t * commodity = find_or_create(symbol)) {
DEBUG("commodity.download", "Adding price for " << symbol << ": "
<< point.when << " " << point.price);
- commodity->add_price(point.when, point.price, true);
+ if (! do_not_add_price)
+ commodity->add_price(point.when, point.price, true);
commodity->add_flags(COMMODITY_KNOWN);
- return point;
+ return std::pair<commodity_t *, price_point_t>(commodity, point);
}
return none;
diff --git a/src/pool.h b/src/pool.h
index 7328df9d..995ab23c 100644
--- a/src/pool.h
+++ b/src/pool.h
@@ -57,6 +57,7 @@ struct cost_breakdown_t
class commodity_pool_t : public noncopyable
{
+public:
/**
* The commodities collection in commodity_pool_t maintains pointers to all
* the commodities which have ever been created by the user, whether
@@ -65,7 +66,6 @@ class commodity_pool_t : public noncopyable
*/
typedef std::map<string, commodity_t *> commodities_map;
-public:
commodities_map commodities;
commodity_t * null_commodity;
commodity_t * default_commodity;
@@ -76,13 +76,15 @@ public:
long quote_leeway; // --leeway=
bool get_quotes; // --download
+ static shared_ptr<commodity_pool_t> current_pool;
+
function<optional<price_point_t>
(commodity_t& commodity, const optional<commodity_t&>& in_terms_of)>
get_commodity_quote;
explicit commodity_pool_t();
- ~commodity_pool_t() {
+ virtual ~commodity_pool_t() {
TRACE_DTOR(commodity_pool_t);
foreach (commodities_map::value_type pair, commodities)
checked_delete(pair.second);
@@ -121,7 +123,8 @@ public:
// Parse commodity prices from a textual representation
- optional<price_point_t> parse_price_directive(char * line);
+ optional<std::pair<commodity_t *, price_point_t> >
+ parse_price_directive(char * line, bool do_not_add_price = false);
commodity_t *
parse_price_expression(const std::string& str,
@@ -136,6 +139,7 @@ private:
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
+ ar & current_pool;
ar & commodities;
ar & null_commodity;
ar & default_commodity;
diff --git a/src/post.cc b/src/post.cc
index 34284e1b..43cfe55f 100644
--- a/src/post.cc
+++ b/src/post.cc
@@ -97,6 +97,18 @@ date_t post_t::date() const
return *_date;
}
+date_t post_t::actual_date() const
+{
+ if (xdata_ && is_valid(xdata_->date))
+ return xdata_->date;
+
+ if (! _date) {
+ assert(xact);
+ return xact->date();
+ }
+ return *_date;
+}
+
optional<date_t> post_t::effective_date() const
{
optional<date_t> date = item_t::effective_date();
@@ -405,6 +417,20 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind,
return item_t::lookup(kind, name);
}
+amount_t post_t::resolve_expr(scope_t& scope, expr_t& expr)
+{
+ bind_scope_t bound_scope(scope, *this);
+ value_t result(expr.calc(bound_scope));
+ if (result.is_long()) {
+ return result.to_amount();
+ } else {
+ if (! result.is_amount())
+ throw_(amount_error,
+ _("Amount expressions must result in a simple amount"));
+ return result.as_amount();
+ }
+}
+
bool post_t::valid() const
{
if (! xact) {
diff --git a/src/post.h b/src/post.h
index a0ab5ffd..8852e7b2 100644
--- a/src/post.h
+++ b/src/post.h
@@ -105,6 +105,7 @@ public:
const optional<mask_t>& value_mask = none) const;
virtual date_t date() const;
+ virtual date_t actual_date() const;
virtual optional<date_t> effective_date() const;
bool must_balance() const {
@@ -114,6 +115,8 @@ public:
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
+ amount_t resolve_expr(scope_t& scope, expr_t& expr);
+
bool valid() const;
struct xdata_t : public supports_flags<uint_least16_t>
@@ -198,6 +201,20 @@ public:
friend class xact_t;
+ struct compare_by_date_and_sequence
+ {
+ bool operator()(const post_t * left, const post_t * right) const {
+ gregorian::date_duration duration =
+ left->actual_date() - right->actual_date();
+ if (duration.days() == 0) {
+ return ((left->pos ? left->pos->sequence : 0) <
+ (right->pos ? right->pos->sequence : 0));
+ } else {
+ return duration.days() < 0;
+ }
+ }
+ };
+
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
diff --git a/src/precmd.cc b/src/precmd.cc
index 31249016..632caeae 100644
--- a/src/precmd.cc
+++ b/src/precmd.cc
@@ -174,7 +174,7 @@ value_t period_command(call_scope_t& args)
return NULL_VALUE;
}
-value_t args_command(call_scope_t& args)
+value_t query_command(call_scope_t& args)
{
report_t& report(find_scope<report_t>(args));
std::ostream& out(report.output_stream);
diff --git a/src/precmd.h b/src/precmd.h
index e0f81cf8..88d66ab2 100644
--- a/src/precmd.h
+++ b/src/precmd.h
@@ -52,7 +52,7 @@ value_t parse_command(call_scope_t& args);
value_t eval_command(call_scope_t& args);
value_t format_command(call_scope_t& args);
value_t period_command(call_scope_t& args);
-value_t args_command(call_scope_t& args);
+value_t query_command(call_scope_t& args);
} // namespace ledger
diff --git a/src/predicate.h b/src/predicate.h
index 943b5a12..b3b81f9b 100644
--- a/src/predicate.h
+++ b/src/predicate.h
@@ -75,7 +75,7 @@ public:
: expr_t(in, flags), what_to_keep(_what_to_keep) {
TRACE_CTOR(predicate_t, "std::istream&, keep_details_t, parse_flags_t");
}
- ~predicate_t() throw() {
+ virtual ~predicate_t() {
TRACE_DTOR(predicate_t);
}
diff --git a/src/py_account.cc b/src/py_account.cc
index d1d35cda..056cd722 100644
--- a/src/py_account.cc
+++ b/src/py_account.cc
@@ -32,6 +32,7 @@
#include <system.hh>
#include "pyinterp.h"
+#include "pyutils.h"
#include "account.h"
#include "post.h"
@@ -90,6 +91,10 @@ namespace {
return account.xdata();
}
+ PyObject * py_account_unicode(account_t& account) {
+ return str_to_py_unicode(account.fullname());
+ }
+
} // unnamed namespace
void export_account()
@@ -180,7 +185,8 @@ void export_account()
.def_readwrite("note", &account_t::note)
.def_readonly("depth", &account_t::depth)
- .def(self_ns::str(self))
+ .def("__str__", &account_t::fullname)
+ .def("__unicode__", py_account_unicode)
.def("fullname", &account_t::fullname)
.def("partial_name", &account_t::partial_name)
diff --git a/src/py_amount.cc b/src/py_amount.cc
index b44f3716..8fb507a3 100644
--- a/src/py_amount.cc
+++ b/src/py_amount.cc
@@ -45,26 +45,16 @@ using namespace boost::python;
namespace {
boost::optional<amount_t> py_value_0(const amount_t& amount) {
- return amount.value();
+ return amount.value(false, CURRENT_TIME());
}
boost::optional<amount_t> py_value_1(const amount_t& amount,
- const bool primary_only) {
- return amount.value(primary_only);
+ commodity_t& in_terms_of) {
+ return amount.value(false, CURRENT_TIME(), in_terms_of);
}
-
- boost::optional<amount_t>
- py_value_2(const amount_t& amount,
- const bool primary_only,
- const boost::optional<datetime_t>& moment) {
- return amount.value(primary_only, moment);
- }
-
- boost::optional<amount_t>
- py_value_3(const amount_t& amount,
- const bool primary_only,
- const boost::optional<datetime_t>& moment,
- const boost::optional<commodity_t&>& in_terms_of) {
- return amount.value(primary_only, moment, in_terms_of);
+ boost::optional<amount_t> py_value_2(const amount_t& amount,
+ commodity_t& in_terms_of,
+ datetime_t& moment) {
+ return amount.value(false, moment, in_terms_of);
}
void py_parse_2(amount_t& amount, object in, unsigned char flags) {
@@ -97,8 +87,19 @@ namespace {
}
}
- void py_amount_initialize() {
- amount_t::initialize();
+ annotation_t& py_amount_annotation(amount_t& amount) {
+ return amount.annotation();
+ }
+
+ amount_t py_strip_annotations_0(amount_t& amount) {
+ return amount.strip_annotations(keep_details_t());
+ }
+ amount_t py_strip_annotations_1(amount_t& amount, const keep_details_t& keep) {
+ return amount.strip_annotations(keep);
+ }
+
+ PyObject * py_amount_unicode(amount_t& amount) {
+ return str_to_py_unicode(amount.to_string());
}
} // unnamed namespace
@@ -113,11 +114,7 @@ EXC_TRANSLATOR(amount_error)
void export_amount()
{
class_< amount_t > ("Amount")
- .add_static_property("current_pool",
- make_getter(&amount_t::current_pool,
- return_internal_reference<>()))
-
- .def("initialize", py_amount_initialize) // only for the PyUnitTests
+ .def("initialize", &amount_t::initialize) // only for the PyUnitTests
.staticmethod("initialize")
.def("shutdown", &amount_t::shutdown)
.staticmethod("shutdown")
@@ -196,11 +193,11 @@ internal precision."))
.def(self / long())
.def(long() / self)
- .def("precision", &amount_t::precision)
- .def("keep_precision", &amount_t::keep_precision)
- .def("set_keep_precision", &amount_t::set_keep_precision, args("keep"),
- _("Set whether an amount should always display at full precision."))
- .def("display_precision", &amount_t::display_precision)
+ .add_property("precision", &amount_t::precision)
+ .add_property("display_precision", &amount_t::display_precision)
+ .add_property("keep_precision",
+ &amount_t::keep_precision,
+ &amount_t::set_keep_precision)
.def("negated", &amount_t::negated)
.def("in_place_negate", &amount_t::in_place_negate,
@@ -237,9 +234,8 @@ internal precision."))
return_internal_reference<>())
.def("value", py_value_0)
- .def("value", py_value_1, args("primary_only"))
- .def("value", py_value_2, args("primary_only", "moment"))
- .def("value", py_value_3, args("primary_only", "moment", "in_terms_of"))
+ .def("value", py_value_1, args("in_terms_of"))
+ .def("value", py_value_2, args("in_terms_of", "moment"))
.def("price", &amount_t::price)
@@ -256,27 +252,30 @@ internal precision."))
.def("__int__", &amount_t::to_long)
.def("fits_in_long", &amount_t::fits_in_long)
- .def("to_string", &amount_t::to_string)
.def("__str__", &amount_t::to_string)
+ .def("to_string", &amount_t::to_string)
+ .def("__unicode__", py_amount_unicode)
.def("to_fullstring", &amount_t::to_fullstring)
.def("__repr__", &amount_t::to_fullstring)
.def("quantity_string", &amount_t::quantity_string)
- .def("commodity", &amount_t::commodity,
- return_internal_reference<>())
+ .add_property("commodity",
+ make_function(&amount_t::commodity,
+ return_value_policy<reference_existing_object>()),
+ make_function(&amount_t::set_commodity,
+ with_custodian_and_ward<1, 2>()))
.def("has_commodity", &amount_t::has_commodity)
- .def("set_commodity", &amount_t::set_commodity,
- with_custodian_and_ward<1, 2>())
.def("clear_commodity", &amount_t::clear_commodity)
.def("number", &amount_t::number)
.def("annotate", &amount_t::annotate)
- .def("is_annotated", &amount_t::is_annotated)
-#if 0
- .def("annotation", &amount_t::annotation)
-#endif
- .def("strip_annotations", &amount_t::strip_annotations)
+ .def("has_annotation", &amount_t::has_annotation)
+ .add_property("annotation",
+ make_function(py_amount_annotation,
+ return_internal_reference<>()))
+ .def("strip_annotations", py_strip_annotations_0)
+ .def("strip_annotations", py_strip_annotations_1)
.def("parse", py_parse_1)
.def("parse", py_parse_2)
diff --git a/src/py_balance.cc b/src/py_balance.cc
index 23a2ff73..8c0c4c58 100644
--- a/src/py_balance.cc
+++ b/src/py_balance.cc
@@ -45,28 +45,18 @@ using namespace boost::python;
namespace {
boost::optional<balance_t> py_value_0(const balance_t& balance) {
- return balance.value();
+ return balance.value(false, CURRENT_TIME());
}
boost::optional<balance_t> py_value_1(const balance_t& balance,
- const bool primary_only) {
- return balance.value(primary_only);
+ commodity_t& in_terms_of) {
+ return balance.value(false, CURRENT_TIME(), in_terms_of);
}
-
- boost::optional<balance_t>
- py_value_2(const balance_t& balance,
- const bool primary_only,
- const boost::optional<datetime_t>& moment) {
- return balance.value(primary_only, moment);
- }
-
- boost::optional<balance_t>
- py_value_3(const balance_t& balance,
- const bool primary_only,
- const boost::optional<datetime_t>& moment,
- const boost::optional<commodity_t&>& in_terms_of) {
- return balance.value(primary_only, moment, in_terms_of);
+ boost::optional<balance_t> py_value_2(const balance_t& balance,
+ commodity_t& in_terms_of,
+ datetime_t& moment) {
+ return balance.value(false, moment, in_terms_of);
}
-
+
boost::optional<amount_t>
py_commodity_amount_0(const balance_t& balance) {
return balance.commodity_amount();
@@ -108,6 +98,17 @@ namespace {
return (*elem).second;
}
+ balance_t py_strip_annotations_0(balance_t& balance) {
+ return balance.strip_annotations(keep_details_t());
+ }
+ balance_t py_strip_annotations_1(balance_t& balance, const keep_details_t& keep) {
+ return balance.strip_annotations(keep);
+ }
+
+ PyObject * py_balance_unicode(balance_t& balance) {
+ return str_to_py_unicode(balance.to_string());
+ }
+
} // unnamed namespace
#define EXC_TRANSLATOR(type) \
@@ -155,7 +156,9 @@ void export_balance()
.def(self != long())
.def(! self)
- .def(self_ns::str(self))
+ .def("__str__", &balance_t::to_string)
+ .def("to_string", &balance_t::to_string)
+ .def("__unicode__", py_balance_unicode)
.def("negated", &balance_t::negated)
.def("in_place_negate", &balance_t::in_place_negate,
@@ -193,9 +196,8 @@ void export_balance()
return_internal_reference<>())
.def("value", py_value_0)
- .def("value", py_value_1, args("primary_only"))
- .def("value", py_value_2, args("primary_only", "moment"))
- .def("value", py_value_3, args("primary_only", "moment", "in_terms_of"))
+ .def("value", py_value_1, args("in_terms_of"))
+ .def("value", py_value_2, args("in_terms_of", "moment"))
.def("price", &balance_t::price)
@@ -215,7 +217,8 @@ void export_balance()
.def("number", &balance_t::number)
- .def("strip_annotations", &balance_t::strip_annotations)
+ .def("strip_annotations", py_strip_annotations_0)
+ .def("strip_annotations", py_strip_annotations_1)
.def("valid", &balance_t::valid)
;
diff --git a/src/py_commodity.cc b/src/py_commodity.cc
index 08af8f62..984be5f0 100644
--- a/src/py_commodity.cc
+++ b/src/py_commodity.cc
@@ -32,6 +32,7 @@
#include <system.hh>
#include "pyinterp.h"
+#include "pyutils.h"
#include "commodity.h"
#include "annotate.h"
#include "pool.h"
@@ -81,6 +82,12 @@ namespace {
// Exchange one commodity for another, while recording the factored price.
+ void py_exchange_2(commodity_pool_t& pool,
+ commodity_t& commodity,
+ const amount_t& per_unit_cost)
+ {
+ pool.exchange(commodity, per_unit_cost, CURRENT_TIME());
+ }
void py_exchange_3(commodity_pool_t& pool,
commodity_t& commodity,
const amount_t& per_unit_cost,
@@ -99,6 +106,77 @@ namespace {
return pool.exchange(amount, cost, is_per_unit, moment, tag);
}
+ commodity_t * py_pool_getitem(commodity_pool_t& pool, const string& symbol)
+ {
+ commodity_pool_t::commodities_map::iterator i =
+ pool.commodities.find(symbol);
+ if (i == pool.commodities.end()) {
+ PyErr_SetString(PyExc_ValueError,
+ (string("Could not find commodity ") + symbol).c_str());
+ throw boost::python::error_already_set();
+ }
+ return (*i).second;
+ }
+
+ python::list py_pool_keys(commodity_pool_t& pool) {
+ python::list keys;
+ BOOST_REVERSE_FOREACH
+ (const commodity_pool_t::commodities_map::value_type& pair,
+ pool.commodities) {
+ keys.insert(0, pair.first);
+ }
+ return keys;
+ }
+
+ bool py_pool_contains(commodity_pool_t& pool, const string& symbol) {
+ return pool.commodities.find(symbol) != pool.commodities.end();
+ }
+
+ commodity_pool_t::commodities_map::iterator
+ py_pool_commodities_begin(commodity_pool_t& pool) {
+ return pool.commodities.begin();
+ }
+ commodity_pool_t::commodities_map::iterator
+ py_pool_commodities_end(commodity_pool_t& pool) {
+ return pool.commodities.end();
+ }
+
+ typedef transform_iterator
+ <function<string(commodity_pool_t::commodities_map::value_type&)>,
+ commodity_pool_t::commodities_map::iterator>
+ commodities_map_firsts_iterator;
+ commodities_map_firsts_iterator
+
+ py_pool_commodities_keys_begin(commodity_pool_t& pool) {
+ return make_transform_iterator
+ (pool.commodities.begin(),
+ bind(&commodity_pool_t::commodities_map::value_type::first, _1));
+ }
+ commodities_map_firsts_iterator
+ py_pool_commodities_keys_end(commodity_pool_t& pool) {
+ return make_transform_iterator
+ (pool.commodities.end(),
+ bind(&commodity_pool_t::commodities_map::value_type::first, _1));
+ }
+
+ typedef transform_iterator
+ <function<commodity_t *(commodity_pool_t::commodities_map::value_type&)>,
+ commodity_pool_t::commodities_map::iterator>
+ commodities_map_seconds_iterator;
+
+ commodities_map_seconds_iterator
+ 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));
+ }
+ 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));
+ }
+
void py_add_price_2(commodity_t& commodity,
const datetime_t& date, const amount_t& price) {
commodity.add_price(date, price);
@@ -123,16 +201,50 @@ namespace {
return details.keep_any(comm);
}
+ commodity_t& py_commodity_referent(commodity_t& comm) {
+ return comm.referent();
+ }
+ commodity_t& py_annotated_commodity_referent(annotated_commodity_t& comm) {
+ return comm.referent();
+ }
+
+ commodity_t& py_strip_annotations_0(commodity_t& comm) {
+ return comm.strip_annotations(keep_details_t());
+ }
+ commodity_t& py_strip_annotations_1(commodity_t& comm,
+ const keep_details_t& keep) {
+ return comm.strip_annotations(keep);
+ }
+
+ commodity_t& py_strip_ann_annotations_0(annotated_commodity_t& comm) {
+ return comm.strip_annotations(keep_details_t());
+ }
+ commodity_t& py_strip_ann_annotations_1(annotated_commodity_t& comm,
+ const keep_details_t& keep) {
+ return comm.strip_annotations(keep);
+ }
+
+ boost::optional<amount_t> py_price(annotation_t& ann) {
+ return ann.price;
+ }
+ boost::optional<amount_t> py_set_price(annotation_t& ann,
+ const boost::optional<amount_t>& price) {
+ return ann.price = price;
+ }
+
+ PyObject * py_commodity_unicode(commodity_t& commodity) {
+ return str_to_py_unicode(commodity.symbol());
+ }
+
} // unnamed namespace
void export_commodity()
{
- class_< commodity_pool_t, boost::noncopyable > ("CommodityPool", no_init)
+ class_< commodity_pool_t, shared_ptr<commodity_pool_t>,
+ boost::noncopyable > ("CommodityPool", no_init)
.add_property("null_commodity",
make_getter(&commodity_pool_t::null_commodity,
- return_internal_reference<>()),
- make_setter(&commodity_pool_t::null_commodity,
- with_custodian_and_ward<1, 2>()))
+ return_internal_reference<>()))
.add_property("default_commodity",
make_getter(&commodity_pool_t::default_commodity,
return_internal_reference<>()),
@@ -157,25 +269,46 @@ void export_commodity()
.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<>())
+ .def("create", py_create_1,
+ return_value_policy<reference_existing_object>())
+ .def("create", py_create_2,
+ return_value_policy<reference_existing_object>())
.def("find_or_create", py_find_or_create_1,
- return_internal_reference<>())
+ return_value_policy<reference_existing_object>())
.def("find_or_create", py_find_or_create_2,
- return_internal_reference<>())
+ return_value_policy<reference_existing_object>())
- .def("find", py_find_1, return_internal_reference<>())
- .def("find", py_find_2, return_internal_reference<>())
+ .def("find", py_find_1, return_value_policy<reference_existing_object>())
+ .def("find", py_find_2, return_value_policy<reference_existing_object>())
+ .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("parse_price_directive", &commodity_pool_t::parse_price_directive)
.def("parse_price_expression", &commodity_pool_t::parse_price_expression,
- return_internal_reference<>())
+ return_value_policy<reference_existing_object>())
+
+ .def("__getitem__", py_pool_getitem,
+ return_value_policy<reference_existing_object>())
+ .def("keys", py_pool_keys)
+ .def("has_key", py_pool_contains)
+ .def("__contains__", py_pool_contains)
+ .def("__iter__", range<return_value_policy<reference_existing_object> >
+ (py_pool_commodities_begin, py_pool_commodities_end))
+ .def("iteritems", range<return_value_policy<reference_existing_object> >
+ (py_pool_commodities_begin, py_pool_commodities_end))
+ .def("iterkeys", range<>(py_pool_commodities_keys_begin,
+ py_pool_commodities_keys_end))
+ .def("itervalues", range<return_value_policy<reference_existing_object> >
+ (py_pool_commodities_values_begin, py_pool_commodities_values_end))
;
+ map_value_type_converter<commodity_pool_t::commodities_map>();
+
+ scope().attr("commodities") = commodity_pool_t::current_pool;
+
scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS;
scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED;
scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED;
@@ -202,6 +335,8 @@ void export_commodity()
make_getter(&commodity_t::european_by_default),
make_setter(&commodity_t::european_by_default))
+ .def("__str__", &commodity_t::symbol)
+ .def("__unicode__", py_commodity_unicode)
.def("__nonzero__", &commodity_t::operator bool)
.def(self == self)
@@ -209,33 +344,30 @@ void export_commodity()
.def("symbol_needs_quotes", &commodity_t::symbol_needs_quotes)
.staticmethod("symbol_needs_quotes")
-#if 0
- .def("referent", &commodity_t::referent,
- return_internal_reference<>())
-#endif
+ .add_property("referent",
+ make_function(py_commodity_referent,
+ return_value_policy<reference_existing_object>()))
- .def("is_annotated", &commodity_t::is_annotated)
- .def("strip_annotations", &commodity_t::strip_annotations,
- return_internal_reference<>())
+ .def("has_annotation", &commodity_t::has_annotation)
+ .def("strip_annotations", py_strip_annotations_0,
+ return_value_policy<reference_existing_object>())
+ .def("strip_annotations", py_strip_annotations_1,
+ return_value_policy<reference_existing_object>())
.def("write_annotations", &commodity_t::write_annotations)
.def("pool", &commodity_t::pool,
- return_internal_reference<>())
-
- .def("base_symbol", &commodity_t::base_symbol)
- .def("symbol", &commodity_t::symbol)
- .def("mapping_key", &commodity_t::mapping_key)
-
- .def("name", &commodity_t::name)
- .def("set_name", &commodity_t::set_name)
- .def("note", &commodity_t::note)
- .def("set_note", &commodity_t::set_note)
- .def("precision", &commodity_t::precision)
- .def("set_precision", &commodity_t::set_precision)
- .def("smaller", &commodity_t::smaller)
- .def("set_smaller", &commodity_t::set_smaller)
- .def("larger", &commodity_t::larger)
- .def("set_larger", &commodity_t::set_larger)
+ return_value_policy<reference_existing_object>())
+
+ .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)
+ .add_property("precision", &commodity_t::precision,
+ &commodity_t::set_precision)
+ .add_property("smaller", &commodity_t::smaller, &commodity_t::set_smaller)
+ .add_property("larger", &commodity_t::larger, &commodity_t::set_larger)
.def("add_price", py_add_price_2)
.def("add_price", py_add_price_3)
@@ -257,9 +389,7 @@ void export_commodity()
.def("drop_flags", &supports_flags<>::drop_flags)
#endif
- .add_property("price",
- make_getter(&annotation_t::price),
- make_setter(&annotation_t::price))
+ .add_property("price", py_price, py_set_price)
.add_property("date",
make_getter(&annotation_t::date),
make_setter(&annotation_t::date))
@@ -306,13 +436,14 @@ void export_commodity()
.def(self == self)
.def(self == other<commodity_t>())
-#if 0
- .def("referent", &annotated_commodity_t::referent,
- return_internal_reference<>())
-#endif
+ .add_property("referent",
+ make_function(py_annotated_commodity_referent,
+ return_value_policy<reference_existing_object>()))
- .def("strip_annotations", &annotated_commodity_t::strip_annotations,
- return_internal_reference<>())
+ .def("strip_annotations", py_strip_ann_annotations_0,
+ return_value_policy<reference_existing_object>())
+ .def("strip_annotations", py_strip_ann_annotations_1,
+ return_value_policy<reference_existing_object>())
.def("write_annotations", &annotated_commodity_t::write_annotations)
;
}
diff --git a/src/py_journal.cc b/src/py_journal.cc
index 7e9f8a1b..5be9cbe1 100644
--- a/src/py_journal.cc
+++ b/src/py_journal.cc
@@ -52,10 +52,6 @@ namespace {
return *journal.master;
}
- commodity_pool_t& py_commodity_pool(journal_t& journal) {
- return *journal.commodity_pool;
- }
-
long xacts_len(journal_t& journal)
{
return journal.xacts.size();
@@ -276,10 +272,6 @@ void export_journal()
with_custodian_and_ward_postcall<1, 0> >()),
make_setter(&journal_t::bucket))
.add_property("was_loaded", make_getter(&journal_t::was_loaded))
- .add_property("commodity_pool",
- make_getter(&journal_t::commodity_pool,
- return_internal_reference<1,
- with_custodian_and_ward_postcall<1, 0> >()))
.def("add_account", &journal_t::add_account)
.def("remove_account", &journal_t::remove_account)
diff --git a/src/py_utils.cc b/src/py_utils.cc
index 2736ed3e..5203599f 100644
--- a/src/py_utils.cc
+++ b/src/py_utils.cc
@@ -79,15 +79,8 @@ struct string_to_python
{
static PyObject* convert(const string& str)
{
-#if 1
- // Return a Unicode object
- PyObject * pstr = PyString_FromString(str.c_str());
- PyObject * uni = PyUnicode_FromEncodedObject(pstr, "UTF-8", NULL);
- return object(handle<>(borrowed(uni))).ptr();
-#else
- // Return a 7-bit ASCII string
+ // Return bytes, not characters; see __unicode__ methods for that
return incref(object(static_cast<const std::string&>(str)).ptr());
-#endif
}
};
diff --git a/src/py_value.cc b/src/py_value.cc
index ee039519..713dc3d4 100644
--- a/src/py_value.cc
+++ b/src/py_value.cc
@@ -47,7 +47,21 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(set_string_overloads, set_string, 0, 2)
namespace {
- PyObject * py_base_type(value_t& value) {
+ boost::optional<value_t> py_value_0(const value_t& value) {
+ return value.value(false, CURRENT_TIME());
+ }
+ boost::optional<value_t> py_value_1(const value_t& value,
+ commodity_t& in_terms_of) {
+ return value.value(false, CURRENT_TIME(), in_terms_of);
+ }
+ boost::optional<value_t> py_value_2(const value_t& value,
+ commodity_t& in_terms_of,
+ datetime_t& moment) {
+ return value.value(false, moment, in_terms_of);
+ }
+
+ PyObject * py_base_type(value_t& value)
+ {
if (value.is_boolean()) {
return (PyObject *)&PyBool_Type;
}
@@ -63,16 +77,6 @@ namespace {
}
}
- expr_t py_value_getattr(const value_t& value, const string& name)
- {
- if (value.is_scope()) {
- if (scope_t * scope = value.as_scope())
- return expr_t(scope->lookup(symbol_t::FUNCTION, name), scope);
- }
- throw_(value_error, _("Cannot lookup attributes in %1") << value.label());
- return expr_t();
- }
-
string py_dump(const value_t& value) {
std::ostringstream buf;
value.dump(buf);
@@ -85,8 +89,23 @@ namespace {
return buf.str();
}
- void py_set_string(value_t& amount, const string& str) {
- return amount.set_string(str);
+ void py_set_string(value_t& value, const string& str) {
+ return value.set_string(str);
+ }
+
+ annotation_t& py_value_annotation(value_t& value) {
+ return value.annotation();
+ }
+
+ value_t py_strip_annotations_0(value_t& value) {
+ return value.strip_annotations(keep_details_t());
+ }
+ value_t py_strip_annotations_1(value_t& value, const keep_details_t& keep) {
+ return value.strip_annotations(keep);
+ }
+
+ PyObject * py_value_unicode(value_t& value) {
+ return str_to_py_unicode(value.to_string());
}
} // unnamed namespace
@@ -101,16 +120,16 @@ EXC_TRANSLATOR(value_error)
void export_value()
{
enum_< value_t::type_t >("ValueType")
- .value("VOID", value_t::VOID)
- .value("BOOLEAN", value_t::BOOLEAN)
- .value("DATETIME", value_t::DATETIME)
- .value("DATE", value_t::DATE)
- .value("INTEGER", value_t::INTEGER)
- .value("AMOUNT", value_t::AMOUNT)
- .value("BALANCE", value_t::BALANCE)
- .value("STRING", value_t::STRING)
- .value("SEQUENCE", value_t::SEQUENCE)
- .value("SCOPE", value_t::SCOPE)
+ .value("Void", value_t::VOID)
+ .value("Boolean", value_t::BOOLEAN)
+ .value("DateTime", value_t::DATETIME)
+ .value("Date", value_t::DATE)
+ .value("Integer", value_t::INTEGER)
+ .value("Amount", value_t::AMOUNT)
+ .value("Balance", value_t::BALANCE)
+ .value("String", value_t::STRING)
+ .value("Sequence", value_t::SEQUENCE)
+ .value("Scope", value_t::SCOPE)
;
class_< value_t > ("Value")
@@ -243,6 +262,10 @@ void export_value()
.def("unreduced", &value_t::unreduced)
.def("in_place_unreduce", &value_t::in_place_unreduce)
+ .def("value", py_value_0)
+ .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("exchange_commodities", &value_t::exchange_commodities,
@@ -291,11 +314,13 @@ void export_value()
.def("to_date", &value_t::to_date)
.def("to_amount", &value_t::to_amount)
.def("to_balance", &value_t::to_balance)
+ .def("__str__", &value_t::to_string)
+ .def("__unicode__", py_value_unicode)
.def("to_string", &value_t::to_string)
.def("to_mask", &value_t::to_mask)
.def("to_sequence", &value_t::to_sequence)
- .def("__str__", py_dump_relaxed)
+ .def("__unicode__", py_dump_relaxed)
.def("__repr__", py_dump)
.def("casted", &value_t::casted)
@@ -306,16 +331,16 @@ void export_value()
.def("number", &value_t::number)
.def("annotate", &value_t::annotate)
- .def("is_annotated", &value_t::is_annotated)
-#if 0
- .def("annotation", &value_t::annotation)
-#endif
- .def("strip_annotations", &value_t::strip_annotations)
+ .def("has_annotation", &value_t::has_annotation)
+ .add_property("annotation",
+ make_function(py_value_annotation,
+ return_internal_reference<>()))
+ .def("strip_annotations", py_strip_annotations_0)
+ .def("strip_annotations", py_strip_annotations_1)
#if 0
.def("__getitem__", &value_t::operator[])
#endif
- .def("__getattr__", py_value_getattr)
.def("push_back", &value_t::push_back)
.def("pop_back", &value_t::pop_back)
.def("size", &value_t::size)
diff --git a/src/pyutils.h b/src/pyutils.h
index a9e968e0..54d6fa28 100644
--- a/src/pyutils.h
+++ b/src/pyutils.h
@@ -106,6 +106,35 @@ struct register_optional_to_python : public boost::noncopyable
}
};
+template <typename T1, typename T2>
+struct PairToTupleConverter
+{
+ static PyObject * convert(const std::pair<T1, T2>& pair) {
+ return boost::python::incref
+ (boost::python::make_tuple(pair.first, pair.second).ptr());
+ }
+};
+
+template <typename MapType>
+struct map_value_type_converter
+{
+ map_value_type_converter() {
+ boost::python::to_python_converter
+ <typename MapType::value_type,
+ PairToTupleConverter<const typename MapType::key_type,
+ typename MapType::mapped_type> >();
+ }
+};
+
+template <typename T>
+PyObject * str_to_py_unicode(const T& str)
+{
+ using namespace boost::python;
+ PyObject * pstr = PyString_FromString(str.c_str());
+ PyObject * uni = PyUnicode_FromEncodedObject(pstr, "UTF-8", NULL);
+ return object(handle<>(borrowed(uni))).ptr();
+}
+
namespace boost { namespace python {
// Use expr to create the PyObject corresponding to x
diff --git a/src/query.cc b/src/query.cc
index 2d6085fa..cfa321b0 100644
--- a/src/query.cc
+++ b/src/query.cc
@@ -337,7 +337,9 @@ query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_contex
}
expr_t::ptr_op_t mask = new expr_t::op_t(expr_t::op_t::VALUE);
+ DEBUG("query.mask", "Mask from string: " << *tok.value);
mask->set_value(mask_t(*tok.value));
+ DEBUG("query.mask", "Mask is: " << mask->as_value().as_mask().str());
node->set_left(ident);
node->set_right(mask);
diff --git a/src/query.h b/src/query.h
index fb73ef2a..ebc14020 100644
--- a/src/query.h
+++ b/src/query.h
@@ -276,7 +276,7 @@ public:
if (! args.empty())
parse_args(args);
}
- ~query_t() throw() {
+ virtual ~query_t() {
TRACE_DTOR(query_t);
}
diff --git a/src/quotes.cc b/src/quotes.cc
index 7f41e4ff..f892e93f 100644
--- a/src/quotes.cc
+++ b/src/quotes.cc
@@ -75,23 +75,23 @@ commodity_quote_from_script(commodity_t& commodity,
if (char * p = std::strchr(buf, '\n')) *p = '\0';
DEBUG("commodity.download", "downloaded quote: " << buf);
- if (optional<price_point_t> point =
- amount_t::current_pool->parse_price_directive(buf)) {
- if (amount_t::current_pool->price_db) {
+ if (optional<std::pair<commodity_t *, price_point_t> > point =
+ commodity_pool_t::current_pool->parse_price_directive(buf)) {
+ if (commodity_pool_t::current_pool->price_db) {
#if defined(__GNUG__) && __GNUG__ < 3
- ofstream database(*amount_t::current_pool->price_db,
+ ofstream database(*commodity_pool_t::current_pool->price_db,
ios::out | ios::app);
#else
- ofstream database(*amount_t::current_pool->price_db,
+ ofstream database(*commodity_pool_t::current_pool->price_db,
std::ios_base::out | std::ios_base::app);
#endif
database << "P "
- << format_datetime(point->when, FMT_WRITTEN)
+ << format_datetime(point->second.when, FMT_WRITTEN)
<< " " << commodity.symbol()
- << " " << point->price
+ << " " << point->second.price
<< std::endl;
}
- return point;
+ return point->second;
}
} else {
DEBUG("commodity.download",
diff --git a/src/report.cc b/src/report.cc
index 7da44f8c..b882ca92 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -69,18 +69,17 @@ void report_t::normalize_options(const string& verb)
item_t::use_effective_date = (HANDLED(effective) &&
! HANDLED(actual_dates));
- session.journal->commodity_pool->keep_base = HANDLED(base);
- session.journal->commodity_pool->get_quotes = session.HANDLED(download);
+ commodity_pool_t::current_pool->keep_base = HANDLED(base);
+ commodity_pool_t::current_pool->get_quotes = session.HANDLED(download);
if (session.HANDLED(price_exp_))
- session.journal->commodity_pool->quote_leeway =
+ commodity_pool_t::current_pool->quote_leeway =
session.HANDLER(price_exp_).value.as_long();
if (session.HANDLED(price_db_))
- session.journal->commodity_pool->price_db =
- session.HANDLER(price_db_).str();
+ commodity_pool_t::current_pool->price_db = session.HANDLER(price_db_).str();
else
- session.journal->commodity_pool->price_db = none;
+ commodity_pool_t::current_pool->price_db = none;
if (HANDLED(date_format_))
set_date_format(HANDLER(date_format_).str().c_str());
@@ -294,12 +293,15 @@ void report_t::accounts_report(acct_handler_ptr handler)
sort_expr, HANDLED(flat)));
}
- if (HANDLED(display_))
+ if (HANDLED(display_)) {
+ DEBUG("report.predicate",
+ "Display predicate = " << HANDLER(display_).str());
pass_down_accounts(handler, *iter.get(),
predicate_t(HANDLER(display_).str(), what_to_keep()),
*this);
- else
+ } else {
pass_down_accounts(handler, *iter.get());
+ }
session.journal->clear_xdata();
}
@@ -381,6 +383,32 @@ value_t report_t::fn_strip(call_scope_t& args)
return args.value().strip_annotations(what_to_keep());
}
+value_t report_t::fn_trim(call_scope_t& args)
+{
+ string temp(args.value().to_string());
+ scoped_array<char> buf(new char[temp.length() + 1]);
+ std::strcpy(buf.get(), temp.c_str());
+
+ const char * p = buf.get();
+ while (*p && std::isspace(*p))
+ p++;
+
+ const char * e = buf.get() + temp.length();
+ while (e > p && std::isspace(*e))
+ e--;
+
+ if (e == p) {
+ return string_value(empty_string);
+ }
+ else if (e < p) {
+ assert(false);
+ return string_value(empty_string);
+ }
+ else {
+ return string_value(string(p, e - p));
+ }
+}
+
value_t report_t::fn_scrub(call_scope_t& args)
{
value_t temp(args.value().strip_annotations(what_to_keep()));
@@ -522,7 +550,7 @@ value_t report_t::fn_price(call_scope_t& scope)
value_t report_t::fn_lot_date(call_scope_t& scope)
{
interactive_t args(scope, "v");
- if (args.value_at(0).is_annotated()) {
+ if (args.value_at(0).has_annotation()) {
const annotation_t& details(args.value_at(0).annotation());
if (details.date)
return *details.date;
@@ -533,7 +561,7 @@ value_t report_t::fn_lot_date(call_scope_t& scope)
value_t report_t::fn_lot_price(call_scope_t& scope)
{
interactive_t args(scope, "v");
- if (args.value_at(0).is_annotated()) {
+ if (args.value_at(0).has_annotation()) {
const annotation_t& details(args.value_at(0).annotation());
if (details.price)
return *details.price;
@@ -544,7 +572,7 @@ value_t report_t::fn_lot_price(call_scope_t& scope)
value_t report_t::fn_lot_tag(call_scope_t& scope)
{
interactive_t args(scope, "v");
- if (args.value_at(0).is_annotated()) {
+ if (args.value_at(0).has_annotation()) {
const annotation_t& details(args.value_at(0).annotation());
if (details.tag)
return string_value(*details.tag);
@@ -845,6 +873,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
else OPT(percent);
else OPT_(period_);
else OPT_ALT(sort_xacts_, period_sort_);
+ else OPT(pivot_);
else OPT(plot_amount_format_);
else OPT(plot_total_format_);
else OPT(price);
@@ -888,6 +917,8 @@ option_t<report_t> * report_t::lookup_option(const char * p)
OPT(unbudgeted);
else OPT(uncleared);
else OPT(unrealized);
+ else OPT(unrealized_gains_);
+ else OPT(unrealized_losses_);
else OPT(unround);
else OPT(unsorted);
break;
@@ -1077,6 +1108,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
return MAKE_FUNCTOR(report_t::fn_today);
else if (is_eq(p, "t"))
return MAKE_FUNCTOR(report_t::fn_display_amount);
+ else if (is_eq(p, "trim"))
+ return MAKE_FUNCTOR(report_t::fn_trim);
else if (is_eq(p, "to_boolean"))
return MAKE_FUNCTOR(report_t::fn_to_boolean);
else if (is_eq(p, "to_int"))
@@ -1239,7 +1272,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
switch (*p) {
case 'a':
if (is_eq(p, "args"))
- return WRAP_FUNCTOR(args_command);
+ return WRAP_FUNCTOR(query_command);
break;
case 'e':
if (is_eq(p, "eval"))
@@ -1261,6 +1294,10 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
else if (is_eq(p, "period"))
return WRAP_FUNCTOR(period_command);
break;
+ case 'q':
+ if (is_eq(p, "query"))
+ return WRAP_FUNCTOR(query_command);
+ break;
case 't':
if (is_eq(p, "template"))
return WRAP_FUNCTOR(template_command);
diff --git a/src/report.h b/src/report.h
index 354e31b6..08819e23 100644
--- a/src/report.h
+++ b/src/report.h
@@ -143,6 +143,7 @@ public:
value_t fn_get_at(call_scope_t& scope);
value_t fn_is_seq(call_scope_t& scope);
value_t fn_strip(call_scope_t& scope);
+ value_t fn_trim(call_scope_t& scope);
value_t fn_scrub(call_scope_t& scope);
value_t fn_quantity(call_scope_t& scope);
value_t fn_rounded(call_scope_t& scope);
@@ -272,6 +273,7 @@ public:
HANDLER(pending).report(out);
HANDLER(percent).report(out);
HANDLER(period_).report(out);
+ HANDLER(pivot_).report(out);
HANDLER(plot_amount_format_).report(out);
HANDLER(plot_total_format_).report(out);
HANDLER(prepend_format_).report(out);
@@ -302,6 +304,8 @@ public:
HANDLER(unbudgeted).report(out);
HANDLER(uncleared).report(out);
HANDLER(unrealized).report(out);
+ HANDLER(unrealized_gains_).report(out);
+ HANDLER(unrealized_losses_).report(out);
HANDLER(unround).report(out);
HANDLER(unsorted).report(out);
HANDLER(weekly).report(out);
@@ -359,7 +363,7 @@ public:
OPTION_(report_t, average, DO() { // -A
parent->HANDLER(display_total_)
- .set_expr(string("--average"), "total_expr/count");
+ .set_expr(string("--average"), "count>0?(total_expr/count):0");
});
OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) {
@@ -712,6 +716,8 @@ public:
string_value(text.as_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");
@@ -876,6 +882,9 @@ public:
OPTION(report_t, unrealized);
+ OPTION(report_t, unrealized_gains_);
+ OPTION(report_t, unrealized_losses_);
+
OPTION_(report_t, unround, DO() {
parent->HANDLER(display_amount_)
.set_expr(string("--unround"), "unrounded(amount_expr)");
diff --git a/src/scope.cc b/src/scope.cc
index 99f6b669..64736ca3 100644
--- a/src/scope.cc
+++ b/src/scope.cc
@@ -42,15 +42,18 @@ void symbol_scope_t::define(const symbol_t::kind_t kind,
{
DEBUG("scope.symbols", "Defining '" << name << "' = " << def);
+ if (! symbols)
+ symbols = symbol_map();
+
std::pair<symbol_map::iterator, bool> result
- = symbols.insert(symbol_map::value_type(symbol_t(kind, name, def), def));
+ = symbols->insert(symbol_map::value_type(symbol_t(kind, name, def), def));
if (! result.second) {
- symbol_map::iterator i = symbols.find(symbol_t(kind, name));
- assert(i != symbols.end());
- symbols.erase(i);
+ symbol_map::iterator i = symbols->find(symbol_t(kind, name));
+ 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);
@@ -60,10 +63,11 @@ void symbol_scope_t::define(const symbol_t::kind_t kind,
expr_t::ptr_op_t symbol_scope_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
- symbol_map::const_iterator i = symbols.find(symbol_t(kind, name));
- if (i != symbols.end())
- return (*i).second;
-
+ if (symbols) {
+ symbol_map::const_iterator i = symbols->find(symbol_t(kind, name));
+ if (i != symbols->end())
+ return (*i).second;
+ }
return child_scope_t::lookup(kind, name);
}
diff --git a/src/scope.h b/src/scope.h
index 44ca3229..93f80230 100644
--- a/src/scope.h
+++ b/src/scope.h
@@ -172,7 +172,7 @@ class symbol_scope_t : public child_scope_t
{
typedef std::map<symbol_t, expr_t::ptr_op_t> symbol_map;
- symbol_map symbols;
+ optional<symbol_map> symbols;
public:
explicit symbol_scope_t() {
diff --git a/src/session.cc b/src/session.cc
index 0d6a6829..1882e554 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -45,7 +45,7 @@ void set_session_context(session_t * session)
{
if (session) {
times_initialize();
- amount_t::initialize(session->journal->commodity_pool);
+ amount_t::initialize();
amount_t::parse_conversion("1.0m", "60s");
amount_t::parse_conversion("1.0h", "60m");
@@ -179,7 +179,7 @@ void session_t::close_journal_files()
amount_t::shutdown();
journal.reset(new journal_t);
- amount_t::initialize(journal->commodity_pool);
+ amount_t::initialize();
}
option_t<session_t> * session_t::lookup_option(const char * p)
diff --git a/src/textual.cc b/src/textual.cc
index aec7dbda..8953d2b8 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -49,49 +49,26 @@
namespace ledger {
namespace {
- class instance_t : public noncopyable, public scope_t
- {
- static const std::size_t MAX_LINE = 1024;
+ typedef std::pair<commodity_t *, amount_t> fixed_rate_t;
+ typedef variant<account_t *, string, fixed_rate_t> state_t;
+ class parse_context_t : public noncopyable
+ {
public:
- typedef variant<account_t *, string> state_t;
-
- std::list<state_t>& state_stack;
-
-#if defined(TIMELOG_SUPPORT)
- time_log_t& timelog;
-#endif
- instance_t * parent;
- std::istream& in;
- scope_t& scope;
- journal_t& journal;
- account_t * master;
- const path * original_file;
- accounts_map account_aliases;
- bool strict;
- path pathname;
- char linebuf[MAX_LINE + 1];
- std::size_t linenum;
- istream_pos_type line_beg_pos;
- istream_pos_type curr_pos;
- std::size_t count;
- std::size_t errors;
-
- optional<date_t::year_type> current_year;
-
- instance_t(std::list<state_t>& _state_stack,
+ journal_t& journal;
+ scope_t& scope;
+ std::list<state_t> state_stack;
#if defined(TIMELOG_SUPPORT)
- time_log_t& _timelog,
+ time_log_t timelog;
#endif
- std::istream& _in,
- scope_t& _scope,
- journal_t& _journal,
- account_t * _master = NULL,
- const path * _original_file = NULL,
- bool _strict = false,
- instance_t * _parent = NULL);
+ bool strict;
+ std::size_t count;
+ std::size_t errors;
+ std::size_t sequence;
- ~instance_t();
+ parse_context_t(journal_t& _journal, scope_t& _scope)
+ : journal(_journal), scope(_scope), timelog(journal),
+ strict(false), count(0), errors(0), sequence(1) {}
bool front_is_account() {
return state_stack.front().type() == typeid(account_t *);
@@ -99,6 +76,9 @@ namespace {
bool front_is_string() {
return state_stack.front().type() == typeid(string);
}
+ bool front_is_fixed_rate() {
+ return state_stack.front().type() == typeid(fixed_rate_t);
+ }
account_t * top_account() {
foreach (state_t& state, state_stack)
@@ -106,6 +86,34 @@ namespace {
return boost::get<account_t *>(state);
return NULL;
}
+ };
+
+ class instance_t : public noncopyable, public scope_t
+ {
+ static const std::size_t MAX_LINE = 1024;
+
+ public:
+ parse_context_t& context;
+ instance_t * parent;
+ account_t * master;
+ accounts_map account_aliases;
+ const path * original_file;
+ path pathname;
+ std::istream& in;
+ char linebuf[MAX_LINE + 1];
+ std::size_t linenum;
+ istream_pos_type line_beg_pos;
+ istream_pos_type curr_pos;
+
+ optional<date_t::year_type> current_year;
+
+ instance_t(parse_context_t& _context,
+ std::istream& _in,
+ account_t * _master = NULL,
+ const path * _original_file = NULL,
+ instance_t * _parent = NULL);
+
+ ~instance_t();
void parse();
std::streamsize read_line(char *& line);
@@ -135,6 +143,7 @@ namespace {
void master_account_directive(char * line);
void end_directive(char * line);
void alias_directive(char * line);
+ void fixed_directive(char * line);
void tag_directive(char * line);
void define_directive(char * line);
bool general_directive(char * line);
@@ -143,7 +152,6 @@ namespace {
std::streamsize len,
account_t * account,
xact_t * xact,
- bool honor_strict = true,
bool defer_expr = false);
bool parse_posts(account_t * account,
@@ -158,13 +166,13 @@ namespace {
const string& name);
};
- void parse_amount_expr(scope_t& scope,
- std::istream& in,
+ void parse_amount_expr(std::istream& in,
+ scope_t& scope,
+ post_t& post,
amount_t& amount,
- optional<expr_t> * amount_expr,
- post_t * post,
- const parse_flags_t& flags = PARSE_DEFAULT,
- const bool defer_expr = false)
+ const parse_flags_t& flags = PARSE_DEFAULT,
+ const bool defer_expr = false,
+ optional<expr_t> * amount_expr = NULL)
{
expr_t expr(in, flags.plus_flags(PARSE_PARTIAL));
@@ -180,51 +188,27 @@ namespace {
#endif
if (expr) {
- bind_scope_t bound_scope(scope, *post);
- if (defer_expr) {
- assert(amount_expr);
+ if (amount_expr)
*amount_expr = expr;
- (*amount_expr)->compile(bound_scope);
- } else {
- value_t result(expr.calc(bound_scope));
- if (result.is_long()) {
- amount = result.to_amount();
- } else {
- if (! result.is_amount())
- throw_(amount_error,
- _("Amount expressions must result in a simple amount"));
- amount = result.as_amount();
- }
- DEBUG("textual.parse", "The posting amount is " << amount);
- }
+ if (! defer_expr)
+ amount = post.resolve_expr(scope, expr);
}
}
}
-instance_t::instance_t(std::list<state_t>& _state_stack,
-#if defined(TIMELOG_SUPPORT)
- time_log_t& _timelog,
-#endif
- std::istream& _in,
- scope_t& _scope,
- journal_t& _journal,
- account_t * _master,
- const path * _original_file,
- bool _strict,
- instance_t * _parent)
- : state_stack(_state_stack),
-#if defined(TIMELOG_SUPPORT)
- timelog(_timelog),
-#endif
- parent(_parent), in(_in), scope(_scope),
- journal(_journal), master(_master),
- original_file(_original_file), strict(_strict)
+instance_t::instance_t(parse_context_t& _context,
+ std::istream& _in,
+ account_t * _master,
+ const path * _original_file,
+ instance_t * _parent)
+ : context(_context), parent(_parent), master(_master),
+ original_file(_original_file), in(_in)
{
TRACE_CTOR(instance_t, "...");
if (! master)
- master = journal.master;
- state_stack.push_front(master);
+ master = context.journal.master;
+ context.state_stack.push_front(master);
if (_original_file)
pathname = *_original_file;
@@ -236,8 +220,8 @@ instance_t::~instance_t()
{
TRACE_DTOR(instance_t);
- assert(! state_stack.empty());
- state_stack.pop_front();
+ assert(! context.state_stack.empty());
+ context.state_stack.pop_front();
}
void instance_t::parse()
@@ -251,8 +235,6 @@ void instance_t::parse()
return;
linenum = 0;
- errors = 0;
- count = 0;
curr_pos = in.tellg();
while (in.good() && ! in.eof()) {
@@ -281,15 +263,15 @@ void instance_t::parse()
if (caught_signal != NONE_CAUGHT)
throw;
- string context = error_context();
- if (! context.empty())
- std::cerr << context << std::endl;
+ string err_context = error_context();
+ if (! err_context.empty())
+ std::cerr << err_context << std::endl;
if (! current_context.empty())
std::cerr << current_context << std::endl;
std::cerr << _("Error: ") << err.what() << std::endl;
- errors++;
+ context.errors++;
}
}
@@ -345,8 +327,10 @@ void instance_t::read_next_directive()
break;
}
- case '#': // comment line
- case ';': // comment line
+ case ';': // comments
+ case '#':
+ case '*':
+ case '|':
break;
case '-': // option setting
@@ -445,13 +429,14 @@ void instance_t::clock_in_directive(char * line,
position.beg_line = linenum;
position.end_pos = curr_pos;
position.end_line = linenum;
+ position.sequence = context.sequence++;
time_xact_t event(position, parse_datetime(datetime, current_year),
- p ? top_account()->find_account(p) : NULL,
+ p ? context.top_account()->find_account(p) : NULL,
n ? n : "",
end ? end : "");
- timelog.clock_in(event);
+ context.timelog.clock_in(event);
}
void instance_t::clock_out_directive(char * line,
@@ -474,14 +459,15 @@ void instance_t::clock_out_directive(char * line,
position.beg_line = linenum;
position.end_pos = curr_pos;
position.end_line = linenum;
+ position.sequence = context.sequence++;
time_xact_t event(position, parse_datetime(datetime, current_year),
- p ? top_account()->find_account(p) : NULL,
+ p ? context.top_account()->find_account(p) : NULL,
n ? n : "",
end ? end : "");
- timelog.clock_out(event);
- count++;
+ context.timelog.clock_out(event);
+ context.count++;
}
#endif // TIMELOG_SUPPORT
@@ -490,14 +476,14 @@ void instance_t::default_commodity_directive(char * line)
{
amount_t amt(skip_ws(line + 1));
VERIFY(amt.valid());
- amount_t::current_pool->default_commodity = &amt.commodity();
+ commodity_pool_t::current_pool->default_commodity = &amt.commodity();
amt.commodity().add_flags(COMMODITY_KNOWN);
}
void instance_t::default_account_directive(char * line)
{
- journal.bucket = top_account()->find_account(skip_ws(line + 1));
- journal.bucket->add_flags(ACCOUNT_KNOWN);
+ context.journal.bucket = context.top_account()->find_account(skip_ws(line + 1));
+ context.journal.bucket->add_flags(ACCOUNT_KNOWN);
}
void instance_t::price_conversion_directive(char * line)
@@ -510,8 +496,8 @@ void instance_t::price_conversion_directive(char * line)
void instance_t::price_xact_directive(char * line)
{
- optional<price_point_t> point =
- amount_t::current_pool->parse_price_directive(skip_ws(line + 1));
+ optional<std::pair<commodity_t *, price_point_t> > point =
+ commodity_pool_t::current_pool->parse_price_directive(skip_ws(line + 1));
if (! point)
throw parse_error(_("Pricing entry failed to parse"));
}
@@ -523,7 +509,7 @@ void instance_t::nomarket_directive(char * line)
commodity_t::parse_symbol(p, symbol);
if (commodity_t * commodity =
- amount_t::current_pool->find_or_create(symbol))
+ commodity_pool_t::current_pool->find_or_create(symbol))
commodity->add_flags(COMMODITY_NOMARKET | COMMODITY_KNOWN);
}
@@ -541,14 +527,13 @@ void instance_t::option_directive(char * line)
*p++ = '\0';
}
- if (! process_option(pathname.string(), line + 2, scope, p, line))
+ if (! process_option(pathname.string(), line + 2, context.scope, p, line))
throw_(option_error, _("Illegal option --%1") << line + 2);
}
void instance_t::automated_xact_directive(char * line)
{
- istream_pos_type pos = line_beg_pos;
- std::size_t lnum = linenum;
+ istream_pos_type pos= line_beg_pos;
bool reveal_context = true;
@@ -557,19 +542,20 @@ void instance_t::automated_xact_directive(char * line)
std::auto_ptr<auto_xact_t> ae
(new auto_xact_t(query_t(string(skip_ws(line + 1)),
keep_details_t(true, true, true))));
+ ae->pos = position_t();
+ ae->pos->pathname = pathname;
+ ae->pos->beg_pos = line_beg_pos;
+ ae->pos->beg_line = linenum;
+ ae->pos->sequence = context.sequence++;
reveal_context = false;
- if (parse_posts(top_account(), *ae.get(), true)) {
+ if (parse_posts(context.top_account(), *ae.get(), true)) {
reveal_context = true;
- journal.auto_xacts.push_back(ae.get());
+ context.journal.auto_xacts.push_back(ae.get());
- ae->journal = &journal;
- ae->pos = position_t();
- ae->pos->pathname = pathname;
- ae->pos->beg_pos = pos;
- ae->pos->beg_line = lnum;
+ ae->journal = &context.journal;
ae->pos->end_pos = curr_pos;
ae->pos->end_line = linenum;
@@ -588,29 +574,29 @@ void instance_t::automated_xact_directive(char * line)
void instance_t::period_xact_directive(char * line)
{
- istream_pos_type pos = line_beg_pos;
- std::size_t lnum = linenum;
+ istream_pos_type pos = line_beg_pos;
bool reveal_context = true;
try {
std::auto_ptr<period_xact_t> pe(new period_xact_t(skip_ws(line + 1)));
+ pe->pos = position_t();
+ pe->pos->pathname = pathname;
+ pe->pos->beg_pos = line_beg_pos;
+ pe->pos->beg_line = linenum;
+ pe->pos->sequence = context.sequence++;
reveal_context = false;
- if (parse_posts(top_account(), *pe.get())) {
+ if (parse_posts(context.top_account(), *pe.get())) {
reveal_context = true;
- pe->journal = &journal;
+ pe->journal = &context.journal;
if (pe->finalize()) {
- journal.extend_xact(pe.get());
- journal.period_xacts.push_back(pe.get());
+ context.journal.extend_xact(pe.get());
+ context.journal.period_xacts.push_back(pe.get());
- pe->pos = position_t();
- pe->pos->pathname = pathname;
- pe->pos->beg_pos = pos;
- pe->pos->beg_line = lnum;
pe->pos->end_pos = curr_pos;
pe->pos->end_line = linenum;
@@ -635,12 +621,12 @@ 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())) {
+ if (xact_t * xact = parse_xact(line, len, context.top_account())) {
std::auto_ptr<xact_t> manager(xact);
- if (journal.add_xact(xact)) {
+ if (context.journal.add_xact(xact)) {
manager.release(); // it's owned by the journal now
- count++;
+ context.count++;
}
// It's perfectly valid for the journal to reject the xact, which it will
// do if the xact has no substantive effect (for example, a checking
@@ -656,20 +642,26 @@ void instance_t::include_directive(char * line)
{
path filename;
+ DEBUG("textual.include", "include: " << line);
+
if (line[0] != '/' && line[0] != '\\' && line[0] != '~') {
+ DEBUG("textual.include", "received a relative path");
+ DEBUG("textual.include", "parent file path: " << pathname.string());
string::size_type pos = pathname.string().rfind('/');
if (pos == string::npos)
pos = pathname.string().rfind('\\');
- if (pos != string::npos)
+ if (pos != string::npos) {
filename = path(string(pathname.string(), 0, pos + 1)) / line;
+ DEBUG("textual.include", "normalized path: " << filename.string());
+ } else {
+ filename = path(string(".")) / line;
+ }
} else {
filename = line;
}
filename = resolve_path(filename);
-
- DEBUG("textual.include", "Line " << linenum << ": " <<
- "Including path '" << filename << "'");
+ DEBUG("textual.include", "resolved path: " << filename.string());
if (! exists(filename))
throw_(std::runtime_error,
@@ -677,22 +669,14 @@ void instance_t::include_directive(char * line)
ifstream stream(filename);
- instance_t instance(state_stack,
-#if defined(TIMELOG_SUPPORT)
- timelog,
-#endif
- stream, scope, journal, master,
- &filename, strict, this);
+ instance_t instance(context, stream, master, &filename, this);
instance.parse();
-
- errors += instance.errors;
- count += instance.count;
}
void instance_t::master_account_directive(char * line)
{
- if (account_t * acct = top_account()->find_account(line))
- state_stack.push_front(acct);
+ if (account_t * acct = context.top_account()->find_account(line))
+ context.state_stack.push_front(acct);
else
assert(! "Failed to create account");
}
@@ -701,18 +685,21 @@ void instance_t::end_directive(char * kind)
{
string name(kind);
- if ((name.empty() || name == "account") && ! front_is_account())
+ if ((name.empty() || name == "account") && ! context.front_is_account())
+ throw_(std::runtime_error,
+ _("'end account' directive does not match open directive"));
+ else if (name == "tag" && ! context.front_is_string())
throw_(std::runtime_error,
- _("'end account' directive does not match open tag directive"));
- else if (name == "tag" && ! front_is_string())
+ _("'end tag' directive does not match open directive"));
+ else if (name == "fixed" && ! context.front_is_fixed_rate())
throw_(std::runtime_error,
- _("'end tag' directive does not match open account directive"));
+ _("'end fixed' directive does not match open directive"));
- if (state_stack.size() <= 1)
+ if (context.state_stack.size() <= 1)
throw_(std::runtime_error,
_("'end' found, but no enclosing tag or account directive"));
else
- state_stack.pop_front();
+ context.state_stack.pop_front();
}
void instance_t::alias_directive(char * line)
@@ -729,13 +716,25 @@ void instance_t::alias_directive(char * line)
// name (e), add a reference to the account in the
// `account_aliases' map, which is used by the post
// parser to resolve alias references.
- account_t * acct = top_account()->find_account(e);
+ account_t * acct = context.top_account()->find_account(e);
std::pair<accounts_map::iterator, bool> result
= account_aliases.insert(accounts_map::value_type(b, acct));
assert(result.second);
}
}
+void instance_t::fixed_directive(char * line)
+{
+ if (optional<std::pair<commodity_t *, price_point_t> > price_point =
+ commodity_pool_t::current_pool->parse_price_directive(trim_ws(line),
+ true)) {
+ context.state_stack.push_front(fixed_rate_t(price_point->first,
+ price_point->second.price));
+ } else {
+ throw_(std::runtime_error, _("Error in fixed directive"));
+ }
+}
+
void instance_t::tag_directive(char * line)
{
string tag(trim_ws(line));
@@ -743,13 +742,13 @@ void instance_t::tag_directive(char * line)
if (tag.find(':') == string::npos)
tag = string(":") + tag + ":";
- state_stack.push_front(tag);
+ context.state_stack.push_front(tag);
}
void instance_t::define_directive(char * line)
{
expr_t def(skip_ws(line));
- def.compile(scope); // causes definitions to be established
+ def.compile(context.scope); // causes definitions to be established
}
bool instance_t::general_directive(char * line)
@@ -797,6 +796,13 @@ bool instance_t::general_directive(char * line)
}
break;
+ case 'f':
+ if (std::strcmp(p, "fixed") == 0) {
+ fixed_directive(arg);
+ return true;
+ }
+ break;
+
case 'i':
if (std::strcmp(p, "include") == 0) {
include_directive(arg);
@@ -810,6 +816,13 @@ bool instance_t::general_directive(char * line)
return true;
}
break;
+
+ case 'y':
+ if (std::strcmp(p, "year") == 0) {
+ year_directive(arg);
+ return true;
+ }
+ break;
}
if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) {
@@ -826,7 +839,6 @@ post_t * instance_t::parse_post(char * line,
std::streamsize len,
account_t * account,
xact_t * xact,
- bool honor_strict,
bool defer_expr)
{
TRACE_START(post_details, 1, "Time spent parsing postings:");
@@ -838,6 +850,7 @@ post_t * instance_t::parse_post(char * line,
post->pos->pathname = pathname;
post->pos->beg_pos = line_beg_pos;
post->pos->beg_line = linenum;
+ post->pos->sequence = context.sequence++;
char buf[MAX_LINE + 1];
std::strcpy(buf, line);
@@ -909,7 +922,7 @@ post_t * instance_t::parse_post(char * line,
if (! post->account)
post->account = account->find_account(name);
- if (honor_strict && strict && ! post->account->has_flags(ACCOUNT_KNOWN)) {
+ if (context.strict && ! post->account->has_flags(ACCOUNT_KNOWN)) {
if (post->_state == item_t::UNCLEARED)
warning_(_("\"%1\", line %2: Unknown account '%3'")
<< pathname << linenum << post->account->fullname());
@@ -929,17 +942,32 @@ post_t * instance_t::parse_post(char * line,
if (*next != '(') // indicates a value expression
post->amount.parse(stream, PARSE_NO_REDUCE);
else
- parse_amount_expr(scope, stream, post->amount, &post->amount_expr,
- post.get(), PARSE_NO_REDUCE | PARSE_SINGLE |
- PARSE_NO_ASSIGN, defer_expr);
-
- if (! post->amount.is_null() && honor_strict && strict &&
- post->amount.has_commodity() &&
- ! post->amount.commodity().has_flags(COMMODITY_KNOWN)) {
- if (post->_state == item_t::UNCLEARED)
- warning_(_("\"%1\", line %2: Unknown commodity '%3'")
- << pathname << linenum << post->amount.commodity());
- post->amount.commodity().add_flags(COMMODITY_KNOWN);
+ parse_amount_expr(stream, context.scope, *post.get(), post->amount,
+ PARSE_NO_REDUCE | PARSE_SINGLE | PARSE_NO_ASSIGN,
+ defer_expr, &post->amount_expr);
+
+ if (! post->amount.is_null() && post->amount.has_commodity()) {
+ if (context.strict &&
+ ! post->amount.commodity().has_flags(COMMODITY_KNOWN)) {
+ if (post->_state == item_t::UNCLEARED)
+ warning_(_("\"%1\", line %2: Unknown commodity '%3'")
+ << pathname << linenum << post->amount.commodity());
+ post->amount.commodity().add_flags(COMMODITY_KNOWN);
+ }
+
+ if (! post->amount.has_annotation()) {
+ foreach (state_t& state, context.state_stack) {
+ if (state.type() == typeid(fixed_rate_t)) {
+ fixed_rate_t& rate(boost::get<fixed_rate_t>(state));
+ if (*rate.first == post->amount.commodity()) {
+ annotation_t details(rate.second);
+ details.add_flags(ANNOTATION_PRICE_FIXATED);
+ post->amount.annotate(details);
+ break;
+ }
+ }
+ }
+ }
}
DEBUG("textual.parse", "line " << linenum << ": "
@@ -976,9 +1004,8 @@ post_t * instance_t::parse_post(char * line,
if (*p != '(') // indicates a value expression
post->cost->parse(cstream, PARSE_NO_MIGRATE);
else
- parse_amount_expr(scope, cstream, *post->cost, NULL, post.get(),
- PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_ASSIGN,
- defer_expr);
+ parse_amount_expr(cstream, context.scope, *post.get(), *post->cost,
+ PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_ASSIGN);
if (post->cost->sign() < 0)
throw parse_error(_("A posting's cost may not be negative"));
@@ -993,6 +1020,9 @@ post_t * instance_t::parse_post(char * line,
*post->cost *= post->amount;
post->cost->set_commodity(cost_commodity);
}
+ else if (post->amount.sign() < 0) {
+ post->cost->in_place_negate();
+ }
DEBUG("textual.parse", "line " << linenum << ": "
<< "Total cost is " << *post->cost);
@@ -1028,9 +1058,9 @@ post_t * instance_t::parse_post(char * line,
if (*p != '(') // indicates a value expression
post->assigned_amount->parse(stream, PARSE_NO_MIGRATE);
else
- parse_amount_expr(scope, stream, *post->assigned_amount, NULL,
- post.get(), PARSE_SINGLE | PARSE_NO_MIGRATE,
- defer_expr);
+ parse_amount_expr(stream, context.scope, *post.get(),
+ *post->assigned_amount,
+ PARSE_SINGLE | PARSE_NO_MIGRATE);
if (post->assigned_amount->is_null()) {
if (post->amount.is_null())
@@ -1113,8 +1143,8 @@ post_t * instance_t::parse_post(char * line,
post->pos->end_pos = curr_pos;
post->pos->end_line = linenum;
- if (! state_stack.empty()) {
- foreach (const state_t& state, state_stack)
+ if (! context.state_stack.empty()) {
+ foreach (const state_t& state, context.state_stack)
if (state.type() == typeid(string))
post->parse_tags(boost::get<string>(state).c_str());
}
@@ -1144,9 +1174,7 @@ bool instance_t::parse_posts(account_t * account,
std::streamsize len = read_line(line);
assert(len > 0);
- if (post_t * post =
- parse_post(line, len, account, NULL, /* honor_strict= */ false,
- defer_expr)) {
+ if (post_t * post = parse_post(line, len, account, NULL, defer_expr)) {
xact.add_post(post);
added = true;
}
@@ -1169,6 +1197,7 @@ xact_t * instance_t::parse_xact(char * line,
xact->pos->pathname = pathname;
xact->pos->beg_pos = line_beg_pos;
xact->pos->beg_line = linenum;
+ xact->pos->sequence = context.sequence++;
bool reveal_context = true;
@@ -1278,8 +1307,8 @@ xact_t * instance_t::parse_xact(char * line,
xact->pos->end_pos = curr_pos;
xact->pos->end_line = linenum;
- if (! state_stack.empty()) {
- foreach (const state_t& state, state_stack)
+ if (! context.state_stack.empty()) {
+ foreach (const state_t& state, context.state_stack)
if (state.type() == typeid(string))
xact->parse_tags(boost::get<string>(state).c_str());
}
@@ -1302,7 +1331,7 @@ xact_t * instance_t::parse_xact(char * line,
expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
- return scope.lookup(kind, name);
+ return context.scope.lookup(kind, name);
}
std::size_t journal_t::parse(std::istream& in,
@@ -1313,18 +1342,11 @@ std::size_t journal_t::parse(std::istream& in,
{
TRACE_START(parsing_total, 1, "Total time spent parsing text:");
- std::list<instance_t::state_t> state_stack;
-#if defined(TIMELOG_SUPPORT)
- time_log_t timelog(*this);
-#endif
+ parse_context_t context(*this, scope);
+ context.strict = strict;
- instance_t parsing_instance(state_stack,
-#if defined(TIMELOG_SUPPORT)
- timelog,
-#endif
- in, scope, *this, master,
- original_file, strict);
- parsing_instance.parse();
+ instance_t instance(context, in, master, original_file);
+ instance.parse();
TRACE_STOP(parsing_total, 1);
@@ -1336,10 +1358,10 @@ std::size_t journal_t::parse(std::istream& in,
TRACE_FINISH(instance_parse, 1); // report per-instance timers
TRACE_FINISH(parsing_total, 1);
- if (parsing_instance.errors > 0)
- throw static_cast<int>(parsing_instance.errors);
+ if (context.errors > 0)
+ throw static_cast<int>(context.errors);
- return parsing_instance.count;
+ return context.count;
}
} // namespace ledger
diff --git a/src/times.cc b/src/times.cc
index e3ccaff8..963639f1 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -224,7 +224,7 @@ namespace {
when = date_t(year ? *year : CURRENT_DATE().year(),
when.month(), when.day());
- if (when.month() > CURRENT_DATE().month())
+ if (! year && when.month() > CURRENT_DATE().month())
when -= gregorian::years(1);
}
}
@@ -824,15 +824,6 @@ date_interval_t date_parser_t::parse()
break;
}
- case lexer_t::token_t::TOK_MONTH: {
- date_t temp(today);
- temp += gregorian::months(adjust);
- inclusion_specifier =
- date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
- temp.month());
- break;
- }
-
case lexer_t::token_t::TOK_WEEK: {
date_t temp =
date_duration_t::find_nearest(today, date_duration_t::WEEKS);
@@ -852,10 +843,15 @@ date_interval_t date_parser_t::parse()
}
default:
- tok.unexpected();
+ case lexer_t::token_t::TOK_MONTH: {
+ date_t temp(today);
+ temp += gregorian::months(adjust);
+ inclusion_specifier =
+ date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
+ temp.month());
break;
}
- break;
+ }
}
case lexer_t::token_t::TOK_TODAY:
@@ -1321,10 +1317,13 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
catch (...) {}
}
+ start = begin;
+
string term;
bool alnum = std::isalnum(*begin);
- for (start = begin; (begin != end && ! std::isspace(*begin) &&
- alnum == std::isalnum(*begin)); begin++)
+ for (; (begin != end && ! std::isspace(*begin) &&
+ ((alnum && static_cast<bool>(std::isalnum(*begin))) ||
+ (! alnum && ! static_cast<bool>(std::isalnum(*begin))))); begin++)
term.push_back(*begin);
if (! term.empty()) {
diff --git a/src/value.cc b/src/value.cc
index f4df3329..7d079caf 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -861,7 +861,7 @@ bool value_t::is_less_than(const value_t& val) const
return as_amount() < val.as_amount();
}
catch (const amount_error&) {
- return compare_amount_commodities()(&as_amount(), &val.as_amount());
+ return commodity_t::compare_by_commodity()(&as_amount(), &val.as_amount());
}
default:
break;
@@ -1114,18 +1114,18 @@ void value_t::in_place_cast(type_t cast_type)
break;
}
- case BALANCE:
+ case BALANCE: {
+ const balance_t& bal(as_balance());
switch (cast_type) {
case AMOUNT: {
- const balance_t& temp(as_balance());
- if (temp.amounts.size() == 1) {
+ if (bal.amounts.size() == 1) {
// Because we are changing the current balance value to an amount
// value, and because set_amount takes a reference (and that memory is
// about to be repurposed), we must pass in a copy.
- set_amount(amount_t((*temp.amounts.begin()).second));
+ set_amount(amount_t((*bal.amounts.begin()).second));
return;
}
- else if (temp.amounts.size() == 0) {
+ else if (bal.amounts.size() == 0) {
set_amount(0L);
return;
}
@@ -1135,10 +1135,17 @@ void value_t::in_place_cast(type_t cast_type)
}
break;
}
+ case STRING:
+ if (bal.is_empty())
+ set_string("");
+ else
+ set_string(as_balance().to_string());
+ return;
default:
break;
}
break;
+ }
case STRING:
switch (cast_type) {
@@ -1353,7 +1360,8 @@ value_t value_t::exchange_commodities(const std::string& commodities,
p;
p = std::strtok(NULL, ",")) {
if (commodity_t * commodity =
- amount_t::current_pool->parse_price_expression(p, add_prices, moment)) {
+ commodity_pool_t::current_pool->parse_price_expression(p, add_prices,
+ moment)) {
value_t result = value(false, moment, *commodity);
if (! result.is_null())
return result;
@@ -1523,10 +1531,10 @@ void value_t::annotate(const annotation_t& details)
throw_(value_error, _("Cannot annotate %1") << label());
}
-bool value_t::is_annotated() const
+bool value_t::has_annotation() const
{
if (is_amount())
- return as_amount().is_annotated();
+ return as_amount().has_annotation();
else
throw_(value_error,
_("Cannot determine whether %1 is annotated") << label());
diff --git a/src/value.h b/src/value.h
index 2a420cd3..ffbb89e8 100644
--- a/src/value.h
+++ b/src/value.h
@@ -774,7 +774,7 @@ public:
* Annotated commodity methods.
*/
void annotate(const annotation_t& details);
- bool is_annotated() const;
+ bool has_annotation() const;
annotation_t& annotation();
const annotation_t& annotation() const {
diff --git a/src/xact.cc b/src/xact.cc
index f2694976..623c5772 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -122,7 +122,7 @@ bool xact_base_t::finalize()
amount_t& p(post->cost ? *post->cost : post->amount);
if (! p.is_null()) {
DEBUG("xact.finalize", "post must balance = " << p.reduced());
- if (! post->cost && post->amount.is_annotated() &&
+ if (! post->cost && post->amount.has_annotation() &&
post->amount.annotation().price) {
// If the amount has no cost, but is annotated with a per-unit
// price, use the price times the amount as the cost
@@ -221,7 +221,7 @@ bool xact_base_t::finalize()
foreach (post_t * post, posts) {
if (! post->amount.is_null()) {
- if (post->amount.is_annotated())
+ if (post->amount.has_annotation())
top_post = post;
else if (! top_post)
top_post = post;
@@ -260,7 +260,7 @@ bool xact_base_t::finalize()
foreach (post_t * post, posts) {
if (post != top_post && post->must_balance() &&
! post->amount.is_null() &&
- post->amount.is_annotated() &&
+ post->amount.has_annotation() &&
post->amount.annotation().price) {
amount_t temp = *post->amount.annotation().price * post->amount;
if (total_cost.is_null()) {
@@ -309,10 +309,11 @@ bool xact_base_t::finalize()
_("A posting's cost must be of a different commodity than its amount"));
cost_breakdown_t breakdown =
- amount_t::current_pool->exchange(post->amount, *post->cost, false,
- datetime_t(date(), time_duration(0, 0, 0, 0)));
+ commodity_pool_t::current_pool->exchange
+ (post->amount, *post->cost, false,
+ datetime_t(date(), time_duration(0, 0, 0, 0)));
- if (post->amount.is_annotated() &&
+ if (post->amount.has_annotation() &&
breakdown.basis_cost.commodity() ==
breakdown.final_cost.commodity()) {
if (amount_t gain_loss = (breakdown.basis_cost -