summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2008-07-20 05:03:54 -0400
committerJohn Wiegley <johnw@newartisans.com>2008-07-20 05:03:54 -0400
commit52fc9f2e4499e75d17e7f12d32a8391054d8634a (patch)
tree164b6b4cdb6717f2df7d154793499e90bdd3ae3e
parent59f6ffb863b7121b8a49c13dd64f9943ddaf7ed0 (diff)
downloadledger-52fc9f2e4499e75d17e7f12d32a8391054d8634a.tar.gz
ledger-52fc9f2e4499e75d17e7f12d32a8391054d8634a.tar.bz2
ledger-52fc9f2e4499e75d17e7f12d32a8391054d8634a.zip
Brought in the final round of 3.0 code, although it does not compile yet:
report, session, parts of xpath, main, journal, option.
-rw-r--r--Makefile.am1
-rw-r--r--binary.cc190
-rw-r--r--binary.h4
-rw-r--r--csv.cc3
-rw-r--r--emacs.cc10
-rw-r--r--format.cc32
-rw-r--r--format.h6
-rw-r--r--journal.cc264
-rw-r--r--journal.h256
-rw-r--r--main.cc698
-rw-r--r--option.cc1135
-rw-r--r--option.h70
-rw-r--r--parser.cc14
-rw-r--r--report.cc571
-rw-r--r--report.h249
-rw-r--r--session.cc313
-rw-r--r--session.h198
-rw-r--r--textual.cc2
-rw-r--r--valexpr.cc614
-rw-r--r--valexpr.h698
-rw-r--r--walk.cc24
-rw-r--r--walk.h50
22 files changed, 2729 insertions, 2673 deletions
diff --git a/Makefile.am b/Makefile.am
index a15edf50..934d0e91 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -51,6 +51,7 @@ libledger_la_SOURCES = \
qif.cc \
reconcile.cc \
report.cc \
+ session.cc \
startup.cc \
textual.cc \
valexpr.cc \
diff --git a/binary.cc b/binary.cc
index 90d72cfe..1c1487e9 100644
--- a/binary.cc
+++ b/binary.cc
@@ -179,6 +179,28 @@ void read_string(const char *& data, string * str)
read_guard(data, 0x3002);
}
+void read_string(std::istream& in, optional<string>& str)
+{
+ if (read_bool(in)) {
+ string temp;
+ read_string(in, temp);
+ str = temp;
+ } else {
+ str = none;
+ }
+}
+
+void read_string(const char *& data, optional<string>& str)
+{
+ if (read_bool(data)) {
+ string temp;
+ read_string(data, temp);
+ str = temp;
+ } else {
+ str = none;
+ }
+}
+
void write_bool(std::ostream& out, bool num)
{
@@ -207,6 +229,16 @@ void write_string(std::ostream& out, const string& str)
write_guard(out, 0x3002);
}
+void write_string(std::ostream& out, const optional<string>& str)
+{
+ if (str) {
+ write_bool(out, true);
+ write_string(out, *str);
+ } else {
+ write_bool(out, false);
+ }
+}
+
inline void read_amount(const char *& data, amount_t& amt)
{
commodity_t::ident_t ident;
@@ -260,50 +292,53 @@ inline void read_mask(const char *& data, mask_t *& mask)
mask->exclude = exclude;
}
-inline void read_value_expr(const char *& data, value_expr_t *& expr)
+inline expr::ptr_op_t read_value_expr(const char *& data)
{
- if (! read_bool(data)) {
- expr = NULL;
- return;
- }
+ if (! read_bool(data))
+ return expr::ptr_op_t();
- value_expr_t::kind_t kind;
+ expr::op_t::kind_t kind;
read_number(data, kind);
- expr = new value_expr_t(kind);
+ expr::ptr_op_t expr = new expr::op_t(kind);
- if (kind > value_expr_t::TERMINALS) {
- read_value_expr(data, expr->left);
- if (expr->left) expr->left->acquire();
- }
+ if (kind > expr::op_t::TERMINALS)
+ expr->set_left(read_value_expr(data));
switch (expr->kind) {
- case value_expr_t::O_ARG:
- case value_expr_t::INDEX:
- read_long(data, expr->arg_index);
+ case expr::op_t::O_ARG:
+ case expr::op_t::INDEX: {
+ long temp;
+ read_long(data, temp);
+ expr->set_long(temp);
break;
- case value_expr_t::CONSTANT:
- expr->value = new value_t;
- read_value(data, *expr->value);
+ }
+ case expr::op_t::VALUE: {
+ value_t temp;
+ read_value(data, temp);
+ expr->set_value(temp);
break;
+ }
- case value_expr_t::F_CODE_MASK:
- case value_expr_t::F_PAYEE_MASK:
- case value_expr_t::F_NOTE_MASK:
- case value_expr_t::F_ACCOUNT_MASK:
- case value_expr_t::F_SHORT_ACCOUNT_MASK:
- case value_expr_t::F_COMMODITY_MASK:
+ case expr::op_t::F_CODE_MASK:
+ case expr::op_t::F_PAYEE_MASK:
+ case expr::op_t::F_NOTE_MASK:
+ case expr::op_t::F_ACCOUNT_MASK:
+ case expr::op_t::F_SHORT_ACCOUNT_MASK:
+ case expr::op_t::F_COMMODITY_MASK:
+#if 0
if (read_bool(data))
read_mask(data, expr->mask);
+#endif
break;
default:
- if (kind > value_expr_t::TERMINALS) {
- read_value_expr(data, expr->right);
- if (expr->right) expr->right->acquire();
- }
+ if (kind > expr::op_t::TERMINALS)
+ expr->set_right(read_value_expr(data));
break;
}
+
+ return expr;
}
@@ -322,25 +357,29 @@ inline void read_transaction(const char *& data, transaction_t * xact)
read_string(data, xact->amount_expr.expr);
}
else {
- value_expr_t * ptr = NULL;
- read_value_expr(data, ptr);
- assert(ptr);
+ expr::ptr_op_t ptr = read_value_expr(data);
+ assert(ptr.get());
xact->amount_expr.reset(ptr);
read_string(data, xact->amount_expr.expr);
}
if (read_bool(data)) {
- xact->cost = new amount_t;
+ xact->cost = amount_t();
read_amount(data, *xact->cost);
- read_string(data, xact->cost_expr);
+
+ expr::ptr_op_t ptr = read_value_expr(data);
+ assert(ptr.get());
+ value_expr expr;
+ expr.reset(ptr);
+ xact->cost_expr = expr;
} else {
- xact->cost = NULL;
+ xact->cost = none;
}
read_number(data, xact->state);
- read_number(data, xact->flags);
- xact->flags |= TRANSACTION_BULK_ALLOC;
- read_string(data, &xact->note);
+ xact->set_flags(read_number<transaction_t::flags_t>(data));
+ xact->add_flags(TRANSACTION_BULK_ALLOC);
+ read_string(data, xact->note);
xact->beg_pos = read_long<unsigned long>(data);
read_long(data, xact->beg_line);
@@ -350,7 +389,7 @@ inline void read_transaction(const char *& data, transaction_t * xact)
xact->data = NULL;
if (xact->amount_expr)
- compute_amount(xact->amount_expr, xact->amount, xact);
+ expr::compute_amount(xact->amount_expr.get(), xact->amount, xact);
}
inline void read_entry_base(const char *& data, entry_base_t * entry,
@@ -369,7 +408,7 @@ inline void read_entry_base(const char *& data, entry_base_t * entry,
i++) {
new(xact_pool) transaction_t;
read_transaction(data, xact_pool);
- if (ignore_calculated && xact_pool->flags & TRANSACTION_CALCULATED)
+ if (ignore_calculated && xact_pool->has_flags(TRANSACTION_CALCULATED))
finalize = true;
entry->add_transaction(xact_pool++);
}
@@ -381,8 +420,8 @@ inline void read_entry(const char *& data, entry_t * entry,
read_entry_base(data, entry, xact_pool, finalize);
read_number(data, entry->_date);
read_number(data, entry->_date_eff);
- read_string(data, &entry->code);
- read_string(data, &entry->payee);
+ read_string(data, entry->code);
+ read_string(data, entry->payee);
}
inline void read_auto_entry(const char *& data, auto_entry_t * entry,
@@ -390,10 +429,7 @@ inline void read_auto_entry(const char *& data, auto_entry_t * entry,
{
bool ignore;
read_entry_base(data, entry, xact_pool, ignore);
- value_expr_t * expr;
- read_value_expr(data, expr);
- // the item_predicate constructor will acquire the reference
- entry->predicate = new item_predicate<transaction_t>(expr);
+ entry->predicate = item_predicate<transaction_t>(read_value_expr(data));
}
inline void read_period_entry(const char *& data, period_entry_t * entry,
@@ -411,8 +447,7 @@ inline commodity_t::base_t * read_commodity_base(const char *& data)
read_string(data, str);
- commodity_t::base_t * commodity = new commodity_t::base_t(str);
- *base_commodities_next++ = commodity;
+ std::auto_ptr<commodity_t::base_t> commodity(new commodity_t::base_t(str));
read_string(data, str);
if (! str.empty())
@@ -427,7 +462,7 @@ inline commodity_t::base_t * read_commodity_base(const char *& data)
read_number(data, flags);
commodity->set_flags(flags);
- return commodity;
+ return *base_commodities_next++ = commodity.release();
}
inline void read_commodity_base_extra(const char *& data,
@@ -596,8 +631,13 @@ unsigned int read_journal(std::istream& in,
// Make sure that the cache uses the same price database,
// otherwise it means that LEDGER_PRICE_DB has been changed, and
// we should ignore this cache file.
- if (read_string(in) != journal->price_db)
- return 0;
+ if (read_bool(in)) {
+ string pathname;
+ read_string(in, pathname);
+ if (! journal->price_db ||
+ journal->price_db->string() != std::string(pathname))
+ return 0;
+ }
}
// Read all of the data in at once, so that we're just dealing with
@@ -812,44 +852,47 @@ void write_mask(std::ostream& out, mask_t * mask)
write_string(out, mask->expr.str());
}
-void write_value_expr(std::ostream& out, const value_expr_t * expr)
+void write_value_expr(std::ostream& out, const expr::ptr_op_t expr)
{
if (! expr) {
write_bool(out, false);
return;
}
+
write_bool(out, true);
write_number(out, expr->kind);
- if (expr->kind > value_expr_t::TERMINALS)
- write_value_expr(out, expr->left);
+ if (expr->kind > expr::op_t::TERMINALS)
+ write_value_expr(out, expr->left());
switch (expr->kind) {
- case value_expr_t::O_ARG:
- case value_expr_t::INDEX:
- write_long(out, expr->arg_index);
+ case expr::op_t::O_ARG:
+ case expr::op_t::INDEX:
+ write_long(out, expr->as_long());
break;
- case value_expr_t::CONSTANT:
- write_value(out, *expr->value);
+ case expr::op_t::VALUE:
+ write_value(out, expr->as_value());
break;
- case value_expr_t::F_CODE_MASK:
- case value_expr_t::F_PAYEE_MASK:
- case value_expr_t::F_NOTE_MASK:
- case value_expr_t::F_ACCOUNT_MASK:
- case value_expr_t::F_SHORT_ACCOUNT_MASK:
- case value_expr_t::F_COMMODITY_MASK:
+ case expr::op_t::F_CODE_MASK:
+ case expr::op_t::F_PAYEE_MASK:
+ case expr::op_t::F_NOTE_MASK:
+ case expr::op_t::F_ACCOUNT_MASK:
+ case expr::op_t::F_SHORT_ACCOUNT_MASK:
+ case expr::op_t::F_COMMODITY_MASK:
+#if 0
if (expr->mask) {
write_bool(out, true);
write_mask(out, expr->mask);
} else {
write_bool(out, false);
}
+#endif
break;
default:
- if (expr->kind > value_expr_t::TERMINALS)
- write_value_expr(out, expr->right);
+ if (expr->kind > expr::op_t::TERMINALS)
+ write_value_expr(out, expr->right());
break;
}
@@ -862,7 +905,7 @@ void write_transaction(std::ostream& out, transaction_t * xact,
write_number(out, xact->_date_eff);
write_long(out, xact->account->ident);
- if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) {
+ if (ignore_calculated && xact->has_flags(TRANSACTION_CALCULATED)) {
write_number<unsigned char>(out, 0);
write_amount(out, amount_t());
}
@@ -882,16 +925,16 @@ void write_transaction(std::ostream& out, transaction_t * xact,
}
if (xact->cost &&
- (! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) {
+ (! (ignore_calculated && xact->has_flags(TRANSACTION_CALCULATED)))) {
write_bool(out, true);
write_amount(out, *xact->cost);
- write_string(out, xact->cost_expr);
+ write_string(out, xact->cost_expr->expr);
} else {
write_bool(out, false);
}
write_number(out, xact->state);
- write_number(out, xact->flags);
+ write_number(out, xact->flags());
write_string(out, xact->note);
write_long(out, xact->beg_pos);
@@ -938,7 +981,7 @@ void write_entry(std::ostream& out, entry_t * entry)
void write_auto_entry(std::ostream& out, auto_entry_t * entry)
{
write_entry_base(out, entry);
- write_value_expr(out, entry->predicate->predicate);
+ write_value_expr(out, entry->predicate.predicate.get());
}
void write_period_entry(std::ostream& out, period_entry_t * entry)
@@ -1088,7 +1131,12 @@ void write_journal(std::ostream& out, journal_t * journal)
// Write out the price database that relates to this data file, so
// that if it ever changes the cache can be invalidated.
- write_string(out, journal->price_db.string());
+ if (journal->price_db) {
+ write_bool(out, true);
+ write_string(out, journal->price_db->string());
+ } else {
+ write_bool(out, false);
+ }
}
ostream_pos_type data_val = out.tellp();
diff --git a/binary.h b/binary.h
index 0a8e1520..852dfe56 100644
--- a/binary.h
+++ b/binary.h
@@ -205,6 +205,9 @@ inline string read_string(const char *& data) {
return temp;
}
+void read_string(std::istream& in, optional<string>& str);
+void read_string(const char *& data, optional<string>& str);
+
template <typename T>
inline void write_number_nocheck(std::ostream& out, T num) {
@@ -262,6 +265,7 @@ void write_long(std::ostream& out, T num)
}
void write_string(std::ostream& out, const string& str);
+void write_string(std::ostream& out, const optional<string>& str);
template <typename T>
inline void write_object(std::ostream& out, const T& journal) {
diff --git a/csv.cc b/csv.cc
index 6a94ec4b..d3c3faf7 100644
--- a/csv.cc
+++ b/csv.cc
@@ -87,7 +87,8 @@ void format_csv_transactions::operator()(transaction_t& xact)
}
out << ',';
- write_escaped_string(out, xact.entry->code);
+ if (xact.entry->code)
+ write_escaped_string(out, *xact.entry->code);
out << ',';
{
diff --git a/emacs.cc b/emacs.cc
index 3a3b0908..44b372fe 100644
--- a/emacs.cc
+++ b/emacs.cc
@@ -20,10 +20,10 @@ void format_emacs_transactions::write_entry(entry_t& entry)
out << "(" << (date / 65536) << " " << (date % 65536) << " 0) ";
- if (entry.code.empty())
+ if (! entry.code)
out << "nil ";
else
- out << "\"" << entry.code << "\" ";
+ out << "\"" << *entry.code << "\" ";
if (entry.payee.empty())
out << "nil";
@@ -67,10 +67,8 @@ void format_emacs_transactions::operator()(transaction_t& xact)
if (xact.cost)
out << " \"" << *xact.cost << "\"";
- else if (! xact.note.empty())
- out << " nil";
- if (! xact.note.empty())
- out << " \"" << xact.note << "\"";
+ if (xact.note)
+ out << " \"" << *xact.note << "\"";
out << ")";
last_entry = xact.entry;
diff --git a/format.cc b/format.cc
index d64d611a..c64e149d 100644
--- a/format.cc
+++ b/format.cc
@@ -468,8 +468,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
else
stream << details.xact->amount.strip_annotations();
- if (! details.xact->cost_expr.empty())
- stream << details.xact->cost_expr;
+ if (details.xact->cost_expr)
+ stream << details.xact->cost_expr->expr;
else
stream << " @ " << amount_t(*details.xact->cost /
details.xact->amount).unround();
@@ -653,9 +653,9 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
case element_t::CODE: {
string temp;
- if (details.entry && ! details.entry->code.empty()) {
+ if (details.entry && details.entry->code) {
temp += "(";
- temp += details.entry->code;
+ temp += *details.entry->code;
temp += ") ";
}
out << temp;
@@ -670,14 +670,14 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
break;
case element_t::OPT_NOTE:
- if (details.xact && ! details.xact->note.empty())
+ if (details.xact && details.xact->note)
out << " ; ";
// fall through...
case element_t::NOTE:
if (details.xact)
out << (elem->max_width == 0 ?
- details.xact->note : truncate(details.xact->note,
+ details.xact->note : truncate(*details.xact->note,
elem->max_width));
break;
@@ -705,11 +705,11 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
details.account->fullname() :
partial_account_name(*details.account));
- if (details.xact && details.xact->flags & TRANSACTION_VIRTUAL) {
+ if (details.xact && details.xact->has_flags(TRANSACTION_VIRTUAL)) {
if (elem->max_width > 2)
name = truncate(name, elem->max_width - 2, true);
- if (details.xact->flags & TRANSACTION_BALANCE)
+ if (details.xact->has_flags(TRANSACTION_BALANCE))
name = string("[") + name + "]";
else
name = string("(") + name + ")";
@@ -828,7 +828,7 @@ void print_entry(std::ostream& out, const entry_base_t& entry_base,
}
else if (const auto_entry_t * entry =
dynamic_cast<const auto_entry_t *>(&entry_base)) {
- out << "= " << entry->predicate_string << '\n';
+ out << "= " << entry->predicate.predicate.expr << '\n';
print_format = prefix + " %-34A %12o\n";
}
else if (const period_entry_t * entry =
@@ -850,13 +850,13 @@ void print_entry(std::ostream& out, const entry_base_t& entry_base,
cleaner);
}
-bool disp_subaccounts_p(const account_t& account,
- const item_predicate<account_t>& disp_pred,
- const account_t *& to_show)
+bool disp_subaccounts_p(const account_t& account,
+ const optional<item_predicate<account_t> >& disp_pred,
+ const account_t *& to_show)
{
bool display = false;
unsigned int counted = 0;
- bool matches = disp_pred(account);
+ bool matches = disp_pred ? (*disp_pred)(account) : true;
value_t acct_total;
bool computed = false;
value_t result;
@@ -866,7 +866,7 @@ bool disp_subaccounts_p(const account_t& account,
for (accounts_map::const_iterator i = account.accounts.begin();
i != account.accounts.end();
i++) {
- if (! disp_pred(*(*i).second))
+ if (disp_pred && ! (*disp_pred)(*(*i).second))
continue;
compute_total(result, details_t(*(*i).second));
@@ -887,7 +887,7 @@ bool disp_subaccounts_p(const account_t& account,
}
bool display_account(const account_t& account,
- const item_predicate<account_t>& disp_pred)
+ const optional<item_predicate<account_t> >& disp_pred)
{
// Never display an account that has already been displayed.
if (account_has_xdata(account) &&
@@ -905,7 +905,7 @@ bool display_account(const account_t& account,
if (disp_subaccounts_p(account, disp_pred, account_to_show))
return true;
- return ! account_to_show && disp_pred(account);
+ return ! account_to_show && (! disp_pred || (*disp_pred)(account));
}
void format_account::operator()(account_t& account)
diff --git a/format.h b/format.h
index 68cc30dd..d61cdd0f 100644
--- a/format.h
+++ b/format.h
@@ -154,16 +154,16 @@ void print_entry(std::ostream& out, const entry_base_t& entry,
const string& prefix = "");
bool disp_subaccounts_p(const account_t& account,
- const item_predicate<account_t>& disp_pred,
+ const optional<item_predicate<account_t> >& disp_pred,
const account_t *& to_show);
inline bool disp_subaccounts_p(const account_t& account) {
const account_t * temp;
- return disp_subaccounts_p(account, item_predicate<account_t>(NULL), temp);
+ return disp_subaccounts_p(account, none, temp);
}
bool display_account(const account_t& account,
- const item_predicate<account_t>& disp_pred);
+ const optional<item_predicate<account_t> >& disp_pred);
class format_account : public item_handler<account_t>
{
diff --git a/journal.cc b/journal.cc
index 7ee8526e..7fcb4dda 100644
--- a/journal.cc
+++ b/journal.cc
@@ -1,9 +1,38 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#include "journal.h"
#include "utils.h"
#include "valexpr.h"
#include "mask.h"
-#include "format.h"
-#include "acconf.h"
namespace ledger {
@@ -13,22 +42,21 @@ bool transaction_t::use_effective_date = false;
transaction_t::~transaction_t()
{
- DEBUG("ledger.memory.dtors", "dtor transaction_t");
- if (cost) delete cost;
+ TRACE_DTOR(transaction_t);
}
datetime_t transaction_t::actual_date() const
{
- if (! is_valid(_date) && entry)
+ if (! _date && entry)
return entry->actual_date();
- return _date;
+ return *_date;
}
datetime_t transaction_t::effective_date() const
{
- if (! is_valid(_date_eff) && entry)
+ if (! _date_eff && entry)
return entry->effective_date();
- return _date_eff;
+ return *_date_eff;
}
bool transaction_t::valid() const
@@ -43,15 +71,10 @@ bool transaction_t::valid() const
return false;
}
- bool found = false;
- for (transactions_list::const_iterator i = entry->transactions.begin();
- i != entry->transactions.end();
- i++)
- if (*i == this) {
- found = true;
- break;
- }
- if (! found) {
+ transactions_list::const_iterator i =
+ std::find(entry->transactions.begin(),
+ entry->transactions.end(), this);
+ if (i == entry->transactions.end()) {
DEBUG("ledger.validate", "transaction_t: ! found");
return false;
}
@@ -71,7 +94,7 @@ bool transaction_t::valid() const
return false;
}
- if (flags & ~0x003f) {
+ if (flags() & ~0x003f) {
DEBUG("ledger.validate", "transaction_t: flags are bad");
return false;
}
@@ -99,29 +122,30 @@ bool entry_base_t::finalize()
// and the per-unit price of unpriced commodities.
value_t balance;
+ bool no_amounts = true;
+ bool saw_null = false;
- bool no_amounts = true;
- bool saw_null = false;
for (transactions_list::const_iterator x = transactions.begin();
x != transactions.end();
x++)
- if (! ((*x)->flags & TRANSACTION_VIRTUAL) ||
- ((*x)->flags & TRANSACTION_BALANCE)) {
- amount_t * p = (*x)->cost ? (*x)->cost : &(*x)->amount;
- if (! p->is_null()) {
+ if (! (*x)->has_flags(TRANSACTION_VIRTUAL) ||
+ (*x)->has_flags(TRANSACTION_BALANCE)) {
+ amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount);
+ if (! p.is_null()) {
if (no_amounts) {
- balance = *p;
+ balance = p;
no_amounts = false;
} else {
- balance += *p;
+ balance += p;
}
+ assert((*x)->amount);
if ((*x)->cost && (*x)->amount.commodity().annotated) {
annotated_commodity_t&
ann_comm(static_cast<annotated_commodity_t&>
((*x)->amount.commodity()));
if (ann_comm.details.price)
- balance += ((*ann_comm.details.price) * (*x)->amount -
+ balance += (*ann_comm.details.price * (*x)->amount.number() -
*((*x)->cost));
}
} else {
@@ -137,12 +161,12 @@ bool entry_base_t::finalize()
// account if one has been set.
if (journal && journal->basket && transactions.size() == 1) {
- assert(balance.type() < value_t::BALANCE);
+ assert(balance.is_amount());
transaction_t * nxact = new transaction_t(journal->basket);
// The amount doesn't need to be set because the code below will
// balance this transaction against the other.
add_transaction(nxact);
- nxact->flags |= TRANSACTION_CALCULATED;
+ nxact->add_flags(TRANSACTION_CALCULATED);
}
// If the first transaction of a two-transaction entry is of a
@@ -150,30 +174,28 @@ bool entry_base_t::finalize()
// determine its price by dividing the unit count into the value of
// the balance. This is done for the last eligible commodity.
- if (! saw_null && balance && balance.is_type(value_t::BALANCE) &&
- balance.as_balance_lval().amounts.size() == 2) {
- transactions_list::const_iterator x = transactions.begin();
- commodity_t& this_comm = (*x)->amount.commodity();
-
+ if (! saw_null && balance && balance.is_balance()) {
balance_t& bal(balance.as_balance_lval());
+ if (bal.amounts.size() == 2) {
+ transactions_list::const_iterator x = transactions.begin();
+ assert((*x)->amount);
+ commodity_t& this_comm = (*x)->amount.commodity();
+
+ balance_t::amounts_map::const_iterator this_bal =
+ bal.amounts.find(&this_comm);
+ balance_t::amounts_map::const_iterator other_bal =
+ bal.amounts.begin();
+ if (this_bal == other_bal)
+ other_bal++;
- balance_t::amounts_map::const_iterator this_amt =
- bal.amounts.find(&this_comm);
- balance_t::amounts_map::const_iterator other_amt =
- bal.amounts.begin();
- if (this_amt == other_amt)
- other_amt++;
-
- if (this_amt != bal.amounts.end()) {
amount_t per_unit_cost =
- amount_t((*other_amt).second / (*this_amt).second).unround();
+ amount_t((*other_bal).second / (*this_bal).second.number()).unround();
for (; x != transactions.end(); x++) {
- if ((*x)->cost || ((*x)->flags & TRANSACTION_VIRTUAL) ||
- ! (*x)->amount || (*x)->amount.commodity() != this_comm)
+ if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) ||
+ (*x)->amount.commodity() != this_comm)
continue;
- assert((*x)->amount);
balance -= (*x)->amount;
entry_t * entry = dynamic_cast<entry_t *>(this);
@@ -182,10 +204,10 @@ bool entry_base_t::finalize()
! (*x)->amount.commodity().annotated)
(*x)->amount.annotate_commodity
(annotation_t(per_unit_cost.abs(),
- entry ? optional<datetime_t>(entry->actual_date()) : none,
- entry ? optional<string>(entry->code) : none));
+ entry ? entry->actual_date() : optional<datetime_t>(),
+ entry ? entry->code : optional<string>()));
- (*x)->cost = new amount_t(- (per_unit_cost * (*x)->amount));
+ (*x)->cost = - (per_unit_cost * (*x)->amount.number());
balance += *(*x)->cost;
}
}
@@ -199,13 +221,14 @@ bool entry_base_t::finalize()
for (transactions_list::const_iterator x = transactions.begin();
x != transactions.end();
x++) {
- if (! (*x)->amount.is_null() ||
- (((*x)->flags & TRANSACTION_VIRTUAL) &&
- ! ((*x)->flags & TRANSACTION_BALANCE)))
+ if ((*x)->amount ||
+ ((*x)->has_flags(TRANSACTION_VIRTUAL) &&
+ ! (*x)->has_flags(TRANSACTION_BALANCE)))
continue;
if (! empty_allowed)
- throw new error("Only one transaction with null amount allowed per entry");
+ throw_(std::logic_error,
+ "Only one transaction with null amount allowed per entry");
empty_allowed = false;
// If one transaction gives no value at all, its value will become
@@ -213,25 +236,25 @@ bool entry_base_t::finalize()
// commodities are involved, multiple transactions will be
// generated to balance them all.
- balance_t * bal = NULL;
+ const balance_t * bal = NULL;
switch (balance.type()) {
case value_t::BALANCE_PAIR:
- bal = &(balance.as_balance_pair_lval().quantity());
+ bal = &balance.as_balance_pair_lval().quantity();
// fall through...
case value_t::BALANCE:
if (! bal)
- bal = &(balance.as_balance_lval());
+ bal = &balance.as_balance_lval();
if (bal->amounts.size() < 2) {
balance.cast(value_t::AMOUNT);
} else {
bool first = true;
- for (balance_t::amounts_map::const_iterator i = bal->amounts.begin();
+ for (balance_t::amounts_map::const_iterator
+ i = bal->amounts.begin();
i != bal->amounts.end();
i++) {
- amount_t amt = (*i).second;
- amt.negate();
+ amount_t amt = (*i).second.negate();
if (first) {
(*x)->amount = amt;
@@ -239,7 +262,7 @@ bool entry_base_t::finalize()
} else {
transaction_t * nxact = new transaction_t((*x)->account);
add_transaction(nxact);
- nxact->flags |= TRANSACTION_CALCULATED;
+ nxact->add_flags(TRANSACTION_CALCULATED);
nxact->amount = amt;
}
@@ -250,9 +273,8 @@ bool entry_base_t::finalize()
// fall through...
case value_t::AMOUNT:
- (*x)->amount = balance.as_amount_lval();
- (*x)->amount.in_place_negate();
- (*x)->flags |= TRANSACTION_CALCULATED;
+ (*x)->amount = balance.as_amount().negate();
+ (*x)->add_flags(TRANSACTION_CALCULATED);
balance += (*x)->amount;
break;
@@ -280,8 +302,7 @@ entry_t::entry_t(const entry_t& e)
: entry_base_t(e), _date(e._date), _date_eff(e._date_eff),
code(e.code), payee(e.payee)
{
- DEBUG("ledger.memory.ctors", "ctor entry_t");
-
+ TRACE_CTOR(entry_t, "copy");
for (transactions_list::const_iterator i = transactions.begin();
i != transactions.end();
i++)
@@ -333,20 +354,6 @@ bool entry_t::valid() const
return true;
}
-auto_entry_t::auto_entry_t(const string& _predicate)
- : predicate_string(_predicate)
-{
- DEBUG("ledger.memory.ctors", "ctor auto_entry_t");
- predicate = new item_predicate<transaction_t>(predicate_string);
-}
-
-auto_entry_t::~auto_entry_t()
-{
- DEBUG("ledger.memory.dtors", "dtor auto_entry_t");
- if (predicate)
- delete predicate;
-}
-
void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
{
transactions_list initial_xacts(entry.transactions.begin(),
@@ -355,14 +362,16 @@ void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
for (transactions_list::iterator i = initial_xacts.begin();
i != initial_xacts.end();
i++) {
- if ((*predicate)(**i)) {
+ if (predicate(**i)) {
for (transactions_list::iterator t = transactions.begin();
t != transactions.end();
t++) {
amount_t amt;
+ assert((*t)->amount);
if (! (*t)->amount.commodity()) {
if (! post)
continue;
+ assert((*i)->amount);
amt = (*i)->amount * (*t)->amount;
} else {
if (post)
@@ -371,13 +380,13 @@ void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
}
account_t * account = (*t)->account;
- string fullname = account->fullname();
+ string fullname = account->fullname();
assert(! fullname.empty());
if (fullname == "$account" || fullname == "@account")
account = (*i)->account;
transaction_t * xact
- = new transaction_t(account, amt, (*t)->flags | TRANSACTION_AUTO);
+ = new transaction_t(account, amt, (*t)->flags() | TRANSACTION_AUTO);
// Copy over details so that the resulting transaction is a mirror of
// the automated entry's one.
@@ -398,17 +407,16 @@ void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
account_t::~account_t()
{
- DEBUG("ledger.memory.dtors", "dtor account_t " << this);
- //assert(! data);
+ TRACE_DTOR(account_t);
for (accounts_map::iterator i = accounts.begin();
i != accounts.end();
i++)
- delete (*i).second;
+ checked_delete((*i).second);
}
account_t * account_t::find_account(const string& name,
- const bool auto_create)
+ const bool auto_create)
{
accounts_map::const_iterator i = accounts.find(name);
if (i != accounts.end())
@@ -442,7 +450,7 @@ account_t * account_t::find_account(const string& name,
account->journal = journal;
std::pair<accounts_map::iterator, bool> result
- = accounts.insert(accounts_pair(first, account));
+ = accounts.insert(accounts_map::value_type(first, account));
assert(result.second);
} else {
account = (*i).second;
@@ -479,8 +487,8 @@ string account_t::fullname() const
if (! _fullname.empty()) {
return _fullname;
} else {
- const account_t * first = this;
- string fullname = name;
+ const account_t * first = this;
+ string fullname = name;
while (first->parent) {
first = first->parent;
@@ -526,29 +534,33 @@ bool account_t::valid() const
journal_t::~journal_t()
{
- DEBUG("ledger.memory.dtors", "dtor journal_t");
+ TRACE_DTOR(journal_t);
assert(master);
- delete master;
+ checked_delete(master);
// Don't bother unhooking each entry's transactions from the
// accounts they refer to, because all accounts are about to
// be deleted.
for (entries_list::iterator i = entries.begin();
i != entries.end();
- i++)
+ i++) {
if (! item_pool ||
- ((char *) *i) < item_pool || ((char *) *i) >= item_pool_end)
- delete *i;
- else
+ reinterpret_cast<char *>(*i) < item_pool ||
+ reinterpret_cast<char *>(*i) >= item_pool_end) {
+ checked_delete(*i);
+ } else {
(*i)->~entry_t();
+ }
+ }
for (auto_entries_list::iterator i = auto_entries.begin();
i != auto_entries.end();
i++)
if (! item_pool ||
- ((char *) *i) < item_pool || ((char *) *i) >= item_pool_end)
- delete *i;
+ reinterpret_cast<char *>(*i) < item_pool ||
+ reinterpret_cast<char *>(*i) >= item_pool_end)
+ checked_delete(*i);
else
(*i)->~auto_entry_t();
@@ -556,13 +568,14 @@ journal_t::~journal_t()
i != period_entries.end();
i++)
if (! item_pool ||
- ((char *) *i) < item_pool || ((char *) *i) >= item_pool_end)
- delete *i;
+ reinterpret_cast<char *>(*i) < item_pool ||
+ reinterpret_cast<char *>(*i) >= item_pool_end)
+ checked_delete(*i);
else
(*i)->~period_entry_t();
if (item_pool)
- delete[] item_pool;
+ checked_array_delete(item_pool);
}
bool journal_t::add_entry(entry_t * entry)
@@ -581,9 +594,11 @@ bool journal_t::add_entry(entry_t * entry)
for (transactions_list::const_iterator i = entry->transactions.begin();
i != entry->transactions.end();
i++)
- if ((*i)->cost && (*i)->amount)
+ if ((*i)->cost) {
+ assert((*i)->amount);
(*i)->amount.commodity().add_price(entry->date(),
- *(*i)->cost / (*i)->amount);
+ *(*i)->cost / (*i)->amount.number());
+ }
return true;
}
@@ -621,18 +636,45 @@ bool journal_t::valid() const
return false;
}
- for (commodity_pool_t::commodities_by_ident::const_iterator
- i = amount_t::current_pool->commodities.begin();
- i != amount_t::current_pool->commodities.end();
- i++)
- if (! (*i)->valid()) {
- DEBUG("ledger.validate", "journal_t: commodity not valid");
- return false;
- }
-
return true;
}
+void print_entry(std::ostream& out, const entry_base_t& entry_base,
+ const string& prefix)
+{
+ string print_format;
+
+ if (dynamic_cast<const entry_t *>(&entry_base)) {
+ print_format = (prefix + "%D %X%C%P\n" +
+ prefix + " %-34A %12o\n%/" +
+ prefix + " %-34A %12o\n");
+ }
+ else if (const auto_entry_t * entry =
+ dynamic_cast<const auto_entry_t *>(&entry_base)) {
+ out << "= " << entry->predicate.predicate.expr << '\n';
+ print_format = prefix + " %-34A %12o\n";
+ }
+ else if (const period_entry_t * entry =
+ dynamic_cast<const period_entry_t *>(&entry_base)) {
+ out << "~ " << entry->period_string << '\n';
+ print_format = prefix + " %-34A %12o\n";
+ }
+ else {
+ assert(false);
+ }
+
+#if 0
+ format_entries formatter(out, print_format);
+ walk_transactions(const_cast<transactions_list&>(entry_base.transactions),
+ formatter);
+ formatter.flush();
+
+ clear_transaction_xdata cleaner;
+ walk_transactions(const_cast<transactions_list&>(entry_base.transactions),
+ cleaner);
+#endif
+}
+
void entry_context::describe(std::ostream& out) const throw()
{
if (! desc.empty())
diff --git a/journal.h b/journal.h
index c04a4ea9..86ae58fd 100644
--- a/journal.h
+++ b/journal.h
@@ -1,3 +1,34 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#ifndef _JOURNAL_H
#define _JOURNAL_H
@@ -19,52 +50,64 @@ namespace ledger {
class entry_t;
class account_t;
-class transaction_t
+class transaction_t : public supports_flags<>
{
public:
enum state_t { UNCLEARED, CLEARED, PENDING };
- entry_t * entry;
- datetime_t _date;
- datetime_t _date_eff;
- account_t * account;
- amount_t amount;
- value_expr amount_expr;
- amount_t * cost;
- string cost_expr;
- state_t state;
- unsigned short flags;
- string note;
- istream_pos_type beg_pos;
- unsigned long beg_line;
- istream_pos_type end_pos;
- unsigned long end_line;
- mutable void * data;
-
- static bool use_effective_date;
-
- transaction_t(account_t * _account = NULL)
- : entry(NULL), account(_account), cost(NULL),
- state(UNCLEARED), flags(TRANSACTION_NORMAL),
- beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
- DEBUG("ledger.memory.ctors", "ctor transaction_t");
- }
- transaction_t(account_t * _account,
- const amount_t& _amount,
- unsigned int _flags = TRANSACTION_NORMAL,
- const string& _note = "")
- : entry(NULL), account(_account), amount(_amount), cost(NULL),
- state(UNCLEARED), flags(_flags),
- note(_note), beg_pos(0), beg_line(0), end_pos(0), end_line(0),
- data(NULL) {
- DEBUG("ledger.memory.ctors", "ctor transaction_t");
- }
- transaction_t(const transaction_t& xact)
- : entry(xact.entry), account(xact.account), amount(xact.amount),
- cost(xact.cost ? new amount_t(*xact.cost) : NULL),
- state(xact.state), flags(xact.flags), note(xact.note),
- beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
- DEBUG("ledger.memory.ctors", "ctor transaction_t");
+ entry_t * entry;
+ state_t state;
+ account_t * account;
+ optional<datetime_t> _date;
+ optional<datetime_t> _date_eff;
+ amount_t amount;
+ value_expr amount_expr;
+ optional<amount_t> cost;
+ optional<value_expr> cost_expr;
+ optional<string> note;
+ istream_pos_type beg_pos;
+ unsigned long beg_line;
+ istream_pos_type end_pos;
+ unsigned long end_line;
+
+ mutable void * data;
+ static bool use_effective_date;
+
+ explicit transaction_t(account_t * _account = NULL)
+ : supports_flags<>(TRANSACTION_NORMAL), entry(NULL),
+ state(UNCLEARED), account(_account),
+ beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
+ {
+ TRACE_CTOR(transaction_t, "account_t *");
+ }
+ explicit transaction_t(account_t * _account,
+ const amount_t& _amount,
+ unsigned int _flags = TRANSACTION_NORMAL,
+ const optional<string> _note = none)
+ : supports_flags<>(_flags), entry(NULL), state(UNCLEARED),
+ account(_account), amount(_amount), note(_note),
+ beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
+ {
+ TRACE_CTOR(transaction_t,
+ "account_t *, const amount_t&, unsigned int, const string&");
+ }
+ explicit transaction_t(const transaction_t& xact)
+ : supports_flags<>(xact),
+ entry(xact.entry),
+ state(xact.state),
+ account(xact.account),
+ _date(xact._date),
+ _date_eff(xact._date_eff),
+ amount(xact.amount),
+ cost(xact.cost),
+ note(xact.note),
+ beg_pos(xact.beg_pos),
+ beg_line(xact.beg_line),
+ end_pos(xact.end_pos),
+ end_line(xact.end_line),
+ data(xact.data) // jww (2008-07-19): What are the copy semantics?
+ {
+ TRACE_CTOR(transaction_t, "copy");
}
~transaction_t();
@@ -77,13 +120,6 @@ class transaction_t
return actual_date();
}
- bool operator==(const transaction_t& xact) {
- return this == &xact;
- }
- bool operator!=(const transaction_t& xact) {
- return ! (*this == xact);
- }
-
bool valid() const;
};
@@ -115,24 +151,24 @@ class entry_base_t
entry_base_t() : journal(NULL),
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
{
- DEBUG("ledger.memory.ctors", "ctor entry_base_t");
+ TRACE_CTOR(entry_base_t, "");
}
entry_base_t(const entry_base_t& e) : journal(NULL),
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
{
- DEBUG("ledger.memory.ctors", "ctor entry_base_t");
+ TRACE_CTOR(entry_base_t, "copy");
for (transactions_list::const_iterator i = e.transactions.begin();
i != e.transactions.end();
i++)
transactions.push_back(new transaction_t(**i));
}
virtual ~entry_base_t() {
- DEBUG("ledger.memory.dtors", "dtor entry_base_t");
+ TRACE_DTOR(entry_base_t);
for (transactions_list::iterator i = transactions.begin();
i != transactions.end();
i++)
- if (! ((*i)->flags & TRANSACTION_BULK_ALLOC))
- delete *i;
+ if (! (*i)->has_flags(TRANSACTION_BULK_ALLOC))
+ checked_delete(*i);
else
(*i)->~transaction_t();
}
@@ -153,28 +189,28 @@ class entry_base_t
class entry_t : public entry_base_t
{
- public:
- datetime_t _date;
- datetime_t _date_eff;
- string code;
- string payee;
+public:
+ datetime_t _date;
+ optional<datetime_t> _date_eff;
+ optional<string> code;
+ string payee;
entry_t() {
- DEBUG("ledger.memory.ctors", "ctor entry_t");
+ TRACE_CTOR(entry_t, "");
}
entry_t(const entry_t& e);
virtual ~entry_t() {
- DEBUG("ledger.memory.dtors", "dtor entry_t");
+ TRACE_DTOR(entry_t);
}
datetime_t actual_date() const {
return _date;
}
datetime_t effective_date() const {
- if (! is_valid(_date_eff))
+ if (! _date_eff)
return _date;
- return _date_eff;
+ return *_date_eff;
}
datetime_t date() const {
if (transaction_t::use_effective_date)
@@ -200,8 +236,8 @@ class entry_context : public error_context {
const entry_base_t& entry;
entry_context(const entry_base_t& _entry,
- const string& desc = "") throw()
- : error_context(desc), entry(_entry) {}
+ const string& _desc = "") throw()
+ : error_context(_desc), entry(_entry) {}
virtual ~entry_context() throw() {}
virtual void describe(std::ostream& out) const throw();
@@ -214,15 +250,18 @@ class item_predicate;
class auto_entry_t : public entry_base_t
{
public:
- item_predicate<transaction_t> * predicate;
- string predicate_string;
+ item_predicate<transaction_t> predicate;
- auto_entry_t() : predicate(NULL) {
- DEBUG("ledger.memory.ctors", "ctor auto_entry_t");
+ auto_entry_t();
+ auto_entry_t(const string& _predicate)
+ : predicate(_predicate)
+ {
+ TRACE_CTOR(auto_entry_t, "const string&");
}
- auto_entry_t(const string& _predicate);
- virtual ~auto_entry_t();
+ virtual ~auto_entry_t() {
+ TRACE_DTOR(auto_entry_t);
+ }
virtual void extend_entry(entry_base_t& entry, bool post);
virtual bool valid() const {
@@ -230,7 +269,6 @@ public:
}
};
-class journal_t;
struct auto_entry_finalizer_t : public entry_finalizer_t {
journal_t * journal;
auto_entry_finalizer_t(journal_t * _journal) : journal(_journal) {}
@@ -245,19 +283,19 @@ class period_entry_t : public entry_base_t
string period_string;
period_entry_t() {
- DEBUG("ledger.memory.ctors", "ctor period_entry_t");
+ TRACE_CTOR(period_entry_t, "");
}
period_entry_t(const string& _period)
: period(_period), period_string(_period) {
- DEBUG("ledger.memory.ctors", "ctor period_entry_t");
+ TRACE_CTOR(period_entry_t, "const string&");
}
period_entry_t(const period_entry_t& e)
: entry_base_t(e), period(e.period), period_string(e.period_string) {
- DEBUG("ledger.memory.ctors", "ctor period_entry_t");
+ TRACE_CTOR(period_entry_t, "copy");
}
virtual ~period_entry_t() {
- DEBUG("ledger.memory.dtors", "dtor period_entry_t");
+ TRACE_DTOR(period_entry_t);
}
virtual bool valid() const {
@@ -267,44 +305,39 @@ class period_entry_t : public entry_base_t
typedef std::map<const string, account_t *> accounts_map;
-typedef std::pair<const string, account_t *> accounts_pair;
class account_t
{
public:
typedef unsigned long ident_t;
- journal_t * journal;
- account_t * parent;
- string name;
- string note;
- unsigned short depth;
- accounts_map accounts;
+ journal_t * journal;
+ account_t * parent;
+ string name;
+ optional<string> note;
+ unsigned short depth;
+ accounts_map accounts;
mutable void * data;
mutable ident_t ident;
- mutable string _fullname;
+ mutable string _fullname;
account_t(account_t * _parent = NULL,
const string& _name = "",
- const string& _note = "")
+ const optional<string> _note = none)
: parent(_parent), name(_name), note(_note),
depth(parent ? parent->depth + 1 : 0), data(NULL), ident(0) {
- DEBUG("ledger.memory.ctors", "ctor account_t " << this);
+ TRACE_CTOR(account_t, "account_t *, const string&, const string&");
}
~account_t();
- bool operator==(const account_t& account) {
- return this == &account;
- }
- bool operator!=(const account_t& account) {
- return ! (*this == account);
+ operator string() const {
+ return fullname();
}
-
string fullname() const;
void add_account(account_t * acct) {
- accounts.insert(accounts_pair(acct->name, acct));
+ accounts.insert(accounts_map::value_type(acct->name, acct));
acct->journal = journal;
}
bool remove_account(account_t * acct) {
@@ -315,10 +348,6 @@ class account_t
account_t * find_account(const string& name, bool auto_create = true);
- operator string() const {
- return fullname();
- }
-
bool valid() const;
friend class journal_t;
@@ -331,7 +360,8 @@ struct func_finalizer_t : public entry_finalizer_t {
typedef bool (*func_t)(entry_t& entry, bool post);
func_t func;
func_finalizer_t(func_t _func) : func(_func) {}
- func_finalizer_t(const func_finalizer_t& other) : func(other.func) {}
+ func_finalizer_t(const func_finalizer_t& other) :
+ entry_finalizer_t(), func(other.func) {}
virtual bool operator()(entry_t& entry, bool post) {
return func(entry, post);
}
@@ -364,19 +394,19 @@ bool run_hooks(std::list<T>& list, Data& item, bool post) {
typedef std::list<entry_t *> entries_list;
typedef std::list<auto_entry_t *> auto_entries_list;
typedef std::list<period_entry_t *> period_entries_list;
+typedef std::list<path> paths_list;
typedef std::list<string> strings_list;
-typedef std::list<path> paths_list;
class journal_t
{
public:
- account_t * master;
- account_t * basket;
- entries_list entries;
- paths_list sources;
- path price_db;
- char * item_pool;
- char * item_pool_end;
+ account_t * master;
+ account_t * basket;
+ entries_list entries;
+ paths_list sources;
+ optional<path> price_db;
+ char * item_pool;
+ char * item_pool_end;
auto_entries_list auto_entries;
period_entries_list period_entries;
@@ -384,21 +414,13 @@ class journal_t
std::list<entry_finalizer_t *> entry_finalize_hooks;
- journal_t() : basket(NULL) {
- DEBUG("ledger.memory.ctors", "ctor journal_t");
+ journal_t() : basket(NULL), item_pool(NULL), item_pool_end(NULL) {
+ TRACE_CTOR(journal_t, "");
master = new account_t(NULL, "");
master->journal = this;
- item_pool = item_pool_end = NULL;
}
~journal_t();
- bool operator==(const journal_t& journal) {
- return this == &journal;
- }
- bool operator!=(const journal_t& journal) {
- return ! (*this == journal);
- }
-
void add_account(account_t * acct) {
master->add_account(acct);
acct->journal = this;
@@ -414,7 +436,7 @@ class journal_t
return (*c).second;
account_t * account = master->find_account(name, auto_create);
- accounts_cache.insert(accounts_pair(name, account));
+ accounts_cache.insert(accounts_map::value_type(name, account));
account->journal = this;
return account;
}
diff --git a/main.cc b/main.cc
index bae22bcd..469bb5ee 100644
--- a/main.cc
+++ b/main.cc
@@ -1,232 +1,214 @@
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include <algorithm>
-#include <exception>
-#include <iterator>
-#include <string>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "acconf.h"
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "utils.h"
+#include "option.h"
+//#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+//#include "gnucash.h"
+//#endif
+//#include "qif.h"
+//#include "ofx.h"
+#include "jbuilder.h"
+#include "compile.h"
+
+#include <ledger.h>
#ifdef HAVE_UNIX_PIPES
#include <sys/types.h>
#include <sys/wait.h>
-#include <unistd.h>
-#include "fdstream.hpp"
+#include <fdstream.hpp>
#endif
-#include "ledger.h"
-
-using namespace ledger;
-
-int parse_and_report(config_t& config, std::auto_ptr<journal_t>& journal,
- report_t& report, int argc, char * argv[], char * envp[])
+static int read_and_report(ledger::report_t& report, int argc, char * argv[],
+ char * envp[])
{
- // Configure the terminus for value expressions
+ using namespace ledger;
- ledger::terminus = current_moment;
+ session_t& session(report.session);
- // Parse command-line arguments, and those set in the environment
+ // Handle the command-line arguments
- std::list<string> args;
- process_arguments(ledger::config_options, argc - 1, argv + 1, false, args);
+ strings_list args;
+ process_arguments(argc - 1, argv + 1, false, report, args);
if (args.empty()) {
- option_help(std::cerr);
+#if 0
+ help(std::cerr);
+#endif
return 1;
}
strings_list::iterator arg = args.begin();
- if (config.cache_file == "<none>")
- config.use_cache = false;
+ if (! session.cache_file)
+ session.use_cache = false;
else
- config.use_cache = config.data_file.empty() && config.price_db.empty();
- DEBUG("ledger.config.cache", "1. use_cache = " << config.use_cache);
+ session.use_cache = ! session.data_file.empty() && session.price_db;
-#if 0
- TRACE(main, "Processing options and environment variables");
-#endif
+ DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
- process_environment(ledger::config_options,
- const_cast<const char **>(envp), "LEDGER_");
+ // Process the environment settings
-#if 1
- // These are here for backwards compatability, but are deprecated.
+ TRACE_START(environment, 1, "Processed environment variables");
+ process_environment(const_cast<const char **>(envp), "LEDGER_", report);
+ TRACE_FINISH(environment, 1);
- if (const char * p = std::getenv("LEDGER"))
- process_option(ledger::config_options, "file", p);
- if (const char * p = std::getenv("LEDGER_INIT"))
- process_option(ledger::config_options, "init-file", p);
- if (const char * p = std::getenv("PRICE_HIST"))
- process_option(ledger::config_options, "price-db", p);
- if (const char * p = std::getenv("PRICE_EXP"))
- process_option(ledger::config_options, "price-exp", p);
-#endif
+ optional<path> home;
+ if (const char * home_var = std::getenv("HOME"))
+ home = home_var;
- const char * p = std::getenv("HOME");
- string home = p ? p : "";
+ if (! session.init_file)
+ session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
+ if (! session.price_db)
+ session.price_db = home ? *home / ".pricedb" : "./.pricedb";
- if (config.init_file.empty())
- config.init_file = home + "/.ledgerrc";
- if (config.price_db.empty())
- config.price_db = home + "/.pricedb";
+ if (! session.cache_file)
+ session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
- if (config.cache_file.empty())
- config.cache_file = home + "/.ledger-cache";
+ if (session.data_file == *session.cache_file)
+ session.use_cache = false;
- if (config.data_file == config.cache_file)
- config.use_cache = false;
- DEBUG("ledger.config.cache", "2. use_cache = " << config.use_cache);
+ DEBUG("ledger.session.cache", "2. use_cache = " << session.use_cache);
-#if 0
- TRACE(main, string("Initialization file is ") + config.init_file);
- TRACE(main, string("Price database is ") + config.price_db);
- TRACE(main, string("Binary cache is ") + config.cache_file);
- TRACE(main, string("Main journal is ") + config.data_file);
+ INFO("Initialization file is " << session.init_file->string());
+ INFO("Price database is " << session.price_db->string());
+ INFO("Binary cache is " << session.cache_file->string());
+ INFO("Journal file is " << session.data_file.string());
- TRACE(main, string("Based on option settings, binary cache ") +
- (config.use_cache ? "WILL " : "will NOT ") + "be used");
-#endif
+ if (! session.use_cache)
+ INFO("Binary cache mechanism will not be used");
+
+ // Read the command word and create a command object based on it
+
+ string verb = *arg++;
+
+ xml::xpath_t::function_t command;
- // Read the command word, canonicalize it to its one letter form,
- // then configure the system based on the kind of report to be
- // generated
-
- string command = *arg++;
-
- if (command == "balance" || command == "bal" || command == "b")
- command = "b";
- else if (command == "register" || command == "reg" || command == "r")
- command = "r";
- else if (command == "print" || command == "p")
- command = "p";
- else if (command == "output")
- command = "w";
- else if (command == "dump")
- command = "W";
- else if (command == "emacs" || command == "lisp")
- command = "x";
- else if (command == "xml")
- command = "X";
- else if (command == "entry")
- command = "e";
- else if (command == "equity")
- command = "E";
- else if (command == "prices")
- command = "P";
- else if (command == "pricesdb")
- command = "D";
- else if (command == "csv")
- command = "c";
- else if (command == "parse") {
- value_expr expr(ledger::parse_value_expr(*arg));
-
- if (config.verbose_mode) {
+#if 0
+ if (verb == "register" || verb == "reg" || verb == "r")
+ command = register_command();
+ else if (verb == "balance" || verb == "bal" || verb == "b")
+ command = balance_command();
+ else if (verb == "print" || verb == "p")
+ command = print_command();
+ else if (verb == "equity")
+ command = equity_command();
+ else if (verb == "entry")
+ command = entry_command();
+ else if (verb == "dump")
+ command = dump_command();
+ else if (verb == "output")
+ command = output_command();
+ else if (verb == "prices")
+ command = prices_command();
+ else if (verb == "pricesdb")
+ command = pricesdb_command();
+ else if (verb == "csv")
+ command = csv_command();
+ else if (verb == "emacs" || verb == "lisp")
+ command = emacs_command();
+ else
+#endif
+ if (verb == "xml")
+ command = bind(xml_command, _1);
+ else if (verb == "expr")
+ ;
+ else if (verb == "xpath")
+ ;
+ else if (verb == "parse") {
+ xml::xpath_t expr(*arg);
+ xml::document_t temp(xml::LEDGER_NODE);
+
+ xml::xpath_t::context_scope_t doc_scope(report, &temp);
+
+ IF_INFO() {
std::cout << "Value expression tree:" << std::endl;
- ledger::dump_value_expr(std::cout, expr.get());
+ expr.dump(std::cout);
std::cout << std::endl;
+
std::cout << "Value expression parsed was:" << std::endl;
- ledger::print_value_expr(std::cout, expr.get());
+ expr.print(std::cout, doc_scope);
std::cout << std::endl << std::endl;
- std::cout << "Result of computation: ";
+
+ expr.compile(doc_scope);
+
+ std::cout << "Value expression after compiling:" << std::endl;
+ expr.dump(std::cout);
+ std::cout << std::endl;
+
+ std::cout << "Value expression is now:" << std::endl;
+ expr.print(std::cout, doc_scope);
+ std::cout << std::endl << std::endl;
+
+ std::cout << "Result of calculation: ";
}
- value_t result = guarded_compute(expr.get());
- std::cout << result.strip_annotations() << std::endl;
+ std::cout << expr.calc(doc_scope).strip_annotations() << std::endl;
return 0;
}
- else if (command == "expr") {
- // this gets done below...
- }
else {
- throw new error(string("Unrecognized command '") + command + "'");
- }
+ char buf[128];
+ std::strcpy(buf, "command_");
+ std::strcat(buf, verb.c_str());
- // Parse initialization files, ledger data, price database, etc.
+ if (xml::xpath_t::ptr_op_t def = report.lookup(buf))
+ command = def->as_function();
- journal.reset(new journal_t);
-
-#if 0
- { TRACE_PUSH(parser, "Parsing journal file");
-#endif
-
- if (parse_ledger_data(config, journal.get()) == 0)
- throw new error("Please specify ledger file using -f"
- " or LEDGER_FILE environment variable.");
-
-#if 0
- TRACE_POP(parser, "Finished parsing"); }
-#endif
+ if (! command)
+ throw_(std::logic_error, string("Unrecognized command '") + verb + "'");
+ }
- // process the command word and its following arguments
+ // Parse the initialization file, which can only be textual; then
+ // parse the journal data.
- string first_arg;
- if (command == "w") {
- if (arg != args.end())
- first_arg = *arg++;
- }
- else if (command == "W") {
- if (report.output_file.empty())
- throw new error("The 'dump' command requires use of the --output option");
- }
+ session.read_init();
-#if 0
- TRACE(options, string("Post-processing options ") +
- "for command \"" + command + "\"");
-#endif
+ INFO_START(journal, "Read journal file");
- report.process_options(command, arg, args.end());
+ xml::document_t xml_document(xml::LEDGER_NODE);
+ journal_t * journal = session.create_journal();
+ xml::journal_builder_t builder(xml_document, journal);
-#if 0
- // jww (2008-05-08): Is this disabled now?
- // If downloading is to be supported, configure the updater
+ if (! session.read_data(builder, journal, report.account))
+ throw_(parse_error, "Failed to locate any journal entries; "
+ "did you specify a valid file with -f?");
- if (! commodity_base_t::updater && config.download_quotes)
- commodity_base_t::updater =
- new quotes_by_script(config.price_db, config.pricing_leeway,
- config.cache_dirty);
-#endif
+ INFO_FINISH(journal);
- std::auto_ptr<entry_t> new_entry;
- if (command == "e") {
- if (arg == args.end()) {
- std::cout << "\
-The entry command requires at least one argument, so Ledger can intelligently\n\
-create a new entry for you. The possible arguments are:\n\
- DATE PAYEE [ACCOUNT] [AMOUNT] [DRAW ACCOUNT]\n\n\
-Some things to note:\n\
- - The ACCOUNT is optional; if no account is given, the last account affected\n\
- by PAYEE is used. If no payee can be found, the generic account 'Expenses'\n\
- is used.\n\
- - The AMOUNT is optional; if not specified, the same amount is used as the\n\
- last time PAYEE was seen, or 0 if not applicable.\n\
- - The AMOUNT does not require a commodity; if none is given, the commodity\n\
- currently contained within ACCOUNT is used, or no commodity at all if\n\
- either: the ACCOUNT was not found, or it contains more than one commodity.\n\
- - Lastly, the DRAW ACCOUNT is optional; if not present, the last account\n\
- drawn from by PAYEE is used, or the 'basket' account (specified with\n\
- 'A ACCOUNT' in your Ledger file) if that does not apply, or the generic\n\
- account 'Equity' is used.\n\n\
-Here are a few examples, all of which may be equivalent depending on your\n\
-Ledger data:\n\
- ledger entry 3/25 chevron\n\
- ledger entry 3/25 chevron 20\n\
- ledger entry 3/25 chevron \\$20\n\
- ledger entry 3/25 chevron gas 20\n\
- ledger entry 3/25 chevron gas \\$20 checking\n\n\
-A final note: Ledger never modifies your data! You are responsible for\n\
-appending the output of this command to your Ledger file if you so choose."
- << std::endl;
- return 1;
- }
- new_entry.reset(derive_new_entry(*journal, arg, args.end()));
- if (! new_entry.get())
- return 1;
- }
+ TRACE_FINISH(entry_text, 1);
+ TRACE_FINISH(entry_date, 1);
+ TRACE_FINISH(entry_details, 1);
+ TRACE_FINISH(entry_xacts, 1);
+ TRACE_FINISH(entries, 1);
+ TRACE_FINISH(parsing_total, 1);
// Configure the output stream
@@ -235,22 +217,20 @@ appending the output of this command to your Ledger file if you so choose."
#endif
std::ostream * out = &std::cout;
- if (! report.output_file.empty()) {
- out = new ofstream(report.output_file);
+ if (report.output_file) {
+ out = new ofstream(*report.output_file);
}
#ifdef HAVE_UNIX_PIPES
- else if (! config.pager.empty()) {
+ else if (report.pager) {
status = pipe(pfd);
if (status == -1)
- throw new error("Failed to create pipe");
+ throw_(std::logic_error, "Failed to create pipe");
status = fork();
if (status < 0) {
- throw new error("Failed to fork child process");
+ throw_(std::logic_error, "Failed to fork child process");
}
else if (status == 0) { // child
- const char *arg0;
-
// Duplicate pipe's reading end into stdin
status = dup2(pfd[0], STDIN_FILENO);
if (status == -1)
@@ -265,13 +245,8 @@ appending the output of this command to your Ledger file if you so choose."
// Find command name: its the substring starting right of the
// rightmost '/' character in the pager pathname. See manpage
// for strrchr.
- arg0 = std::strrchr(config.pager.c_str(), '/');
- if (arg0)
- arg0++;
- else
- arg0 = config.pager.c_str(); // No slashes in pager.
-
- execlp(config.pager.c_str(), arg0, (char *)0);
+ execlp(report.pager->native_file_string().c_str(),
+ basename(*report.pager).c_str(), (char *)0);
perror("execl");
exit(1);
}
@@ -282,236 +257,188 @@ appending the output of this command to your Ledger file if you so choose."
}
#endif
- // Are we handling the parse or expr commands? Do so now.
-
- if (command == "expr") {
- value_expr expr(ledger::parse_value_expr(*arg));
-
- if (config.verbose_mode) {
- std::cout << "Value expression tree:" << std::endl;
- ledger::dump_value_expr(std::cout, expr.get());
- std::cout << std::endl;
- std::cout << "Value expression parsed was:" << std::endl;
- ledger::print_value_expr(std::cout, expr.get());
- std::cout << std::endl << std::endl;
- std::cout << "Result of computation: ";
- }
+ report.define("ostream", value_t(out));
- value_t result = guarded_compute(expr.get());
- std::cout << result.strip_annotations() << std::endl;
+ // Are we handling the expr commands? Do so now.
- return 0;
- }
+ xml::xpath_t::context_scope_t doc_scope(report, &xml_document);
- // Compile the format strings
-
- const string * format;
-
- if (! report.format_string.empty())
- format = &report.format_string;
- else if (command == "b")
- format = &config.balance_format;
- else if (command == "r")
- format = &config.register_format;
- else if (command == "E")
- format = &config.equity_format;
- else if (command == "P")
- format = &config.prices_format;
- else if (command == "D")
- format = &config.pricesdb_format;
- else if (command == "w")
- format = &config.write_xact_format;
- else
- format = &config.print_format;
-
- // Walk the entries based on the report type and the options
-
- item_handler<transaction_t> * formatter;
- std::list<item_handler<transaction_t> *> formatter_ptrs;
-
- if (command == "b" || command == "E")
- formatter = new set_account_value;
- else if (command == "p" || command == "e")
- formatter = new format_entries(*out, *format);
- else if (command == "x")
- formatter = new format_emacs_transactions(*out);
- else if (command == "X")
- formatter = new format_xml_entries(*out, report.show_totals);
- else if (command == "c")
- formatter = new format_csv_transactions(*out);
- else
- formatter = new format_transactions(*out, *format);
+ if (verb == "expr") {
+ xml::xpath_t expr(*arg);
- if (command == "w") {
-#if 0
- TRACE_PUSH(text_writer, "Writing journal file");
-#endif
- write_textual_journal(*journal, first_arg, *formatter,
- config.write_hdr_format, *out);
-#if 0
- TRACE_POP(text_writer, "Finished writing");
-#endif
- }
- else if (command == "W") {
-#if 0
- TRACE_PUSH(binary_writer, "Writing binary file");
-#endif
- ofstream stream(report.output_file);
- binary::write_journal(stream, journal.get());
-#if 0
- TRACE_POP(binary_writer, "Finished writing");
-#endif
- }
- else {
-#if 0
- TRACE_PUSH(main, "Walking journal entries");
-#endif
-
- formatter = report.chain_xact_handlers(command, formatter, journal.get(),
- journal->master, formatter_ptrs);
- if (command == "e")
- walk_transactions(new_entry->transactions, *formatter);
- else if (command == "P" || command == "D")
- walk_commodities(amount_t::current_pool->commodities, *formatter);
- else
- walk_entries(journal->entries, *formatter);
+ IF_INFO() {
+ *out << "Value expression tree:" << std::endl;
+ expr.dump(*out);
+ *out << std::endl;
+ *out << "Value expression parsed was:" << std::endl;
+ expr.print(*out, doc_scope);
+ *out << std::endl << std::endl;
+ *out << "Result of calculation: ";
+ }
- if (command != "P" && command != "D")
- formatter->flush();
+ *out << expr.calc(doc_scope).strip_annotations() << std::endl;
-#if 0
- TRACE_POP(main, "Finished entry walk");
-#endif
+ return 0;
}
+ else if (verb == "xpath") {
+ std::cout << "XPath parsed: ";
+
+ xml::xpath_t xpath(*arg);
+ xpath.print(*out, doc_scope);
+ *out << std::endl;
+
+ IF_INFO() {
+ *out << "Raw results:" << std::endl;
+
+ foreach (const value_t& value, xpath.find_all(doc_scope)) {
+ if (value.is_xml_node())
+ value.as_xml_node()->print(std::cout);
+ else
+ std::cout << value;
+ std::cout << std::endl;
+ }
- // For the balance and equity reports, output the sum totals.
+ *out << "Compiled results:" << std::endl;
+ }
- if (command == "b") {
-#if 0
- TRACE_PUSH(main, "Walking journal accounts");
-#endif
+ xml::compile_node(xml_document, report);
- format_account acct_formatter(*out, *format, report.display_predicate);
- sum_accounts(*journal->master);
- walk_accounts(*journal->master, acct_formatter, report.sort_string);
- acct_formatter.flush();
-
- if (account_has_xdata(*journal->master)) {
- account_xdata_t& xdata = account_xdata(*journal->master);
- if (! report.show_collapsed && xdata.total) {
- *out << "--------------------\n";
- xdata.value = xdata.total;
- acct_formatter.format.format(*out, details_t(*journal->master));
- }
+ foreach (const value_t& value, xpath.find_all(doc_scope)) {
+ if (value.is_xml_node())
+ value.as_xml_node()->print(std::cout);
+ else
+ std::cout << value;
+ std::cout << std::endl;
}
-#if 0
- TRACE_POP(main, "Finished account walk");
-#endif
+ return 0;
}
- else if (command == "E") {
-#if 0
- TRACE_PUSH(main, "Walking journal accounts");
-#endif
- format_equity acct_formatter(*out, *format, report.display_predicate);
- sum_accounts(*journal->master);
- walk_accounts(*journal->master, acct_formatter, report.sort_string);
- acct_formatter.flush();
+ // Apply transforms to the hierarchical document structure
-#if 0
- TRACE_POP(main, "Finished account walk");
-#endif
- }
+ INFO_START(transforms, "Applied transforms");
+ report.apply_transforms(doc_scope);
+ INFO_FINISH(transforms);
-#if DEBUG_LEVEL >= BETA
-#if 0
- { TRACE_PUSH(cleanup, "Cleaning up allocated memory");
-#endif
+ // Create an argument scope containing the report command's
+ // arguments, and then invoke the command.
- clear_transaction_xdata xact_cleaner;
- walk_entries(journal->entries, xact_cleaner);
+ xml::xpath_t::call_scope_t command_args(doc_scope);
- clear_account_xdata acct_cleaner;
- walk_accounts(*journal->master, acct_cleaner);
+ for (strings_list::iterator i = arg; i != args.end(); i++)
+ command_args.push_back(value_t(*i, true));
- if (! report.output_file.empty())
- delete out;
+ INFO_START(command, "Did user command '" << verb << "'");
- for (std::list<item_handler<transaction_t> *>::iterator i
- = formatter_ptrs.begin();
- i != formatter_ptrs.end();
- i++)
- delete *i;
+ command(command_args);
-#if 0
- TRACE_POP(cleanup, "Finished cleaning"); }
-#endif
-#endif
+ INFO_FINISH(command);
// Write out the binary cache, if need be
- if (config.use_cache && config.cache_dirty &&
- ! config.cache_file.empty()) {
-#if 0
- TRACE_PUSH(binary_cache, "Writing journal file");
-#endif
-
- ofstream stream(config.cache_file);
- binary::write_journal(stream, journal.get());
+ if (session.use_cache && session.cache_dirty && session.cache_file) {
+ TRACE_START(binary_cache, 1, "Wrote binary journal file");
#if 0
- TRACE_POP(binary_cache, "Finished writing");
+ ofstream stream(*session.cache_file);
+ write_binary_journal(stream, journal);
#endif
+
+ TRACE_FINISH(binary_cache, 1);
}
+ // If the user specified a pager, wait for it to exit now
+
#ifdef HAVE_UNIX_PIPES
- if (! config.pager.empty()) {
- delete out;
+ if (! report.output_file && report.pager) {
+ checked_delete(out);
close(pfd[1]);
// Wait for child to finish
wait(&status);
if (status & 0xffff != 0)
- throw new error("Something went wrong in the pager");
+ throw_(std::logic_error, "Something went wrong in the pager");
}
#endif
+ else if (DO_VERIFY() && report.output_file) {
+ checked_delete(out);
+ }
return 0;
}
int main(int argc, char * argv[], char * envp[])
{
- // This variable must be defined here so that any memory it holds is still
- // available should the subsequent exception handlers catch an inner
- // exception and need to report something on the invalid state of the
- // journal (such as an unbalanced entry).
- std::auto_ptr<journal_t> journal;
+ int status = 1;
- try {
-#if DEBUG_LEVEL < BETA
- ledger::do_cleanup = false;
+ for (int i = 1; i < argc; i++)
+ if (argv[i][0] == '-') {
+ if (std::strcmp(argv[i], "--verify") == 0) {
+#if defined(VERIFY_ON)
+ ledger::verify_enabled = true;
+#endif
+ }
+ else if (std::strcmp(argv[i], "--verbose") == 0 ||
+ std::strcmp(argv[i], "-v") == 0) {
+#if defined(LOGGING_ON)
+ ledger::_log_level = ledger::LOG_INFO;
+#endif
+ }
+ else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) {
+#if defined(DEBUG_ON)
+ ledger::_log_level = ledger::LOG_DEBUG;
+ ledger::_log_category = argv[i + 1];
+ i++;
+#endif
+ }
+ else if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
+#if defined(TRACING_ON)
+ ledger::_log_level = ledger::LOG_TRACE;
+ ledger::_trace_level = boost::lexical_cast<int>(argv[i + 1]);
+ i++;
#endif
- config_t config;
- report_t report;
- ledger::config = &config;
- ledger::report = &report;
+ }
+ }
- amount_t::initialize();
- value_t::initialize();
+ IF_VERIFY()
+ ledger::initialize_memory_tracing();
-#if 0
- TRACE_PUSH(main, "Ledger starting");
-#endif
- int status = parse_and_report(config, journal, report, argc, argv, envp);
+ try {
+ std::ios::sync_with_stdio(false);
+
+ boost::filesystem::path::default_name_check
+ (boost::filesystem::portable_posix_name);
+
+ INFO("Ledger starting");
- value_t::shutdown();
- amount_t::shutdown();
+ std::auto_ptr<ledger::session_t> session(new ledger::session_t);
+
+ ledger::set_session_context(session.get());
#if 0
- TRACE_POP(main, "Ledger done");
+ session->register_parser(new binary_parser_t);
+#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+ session->register_parser(new xml::xml_parser_t);
+ session->register_parser(new gnucash_parser_t);
+#endif
+#ifdef HAVE_LIBOFX
+ session->register_parser(new ofx_parser_t);
#endif
- return status;
+ session->register_parser(new qif_parser_t);
+#endif
+ session->register_parser(new ledger::textual_parser_t);
+
+ std::auto_ptr<ledger::report_t> report(new ledger::report_t(*session.get()));
+
+ status = read_and_report(*report.get(), argc, argv, envp);
+
+ if (DO_VERIFY()) {
+ ledger::set_session_context();
+ } else {
+ report.release();
+ session.release();
+ }
}
+#if 0
catch (error * err) {
std::cout.flush();
// Push a null here since there's no file context
@@ -520,8 +447,7 @@ int main(int argc, char * argv[], char * envp[])
err->context.push_front(new error_context(""));
err->reveal_context(std::cerr, "Error");
std::cerr << err->what() << std::endl;
- delete err;
- return 1;
+ checked_delete(err);
}
catch (fatal * err) {
std::cout.flush();
@@ -531,17 +457,25 @@ int main(int argc, char * argv[], char * envp[])
err->context.push_front(new error_context(""));
err->reveal_context(std::cerr, "Fatal");
std::cerr << err->what() << std::endl;
- delete err;
- return 1;
+ checked_delete(err);
}
+#endif
catch (const std::exception& err) {
std::cout.flush();
std::cerr << "Error: " << err.what() << std::endl;
- return 1;
}
- catch (int status) {
- return status;
+ catch (int _status) {
+ status = _status;
}
+
+ IF_VERIFY() {
+ INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
+ ledger::shutdown_memory_tracing();
+ } else {
+ INFO("Ledger ended");
+ }
+
+ return status;
}
// main.cc ends here.
diff --git a/option.cc b/option.cc
index bea96310..fc5c30ab 100644
--- a/option.cc
+++ b/option.cc
@@ -1,71 +1,156 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#include "option.h"
-#include "config.h"
-#include "report.h"
-#include "utils.h"
namespace ledger {
namespace {
- inline void process_option(option_t * opt, const char * arg = NULL) {
- if (! opt->handled) {
- try {
- opt->handler(arg);
- }
- catch (error * err) {
- err->context.push_back
- (new error_context
- (string("While parsing option '--") + opt->long_opt +
- "'" + (opt->short_opt != '\0' ?
- (string(" (-") + opt->short_opt + "):") : ":")));
- throw err;
- }
- opt->handled = true;
+ typedef tuple<expr::ptr_op_t, bool> op_bool_tuple;
+
+ op_bool_tuple find_option(expr::scope_t& scope, const string& name)
+ {
+ char buf[128];
+ std::strcpy(buf, "option_");
+ char * p = &buf[7];
+ for (const char * q = name.c_str(); *q; q++) {
+ if (*q == '-')
+ *p++ = '_';
+ else
+ *p++ = *q;
}
+ *p = '\0';
+
+ expr::ptr_op_t op = scope.lookup(buf);
+ if (op)
+ return op_bool_tuple(op, false);
+
+ *p++ = '_';
+ *p = '\0';
+
+ return op_bool_tuple(scope.lookup(buf), true);
}
- option_t * search_options(option_t * array, const char * name)
+ op_bool_tuple find_option(expr::scope_t& scope, const char letter)
{
- int first = 0;
- int last = CONFIG_OPTIONS_SIZE;
- while (first <= last) {
- int mid = (first + last) / 2; // compute mid point.
+ char buf[10];
+ std::strcpy(buf, "option_");
+ buf[7] = letter;
+ buf[8] = '\0';
- int result;
- if ((result = (int)name[0] - (int)array[mid].long_opt[0]) == 0)
- result = std::strcmp(name, array[mid].long_opt);
+ expr::ptr_op_t op = scope.lookup(buf);
+ if (op)
+ return op_bool_tuple(op, false);
- if (result > 0)
- first = mid + 1; // repeat search in top half.
- else if (result < 0)
- last = mid - 1; // repeat search in bottom half.
- else
- return &array[mid];
- }
- return NULL;
+ buf[8] = '_';
+ buf[9] = '\0';
+
+ return op_bool_tuple(scope.lookup(buf), true);
}
- option_t * search_options(option_t * array, const char letter)
+ void process_option(const expr::function_t& opt,
+ expr::scope_t& scope, const char * arg)
{
- for (int i = 0; i < CONFIG_OPTIONS_SIZE; i++)
- if (letter == array[i].short_opt)
- return &array[i];
- return NULL;
+#if 0
+ try {
+#endif
+ expr::call_scope_t args(scope);
+ if (arg)
+ args.push_back(value_t(arg, true));
+
+ opt(args);
+#if 0
+ }
+ catch (error * err) {
+ err->context.push_back
+ (new error_context
+ (string("While parsing option '--") + opt->long_opt +
+ "'" + (opt->short_opt != '\0' ?
+ (string(" (-") + opt->short_opt + "):") : ":")));
+ throw err;
+ }
+#endif
}
}
-bool process_option(option_t * options, const string& name,
+void process_option(const string& name, expr::scope_t& scope,
const char * arg)
{
- option_t * opt = search_options(options, name.c_str());
- if (opt) {
- process_option(opt, arg);
- return true;
- }
- return false;
+ op_bool_tuple opt(find_option(scope, name));
+ if (opt.get<0>())
+ process_option(opt.get<0>()->as_function(), scope, arg);
}
-void process_arguments(option_t * options, int argc, char ** argv,
- const bool anywhere, std::list<string>& args)
+void process_environment(const char ** envp, const string& tag,
+ expr::scope_t& scope)
+{
+ const char * tag_p = tag.c_str();
+ unsigned int tag_len = tag.length();
+
+ for (const char ** p = envp; *p; p++)
+ if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) {
+ char buf[128];
+ char * r = buf;
+ const char * q;
+ for (q = *p + tag_len;
+ *q && *q != '=' && r - buf < 128;
+ q++)
+ if (*q == '_')
+ *r++ = '-';
+ else
+ *r++ = std::tolower(*q);
+ *r = '\0';
+
+ if (*q == '=') {
+#if 0
+ try {
+#endif
+ process_option(string(buf), scope, q + 1);
+#if 0
+ }
+ catch (error * err) {
+ err->context.push_back
+ (new error_context
+ (string("While parsing environment variable option '") +
+ *p + "':"));
+ throw err;
+ }
+#endif
+ }
+ }
+}
+
+void process_arguments(int argc, char ** argv, const bool anywhere,
+ expr::scope_t& scope,
+ std::list<string>& args)
{
for (char ** i = argv; *i; i++) {
if ((*i)[0] != '-') {
@@ -91,961 +176,47 @@ void process_arguments(option_t * options, int argc, char ** argv,
value = p;
}
- option_t * opt = search_options(options, name);
- if (! opt)
- throw new option_error(string("illegal option --") + name);
+ op_bool_tuple opt(find_option(scope, name));
+ if (! opt.get<0>())
+ throw_(option_error, "illegal option --" << name);
- if (opt->wants_arg && value == NULL) {
+ if (opt.get<1>() && value == NULL) {
value = *++i;
if (value == NULL)
- throw new option_error(string("missing option argument for --") +
- name);
+ throw_(option_error, "missing option argument for --" << name);
}
- process_option(opt, value);
+ process_option(opt.get<0>()->as_function(), scope, value);
}
else if ((*i)[1] == '\0') {
- throw new option_error(string("illegal option -"));
+ throw_(option_error, "illegal option -");
}
else {
- std::list<option_t *> opt_queue;
+ typedef tuple<expr::ptr_op_t, bool, char> op_bool_char_tuple;
+
+ std::list<op_bool_char_tuple> option_queue;
int x = 1;
for (char c = (*i)[x]; c != '\0'; x++, c = (*i)[x]) {
- option_t * opt = search_options(options, c);
- if (! opt)
- throw new option_error(string("illegal option -") + c);
- opt_queue.push_back(opt);
+ op_bool_tuple opt(find_option(scope, c));
+ if (! opt.get<0>())
+ throw_(option_error, "illegal option -" << c);
+
+ option_queue.push_back
+ (op_bool_char_tuple(opt.get<0>(), opt.get<1>(), c));
}
- for (std::list<option_t *>::iterator o = opt_queue.begin();
- o != opt_queue.end(); o++) {
+ foreach (op_bool_char_tuple& o, option_queue) {
char * value = NULL;
- if ((*o)->wants_arg) {
+ if (o.get<1>()) {
value = *++i;
if (value == NULL)
- throw new option_error(string("missing option argument for -") +
- (*o)->short_opt);
+ throw_(option_error,
+ "missing option argument for -" << o.get<2>());
}
- process_option(*o, value);
+ process_option(o.get<0>()->as_function(), scope, value);
}
}
}
}
-void process_environment(option_t * options, const char ** envp,
- const string& tag)
-{
- const char * tag_p = tag.c_str();
- unsigned int tag_len = tag.length();
-
- for (const char ** p = envp; *p; p++)
- if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) {
- char buf[128];
- char * r = buf;
- const char * q;
- for (q = *p + tag_len;
- *q && *q != '=' && r - buf < 128;
- q++)
- if (*q == '_')
- *r++ = '-';
- else
- *r++ = std::tolower(*q);
- *r = '\0';
-
- if (*q == '=') {
- try {
- process_option(options, buf, q + 1);
- }
- catch (error * err) {
- err->context.pop_back();
- err->context.push_back
- (new error_context
- (string("While parsing environment variable option '") +
- *p + "':"));
- throw err;
- }
- }
- }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-config_t * config = NULL;
-report_t * report = NULL;
-
-static void show_version(std::ostream& out)
-{
- out << "Ledger " << ledger::version << ", the command-line accounting tool";
- out << "\n\nCopyright (c) 2003-2006, John Wiegley. All rights reserved.\n\n\
-This program is made available under the terms of the BSD Public License.\n\
-See LICENSE file included with the distribution for details and disclaimer.\n";
- out << "\n(modules: gmp, pcre";
-#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
- out << ", xml";
-#endif
-#ifdef HAVE_LIBOFX
- out << ", ofx";
-#endif
- out << ")\n";
-}
-
-void option_full_help(std::ostream& out)
-{
- out << "usage: ledger [options] COMMAND [ACCT REGEX]... [-- [PAYEE REGEX]...]\n\n\
-Basic options:\n\
- -H, --full-help display this help text\n\
- -h, --help display summarized help text\n\
- -v, --version show version information\n\
- -f, --file FILE read ledger data from FILE\n\
- -o, --output FILE write output to FILE\n\
- -i, --init-file FILE initialize ledger using FILE (default: ~/.ledgerrc)\n\
- --cache FILE use FILE as a binary cache when --file is not used\n\
- --no-cache don't use a cache, even if it would be appropriate\n\
- -a, --account NAME use NAME for the default account (useful with QIF)\n\n\
-Report filtering:\n\
- -c, --current show only current and past entries (not future)\n\
- -b, --begin DATE set report begin date\n\
- -e, --end DATE set report end date\n\
- -p, --period STR report using the given period\n\
- --period-sort EXPR sort each report period's entries by EXPR\n\
- -C, --cleared consider only cleared transactions\n\
- -U, --uncleared consider only uncleared transactions\n\
- -R, --real consider only real (non-virtual) transactions\n\
- -L, --actual consider only actual (non-automated) transactions\n\
- -r, --related calculate report using related transactions\n\
- --budget generate budget entries based on periodic entries\n\
- --add-budget show all transactions plus the budget\n\
- --unbudgeted show only unbudgeted transactions\n\
- --forecast EXPR generate forecast entries while EXPR is true\n\
- -l, --limit EXPR calculate only transactions matching EXPR\n\
- -t, --amount EXPR use EXPR to calculate the displayed amount\n\
- -T, --total EXPR use EXPR to calculate the displayed total\n\n\
-Output customization:\n\
- -n, --collapse register: collapse entries; balance: no grand total\n\
- -s, --subtotal balance: show sub-accounts; other: show subtotals\n\
- -P, --by-payee show summarized totals by payee\n\
- -x, --comm-as-payee set commodity name as the payee, for reporting\n\
- -E, --empty balance: show accounts with zero balance\n\
- -W, --weekly show weekly sub-totals\n\
- -M, --monthly show monthly sub-totals\n\
- -Y, --yearly show yearly sub-totals\n\
- --dow show a days-of-the-week report\n\
- -S, --sort EXPR sort report according to the value expression EXPR\n\
- -w, --wide for the default register report, use 132 columns\n\
- --head COUNT show only the first COUNT entries (negative inverts)\n\
- --tail COUNT show only the last COUNT entries (negative inverts)\n\
- --pager PAGER send all output through the given PAGER program\n\
- -A, --average report average transaction amount\n\
- -D, --deviation report deviation from the average\n\
- -%, --percentage report balance totals as a percentile of the parent\n\
- --totals in the \"xml\" report, include running total\n\
- -j, --amount-data print only raw amount data (useful for scripting)\n\
- -J, --total-data print only raw total data\n\
- -d, --display EXPR display only transactions matching EXPR\n\
- -y, --date-format STR use STR as the date format (default: %Y/%m/%d)\n\
- -F, --format STR use STR as the format; for each report type, use:\n\
- --balance-format --register-format --print-format\n\
- --plot-amount-format --plot-total-format --equity-format\n\
- --prices-format --wide-register-format\n\n\
-Commodity reporting:\n\
- --price-db FILE sets the price database to FILE (def: ~/.pricedb)\n\
- -L, --price-exp MINS download quotes only if newer than MINS (def: 1440)\n\
- -Q, --download download price information when needed\n\
- -O, --quantity report commodity totals (this is the default)\n\
- -B, --basis report cost basis of commodities\n\
- -V, --market report last known market value\n\
- -g, --performance report gain/loss for each displayed transaction\n\
- -G, --gain report net gain/loss\n\n\
-Commands:\n\
- balance [REGEXP]... show balance totals for matching accounts\n\
- register [REGEXP]... show register of matching transactions\n\
- print [REGEXP]... print all matching entries\n\
- xml [REGEXP]... print matching entries in XML format\n\
- equity [REGEXP]... output equity entries for matching accounts\n\
- prices [REGEXP]... display price history for matching commodities\n\
- entry DATE PAYEE AMT output a derived entry, based on the arguments\n";
-}
-
-void option_help(std::ostream& out)
-{
- out << "usage: ledger [options] COMMAND [ACCT REGEX]... [-- [PAYEE REGEX]...]\n\n\
-Use -H to see all the help text on one page, or:\n\
- --help-calc calculation options\n\
- --help-disp display options\n\
- --help-comm commodity options\n\n\
-Basic options:\n\
- -h, --help display this help text\n\
- -v, --version show version information\n\
- -f, --file FILE read ledger data from FILE\n\
- -o, --output FILE write output to FILE\n\
- -i, --init-file FILE initialize ledger using FILE (default: ~/.ledgerrc)\n\
- --cache FILE use FILE as a binary cache when --file is not used\n\
- --no-cache don't use a cache, even if it would be appropriate\n\
- -a, --account NAME use NAME for the default account (useful with QIF)\n\n\
-Commands:\n\
- balance [REGEXP]... show balance totals for matching accounts\n\
- register [REGEXP]... show register of matching transactions\n\
- print [REGEXP]... print all matching entries\n\
- xml [REGEXP]... print matching entries in XML format\n\
- equity [REGEXP]... output equity entries for matching accounts\n\
- prices [REGEXP]... display price history for matching commodities\n\
- entry DATE PAYEE AMT output a derived entry, based on the arguments\n";
-}
-
-void option_calc_help(std::ostream& out)
-{
- out << "Options to control how a report is calculated:\n\
- -c, --current show only current and past entries (not future)\n\
- -b, --begin DATE set report begin date\n\
- -e, --end DATE set report end date\n\
- -p, --period STR report using the given period\n\
- --period-sort EXPR sort each report period's entries by EXPR\n\
- -C, --cleared consider only cleared transactions\n\
- -U, --uncleared consider only uncleared transactions\n\
- -R, --real consider only real (non-virtual) transactions\n\
- -L, --actual consider only actual (non-automated) transactions\n\
- -r, --related calculate report using related transactions\n\
- --budget generate budget entries based on periodic entries\n\
- --add-budget show all transactions plus the budget\n\
- --unbudgeted show only unbudgeted transactions\n\
- --forecast EXPR generate forecast entries while EXPR is true\n\
- -l, --limit EXPR calculate only transactions matching EXPR\n\
- -t, --amount EXPR use EXPR to calculate the displayed amount\n\
- -T, --total EXPR use EXPR to calculate the displayed total\n";
-}
-
-void option_disp_help(std::ostream& out)
-{
- out << "Output to control how report results are displayed:\n\
- -n, --collapse register: collapse entries; balance: no grand total\n\
- -s, --subtotal balance: show sub-accounts; other: show subtotals\n\
- -P, --by-payee show summarized totals by payee\n\
- -x, --comm-as-payee set commodity name as the payee, for reporting\n\
- -E, --empty balance: show accounts with zero balance\n\
- -W, --weekly show weekly sub-totals\n\
- -M, --monthly show monthly sub-totals\n\
- -Y, --yearly show yearly sub-totals\n\
- --dow show a days-of-the-week report\n\
- -S, --sort EXPR sort report according to the value expression EXPR\n\
- -w, --wide for the default register report, use 132 columns\n\
- --head COUNT show only the first COUNT entries (negative inverts)\n\
- --tail COUNT show only the last COUNT entries (negative inverts)\n\
- --pager PAGER send all output through the given PAGER program\n\
- -A, --average report average transaction amount\n\
- -D, --deviation report deviation from the average\n\
- -%, --percentage report balance totals as a percentile of the parent\n\
- --totals in the \"xml\" report, include running total\n\
- -j, --amount-data print only raw amount data (useful for scripting)\n\
- -J, --total-data print only raw total data\n\
- -d, --display EXPR display only transactions matching EXPR\n\
- -y, --date-format STR use STR as the date format (default: %Y/%m/%d)\n\
- -F, --format STR use STR as the format; for each report type, use:\n\
- --balance-format --register-format --print-format\n\
- --plot-amount-format --plot-total-format --equity-format\n\
- --prices-format --wide-register-format\n";
-}
-
-void option_comm_help(std::ostream& out)
-{
- out << "Options to control how commodity values are determined:\n\
- --price-db FILE sets the price database to FILE (def: ~/.pricedb)\n\
- -Z, --price-exp MINS download quotes only if newer than MINS (def: 1440)\n\
- -Q, --download download price information when needed\n\
- -O, --quantity report commodity totals (this is the default)\n\
- -B, --basis report cost basis of commodities\n\
- -V, --market report last known market value\n\
- -g, --performance report gain/loss for each displayed transaction\n\
- -G, --gain report net gain/loss\n";
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// Basic options
-
-OPT_BEGIN(full_help, "H") {
- option_full_help(std::cout);
- throw 0;
-} OPT_END(full_help);
-
-OPT_BEGIN(help, "h") {
- option_help(std::cout);
- throw 0;
-} OPT_END(help);
-
-OPT_BEGIN(help_calc, "") {
- option_calc_help(std::cout);
- throw 0;
-} OPT_END(help_calc);
-
-OPT_BEGIN(help_disp, "") {
- option_disp_help(std::cout);
- throw 0;
-} OPT_END(help_disp);
-
-OPT_BEGIN(help_comm, "") {
- option_comm_help(std::cout);
- throw 0;
-} OPT_END(help_comm);
-
-OPT_BEGIN(version, "v") {
- show_version(std::cout);
- throw 0;
-} OPT_END(version);
-
-OPT_BEGIN(init_file, "i:") {
- path pathname = resolve_path(optarg);
- if (boost::filesystem::exists(pathname))
- config->init_file = pathname;
- else
- throw new error(string("The init file '") + string(pathname.string()) +
- "' does not exist or is not readable");
-} OPT_END(init_file);
-
-OPT_BEGIN(file, "f:") {
- if (string(optarg) == "-") {
- config->data_file = optarg;
- } else {
- path pathname = resolve_path(optarg);
- if (boost::filesystem::exists(pathname))
- config->data_file = pathname;
- else
- throw new error(string("The ledger file '") + string(pathname.string()) +
- "' does not exist or is not readable");
- }
-} OPT_END(file);
-
-OPT_BEGIN(cache, ":") {
- config->cache_file = resolve_path(optarg);
-} OPT_END(cache);
-
-OPT_BEGIN(no_cache, "") {
- config->cache_file = "<none>";
-} OPT_END(no_cache);
-
-OPT_BEGIN(output, "o:") {
- if (string(optarg) != "-")
- report->output_file = resolve_path(optarg);
-} OPT_END(output);
-
-OPT_BEGIN(account, "a:") {
- config->account = optarg;
-} OPT_END(account);
-
-OPT_BEGIN(debug, ":") {
- config->debug_mode = true;
- ::setenv("DEBUG_CLASS", optarg, 1);
-} OPT_END(debug);
-
-OPT_BEGIN(verbose, "") {
- config->verbose_mode = true;
-} OPT_END(verbose);
-
-OPT_BEGIN(trace, "") {
- config->trace_mode = true;
-} OPT_END(trace);
-
-//////////////////////////////////////////////////////////////////////
-//
-// Report filtering
-
-OPT_BEGIN(effective, "") {
- transaction_t::use_effective_date = true;
-} OPT_END(effective);
-
-OPT_BEGIN(begin, "b:") {
- interval_t interval(optarg);
- if (! is_valid(interval.begin))
- throw new error(string("Could not determine beginning of period '") +
- optarg + "'");
-
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "d>=[";
- report->predicate += format_datetime(interval.begin);
- report->predicate += "]";
-} OPT_END(begin);
-
-OPT_BEGIN(end, "e:") {
- interval_t interval(optarg);
- if (! is_valid(interval.begin))
- throw new error(string("Could not determine end of period '") + optarg + "'");
-
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "d<[";
- report->predicate += format_datetime(interval.begin);
- report->predicate += "]";
-
- terminus = interval.begin;
-} OPT_END(end);
-
-OPT_BEGIN(current, "c") {
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "d<=m";
-} OPT_END(current);
-
-OPT_BEGIN(cleared, "C") {
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "X";
-} OPT_END(cleared);
-
-OPT_BEGIN(uncleared, "U") {
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "!X";
-} OPT_END(uncleared);
-
-OPT_BEGIN(real, "R") {
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "R";
-} OPT_END(real);
-
-OPT_BEGIN(actual, "L") {
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "L";
-} OPT_END(actual);
-
-OPT_BEGIN(lots, "") {
- report->keep_price =
- report->keep_date =
- report->keep_tag = true;
-} OPT_END(lots);
-
-OPT_BEGIN(lot_prices, "") {
- report->keep_price = true;
-} OPT_END(lots_prices);
-
-OPT_BEGIN(lot_dates, "") {
- report->keep_date = true;
-} OPT_END(lots_dates);
-
-OPT_BEGIN(lot_tags, "") {
- report->keep_tag = true;
-} OPT_END(lots_tags);
-
-//////////////////////////////////////////////////////////////////////
-//
-// Output customization
-
-OPT_BEGIN(format, "F:") {
- report->format_string = optarg;
-} OPT_END(format);
-
-OPT_BEGIN(date_format, "y:") {
- report->date_output_format = optarg;
-} OPT_END(date_format);
-
-OPT_BEGIN(input_date_format, ":") {
- config->date_input_format = optarg;
-} OPT_END(input_date_format);
-
-OPT_BEGIN(balance_format, ":") {
- config->balance_format = optarg;
-} OPT_END(balance_format);
-
-OPT_BEGIN(register_format, ":") {
- config->register_format = optarg;
-} OPT_END(register_format);
-
-OPT_BEGIN(wide_register_format, ":") {
- config->wide_register_format = optarg;
-} OPT_END(wide_register_format);
-
-OPT_BEGIN(plot_amount_format, ":") {
- config->plot_amount_format = optarg;
-} OPT_END(plot_amount_format);
-
-OPT_BEGIN(plot_total_format, ":") {
- config->plot_total_format = optarg;
-} OPT_END(plot_total_format);
-
-OPT_BEGIN(print_format, ":") {
- config->print_format = optarg;
-} OPT_END(print_format);
-
-OPT_BEGIN(write_hdr_format, ":") {
- config->write_hdr_format = optarg;
-} OPT_END(write_hdr_format);
-
-OPT_BEGIN(write_xact_format, ":") {
- config->write_xact_format = optarg;
-} OPT_END(write_xact_format);
-
-OPT_BEGIN(equity_format, ":") {
- config->equity_format = optarg;
-} OPT_END(equity_format);
-
-OPT_BEGIN(prices_format, ":") {
- config->prices_format = optarg;
-} OPT_END(prices_format);
-
-OPT_BEGIN(wide, "w") {
- config->register_format = config->wide_register_format;
-} OPT_END(wide);
-
-OPT_BEGIN(head, ":") {
- report->head_entries = std::atoi(optarg);
-} OPT_END(head);
-
-OPT_BEGIN(tail, ":") {
- report->tail_entries = std::atoi(optarg);
-} OPT_END(tail);
-
-OPT_BEGIN(pager, ":") {
- config->pager = optarg;
-} OPT_END(pager);
-
-OPT_BEGIN(truncate, ":") {
- string style(optarg);
- if (style == "leading")
- format_t::elision_style = format_t::TRUNCATE_LEADING;
- else if (style == "middle")
- format_t::elision_style = format_t::TRUNCATE_MIDDLE;
- else if (style == "trailing")
- format_t::elision_style = format_t::TRUNCATE_TRAILING;
- else if (style == "abbrev")
- format_t::elision_style = format_t::ABBREVIATE;
-} OPT_END(truncate);
-
-OPT_BEGIN(abbrev_len, ":") {
- format_t::abbrev_length = std::atoi(optarg);
-} OPT_END(abbrev_len);
-
-OPT_BEGIN(empty, "E") {
- report->show_empty = true;
-} OPT_END(empty);
-
-OPT_BEGIN(collapse, "n") {
- report->show_collapsed = true;
-} OPT_END(collapse);
-
-OPT_BEGIN(subtotal, "s") {
- report->show_subtotal = true;
-} OPT_END(subtotal);
-
-OPT_BEGIN(totals, "") {
- report->show_totals = true;
-} OPT_END(totals);
-
-OPT_BEGIN(sort, "S:") {
- report->sort_string = optarg;
-} OPT_END(sort);
-
-OPT_BEGIN(sort_entries, "") {
- report->sort_string = optarg;
- report->entry_sort = true;
-} OPT_END(sort_entries);
-
-OPT_BEGIN(sort_all, "") {
- report->sort_string = optarg;
- report->entry_sort = false;
- report->sort_all = true;
-} OPT_END(sort_all);
-
-OPT_BEGIN(period_sort, ":") {
- report->sort_string = optarg;
- report->entry_sort = true;
-} OPT_END(period_sort);
-
-OPT_BEGIN(related, "r") {
- report->show_related = true;
-} OPT_END(related);
-
-OPT_BEGIN(descend, "") {
- string arg(optarg);
- string::size_type beg = 0;
- report->descend_expr = "";
- for (string::size_type pos = arg.find(';');
- pos != string::npos;
- beg = pos + 1, pos = arg.find(';', beg))
- report->descend_expr += (string("t=={") +
- string(arg, beg, pos - beg) + "};");
- report->descend_expr += (string("t=={") +
- string(arg, beg) + "}");
-} OPT_END(descend);
-
-OPT_BEGIN(descend_if, "") {
- report->descend_expr = optarg;
-} OPT_END(descend_if);
-
-OPT_BEGIN(period, "p:") {
- if (report->report_period.empty()) {
- report->report_period = optarg;
- } else {
- report->report_period += " ";
- report->report_period += optarg;
- }
-
- // If the period gives a beginning and/or ending date, make sure to
- // modify the calculation predicate (via the --begin and --end
- // options) to take this into account.
-
- interval_t interval(report->report_period);
-
- if (is_valid(interval.begin)) {
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "d>=[";
- report->predicate += format_datetime(interval.begin);
- report->predicate += "]";
- }
-
- if (is_valid(interval.end)) {
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "d<[";
- report->predicate += format_datetime(interval.end);
- report->predicate += "]";
-
- terminus = interval.end;
- }
-} OPT_END(period);
-
-OPT_BEGIN(daily, "") {
- if (report->report_period.empty())
- report->report_period = "daily";
- else
- report->report_period = string("daily ") + report->report_period;
-} OPT_END(daily);
-
-OPT_BEGIN(weekly, "W") {
- if (report->report_period.empty())
- report->report_period = "weekly";
- else
- report->report_period = string("weekly ") + report->report_period;
-} OPT_END(weekly);
-
-OPT_BEGIN(monthly, "M") {
- if (report->report_period.empty())
- report->report_period = "monthly";
- else
- report->report_period = string("monthly ") + report->report_period;
-} OPT_END(monthly);
-
-OPT_BEGIN(quarterly, "") {
- if (report->report_period.empty())
- report->report_period = "quarterly";
- else
- report->report_period = string("quarterly ") + report->report_period;
-} OPT_END(quarterly);
-
-OPT_BEGIN(yearly, "Y") {
- if (report->report_period.empty())
- report->report_period = "yearly";
- else
- report->report_period = string("yearly ") + report->report_period;
-} OPT_END(yearly);
-
-OPT_BEGIN(dow, "") {
- report->days_of_the_week = true;
-} OPT_END(dow);
-
-OPT_BEGIN(by_payee, "P") {
- report->by_payee = true;
-} OPT_END(by_payee);
-
-OPT_BEGIN(comm_as_payee, "x") {
- report->comm_as_payee = true;
-} OPT_END(comm_as_payee);
-
-OPT_BEGIN(code_as_payee, "") {
- report->code_as_payee = true;
-} OPT_END(code_as_payee);
-
-OPT_BEGIN(budget, "") {
- report->budget_flags = BUDGET_BUDGETED;
-} OPT_END(budget);
-
-OPT_BEGIN(add_budget, "") {
- report->budget_flags = BUDGET_BUDGETED | BUDGET_UNBUDGETED;
-} OPT_END(add_budget);
-
-OPT_BEGIN(unbudgeted, "") {
- report->budget_flags = BUDGET_UNBUDGETED;
-} OPT_END(unbudgeted);
-
-OPT_BEGIN(forecast, ":") {
- report->forecast_limit = optarg;
-} OPT_END(forecast);
-
-OPT_BEGIN(reconcile, ":") {
- report->reconcile_balance = optarg;
-} OPT_END(reconcile);
-
-OPT_BEGIN(reconcile_date, ":") {
- report->reconcile_date = optarg;
-} OPT_END(reconcile_date);
-
-OPT_BEGIN(limit, "l:") {
- if (! report->predicate.empty())
- report->predicate += "&";
- report->predicate += "(";
- report->predicate += optarg;
- report->predicate += ")";
-} OPT_END(limit);
-
-OPT_BEGIN(only, ":") {
- if (! report->secondary_predicate.empty())
- report->secondary_predicate += "&";
- report->secondary_predicate += "(";
- report->secondary_predicate += optarg;
- report->secondary_predicate += ")";
-} OPT_END(only);
-
-OPT_BEGIN(display, "d:") {
- if (! report->display_predicate.empty())
- report->display_predicate += "&";
- report->display_predicate += "(";
- report->display_predicate += optarg;
- report->display_predicate += ")";
-} OPT_END(display);
-
-OPT_BEGIN(amount, "t:") {
- ledger::amount_expr = optarg;
-} OPT_END(amount);
-
-OPT_BEGIN(total, "T:") {
- ledger::total_expr = optarg;
-} OPT_END(total);
-
-OPT_BEGIN(amount_data, "j") {
- report->format_string = config->plot_amount_format;
-} OPT_END(amount_data);
-
-OPT_BEGIN(total_data, "J") {
- report->format_string = config->plot_total_format;
-} OPT_END(total_data);
-
-OPT_BEGIN(ansi, "") {
- format_t::ansi_codes = true;
- format_t::ansi_invert = false;
-} OPT_END(ansi);
-
-OPT_BEGIN(ansi_invert, "") {
- format_t::ansi_codes =
- format_t::ansi_invert = true;
-} OPT_END(ansi);
-
-//////////////////////////////////////////////////////////////////////
-//
-// Commodity reporting
-
-OPT_BEGIN(base, ":") {
- amount_t::keep_base = true;
-} OPT_END(base);
-
-OPT_BEGIN(price_db, ":") {
- config->price_db = optarg;
-} OPT_END(price_db);
-
-OPT_BEGIN(price_exp, "Z:") {
- config->pricing_leeway = std::atol(optarg) * 60;
-} OPT_END(price_exp);
-
-OPT_BEGIN(download, "Q") {
- config->download_quotes = true;
-} OPT_END(download);
-
-OPT_BEGIN(quantity, "O") {
- ledger::amount_expr = "@a";
- ledger::total_expr = "@O";
-} OPT_END(quantity);
-
-OPT_BEGIN(basis, "B") {
- ledger::amount_expr = "@b";
- ledger::total_expr = "@B";
-} OPT_END(basis);
-
-OPT_BEGIN(price, "I") {
- ledger::amount_expr = "@i";
- ledger::total_expr = "@I";
-} OPT_END(price);
-
-OPT_BEGIN(market, "V") {
- report->show_revalued = true;
-
- ledger::amount_expr = "@v";
- ledger::total_expr = "@V";
-} OPT_END(market);
-
-namespace {
- void parse_price_setting(const char * optarg)
- {
- char * equals = std::strchr(optarg, '=');
- if (! equals)
- return;
-
- while (std::isspace(*optarg))
- optarg++;
- while (equals > optarg && std::isspace(*(equals - 1)))
- equals--;
-
- string symbol(optarg, 0, equals - optarg);
- amount_t price(equals + 1);
-
- if (commodity_t * commodity =
- amount_t::current_pool->find_or_create(symbol)) {
- commodity->add_price(current_moment, price);
-#if 0
- // jww (2008-04-20): what was this?
- commodity->history()->bogus_time = current_moment;
-#endif
- }
- }
-}
-
-OPT_BEGIN(set_price, ":") {
- string arg(optarg);
- string::size_type beg = 0;
- for (string::size_type pos = arg.find(';');
- pos != string::npos;
- beg = pos + 1, pos = arg.find(';', beg))
- parse_price_setting(string(arg, beg, pos - beg).c_str());
- parse_price_setting(string(arg, beg).c_str());
-} OPT_END(set_price);
-
-OPT_BEGIN(performance, "g") {
- ledger::amount_expr = "@P(@a,@m)-@b";
- ledger::total_expr = "@P(@O,@m)-@B";
-} OPT_END(performance);
-
-OPT_BEGIN(gain, "G") {
- report->show_revalued =
- report->show_revalued_only = true;
-
- ledger::amount_expr = "@a";
- ledger::total_expr = "@G";
-} OPT_END(gain);
-
-static string expand_value_expr(const string& tmpl,
- const string& expr)
-{
- string xp = tmpl;
- for (string::size_type i = xp.find('#');
- i != string::npos;
- i = xp.find('#'))
- xp = (string(xp, 0, i) + "(" + expr + ")" +
- string(xp, i + 1));
- return xp;
-}
-
-OPT_BEGIN(average, "A") {
- ledger::total_expr = expand_value_expr("@A(#)", ledger::total_expr.expr);
-} OPT_END(average);
-
-OPT_BEGIN(deviation, "D") {
- ledger::total_expr = expand_value_expr("@t-@A(#)", ledger::total_expr.expr);
-} OPT_END(deviation);
-
-OPT_BEGIN(percentage, "%") {
- ledger::total_expr = expand_value_expr("^#&{100.0%}*(#/^#)",
- ledger::total_expr.expr);
-} OPT_END(percentage);
-
-//////////////////////////////////////////////////////////////////////
-
-option_t config_options[CONFIG_OPTIONS_SIZE] = {
- { "abbrev-len", '\0', true, opt_abbrev_len, false },
- { "account", 'a', true, opt_account, false },
- { "actual", 'L', false, opt_actual, false },
- { "add-budget", '\0', false, opt_add_budget, false },
- { "amount", 't', true, opt_amount, false },
- { "amount-data", 'j', false, opt_amount_data, false },
- { "ansi", '\0', false, opt_ansi, false },
- { "ansi-invert", '\0', false, opt_ansi_invert, false },
- { "average", 'A', false, opt_average, false },
- { "balance-format", '\0', true, opt_balance_format, false },
- { "base", '\0', false, opt_base, false },
- { "basis", 'B', false, opt_basis, false },
- { "begin", 'b', true, opt_begin, false },
- { "budget", '\0', false, opt_budget, false },
- { "by-payee", 'P', false, opt_by_payee, false },
- { "cache", '\0', true, opt_cache, false },
- { "cleared", 'C', false, opt_cleared, false },
- { "code-as-payee", '\0', false, opt_code_as_payee, false },
- { "collapse", 'n', false, opt_collapse, false },
- { "comm-as-payee", 'x', false, opt_comm_as_payee, false },
- { "cost", '\0', false, opt_basis, false },
- { "current", 'c', false, opt_current, false },
- { "daily", '\0', false, opt_daily, false },
- { "date-format", 'y', true, opt_date_format, false },
- { "debug", '\0', true, opt_debug, false },
- { "descend", '\0', true, opt_descend, false },
- { "descend-if", '\0', true, opt_descend_if, false },
- { "deviation", 'D', false, opt_deviation, false },
- { "display", 'd', true, opt_display, false },
- { "dow", '\0', false, opt_dow, false },
- { "download", 'Q', false, opt_download, false },
- { "effective", '\0', false, opt_effective, false },
- { "empty", 'E', false, opt_empty, false },
- { "end", 'e', true, opt_end, false },
- { "equity-format", '\0', true, opt_equity_format, false },
- { "file", 'f', true, opt_file, false },
- { "forecast", '\0', true, opt_forecast, false },
- { "format", 'F', true, opt_format, false },
- { "full-help", 'H', false, opt_full_help, false },
- { "gain", 'G', false, opt_gain, false },
- { "head", '\0', true, opt_head, false },
- { "help", 'h', false, opt_help, false },
- { "help-calc", '\0', false, opt_help_calc, false },
- { "help-comm", '\0', false, opt_help_comm, false },
- { "help-disp", '\0', false, opt_help_disp, false },
- { "init-file", 'i', true, opt_init_file, false },
- { "input-date-format", '\0', true, opt_input_date_format, false },
- { "limit", 'l', true, opt_limit, false },
- { "lot-dates", '\0', false, opt_lot_dates, false },
- { "lot-prices", '\0', false, opt_lot_prices, false },
- { "lot-tags", '\0', false, opt_lot_tags, false },
- { "lots", '\0', false, opt_lots, false },
- { "market", 'V', false, opt_market, false },
- { "monthly", 'M', false, opt_monthly, false },
- { "no-cache", '\0', false, opt_no_cache, false },
- { "only", '\0', true, opt_only, false },
- { "output", 'o', true, opt_output, false },
- { "pager", '\0', true, opt_pager, false },
- { "percentage", '%', false, opt_percentage, false },
- { "performance", 'g', false, opt_performance, false },
- { "period", 'p', true, opt_period, false },
- { "period-sort", '\0', true, opt_period_sort, false },
- { "plot-amount-format", '\0', true, opt_plot_amount_format, false },
- { "plot-total-format", '\0', true, opt_plot_total_format, false },
- { "price", 'I', false, opt_price, false },
- { "price-db", '\0', true, opt_price_db, false },
- { "price-exp", 'Z', true, opt_price_exp, false },
- { "prices-format", '\0', true, opt_prices_format, false },
- { "print-format", '\0', true, opt_print_format, false },
- { "quantity", 'O', false, opt_quantity, false },
- { "quarterly", '\0', false, opt_quarterly, false },
- { "real", 'R', false, opt_real, false },
- { "reconcile", '\0', true, opt_reconcile, false },
- { "reconcile-date", '\0', true, opt_reconcile_date, false },
- { "register-format", '\0', true, opt_register_format, false },
- { "related", 'r', false, opt_related, false },
- { "set-price", '\0', true, opt_set_price, false },
- { "sort", 'S', true, opt_sort, false },
- { "sort-all", '\0', true, opt_sort_all, false },
- { "sort-entries", '\0', true, opt_sort_entries, false },
- { "subtotal", 's', false, opt_subtotal, false },
- { "tail", '\0', true, opt_tail, false },
- { "total", 'T', true, opt_total, false },
- { "total-data", 'J', false, opt_total_data, false },
- { "totals", '\0', false, opt_totals, false },
- { "trace", '\0', false, opt_trace, false },
- { "truncate", '\0', true, opt_truncate, false },
- { "unbudgeted", '\0', false, opt_unbudgeted, false },
- { "uncleared", 'U', false, opt_uncleared, false },
- { "verbose", '\0', false, opt_verbose, false },
- { "version", 'v', false, opt_version, false },
- { "weekly", 'W', false, opt_weekly, false },
- { "wide", 'w', false, opt_wide, false },
- { "wide-register-format", '\0', true, opt_wide_register_format, false },
- { "write-hdr-format", '\0', true, opt_write_hdr_format, false },
- { "write-xact-format", '\0', true, opt_write_xact_format, false },
- { "yearly", 'Y', false, opt_yearly, false },
-};
-
} // namespace ledger
diff --git a/option.h b/option.h
index 1714d9db..02afe0c2 100644
--- a/option.h
+++ b/option.h
@@ -1,44 +1,52 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#ifndef _OPTION_H
#define _OPTION_H
-#include "utils.h"
+#include "valexpr.h"
namespace ledger {
-typedef void (*handler_t)(const char * arg);
-
-struct option_t {
- const char * long_opt;
- char short_opt;
- bool wants_arg;
- handler_t handler;
- bool handled;
-};
-
-DECLARE_EXCEPTION(error, option_error);
-
-bool process_option(option_t * options, const string& opt,
+void process_option(const string& name, expr::scope_t& scope,
const char * arg = NULL);
-void process_arguments(option_t * options, int argc, char ** argv,
- const bool anywhere, std::list<string>& args);
-void process_environment(option_t * options, const char ** envp,
- const string& tag);
-class config_t;
-class report_t;
+void process_environment(const char ** envp, const string& tag,
+ expr::scope_t& scope);
-extern config_t * config;
-extern report_t * report;
+void process_arguments(int argc, char ** argv, const bool anywhere,
+ expr::scope_t& scope,
+ std::list<string>& args);
-#define CONFIG_OPTIONS_SIZE 97
-extern option_t config_options[CONFIG_OPTIONS_SIZE];
-
-void option_help(std::ostream& out);
-
-#define OPT_BEGIN(tag, chars) \
- void opt_ ## tag(const char * optarg)
-
-#define OPT_END(tag)
+DECLARE_EXCEPTION(error, option_error);
} // namespace ledger
diff --git a/parser.cc b/parser.cc
index 434cb0c4..bb00df6a 100644
--- a/parser.cc
+++ b/parser.cc
@@ -127,7 +127,7 @@ unsigned int parse_ledger_data(config_t& config,
if (boost::filesystem::exists(config.cache_file)) {
boost::filesystem::ifstream stream(config.cache_file);
if (cache_parser && cache_parser->test(stream)) {
- path price_db_orig = journal->price_db;
+ optional<path> price_db_orig = journal->price_db;
journal->price_db = config.price_db;
entry_count += cache_parser->parse(stream, config, journal,
NULL, &config.data_file);
@@ -145,13 +145,13 @@ unsigned int parse_ledger_data(config_t& config,
acct = journal->find_account(config.account);
journal->price_db = config.price_db;
- if (! journal->price_db.empty() &&
- boost::filesystem::exists(journal->price_db)) {
- if (parse_journal_file(journal->price_db, config, journal)) {
+ if (journal->price_db &&
+ boost::filesystem::exists(*journal->price_db)) {
+ if (parse_journal_file(*journal->price_db, config, journal)) {
throw new error("Entries not allowed in price history file");
} else {
DEBUG("ledger.config.cache",
- "read price database " << journal->price_db);
+ "read price database " << *journal->price_db);
journal->sources.pop_back();
}
}
@@ -172,8 +172,8 @@ unsigned int parse_ledger_data(config_t& config,
else if (boost::filesystem::exists(config.data_file)) {
entry_count += parse_journal_file(config.data_file, config, journal,
acct);
- if (! journal->price_db.empty())
- journal->sources.push_back(journal->price_db);
+ if (journal->price_db)
+ journal->sources.push_back(*journal->price_db);
}
}
diff --git a/report.cc b/report.cc
index 582f4e93..f04e0327 100644
--- a/report.cc
+++ b/report.cc
@@ -1,413 +1,232 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#include "report.h"
namespace ledger {
-report_t::report_t()
+report_t::~report_t()
{
- ledger::amount_expr = "@a";
- ledger::total_expr = "@O";
-
- predicate = "";
- secondary_predicate = "";
- display_predicate = "";
- descend_expr = "";
-
- budget_flags = BUDGET_NO_BUDGET;
-
- head_entries = 0;
- tail_entries = 0;
-
- show_collapsed = false;
- show_subtotal = false;
- show_totals = false;
- show_related = false;
- show_all_related = false;
- show_inverted = false;
- show_empty = false;
- days_of_the_week = false;
- by_payee = false;
- comm_as_payee = false;
- code_as_payee = false;
- show_revalued = false;
- show_revalued_only = false;
- keep_price = false;
- keep_date = false;
- keep_tag = false;
- entry_sort = false;
- sort_all = false;
+ TRACE_DTOR(report_t);
}
-void
-report_t::regexps_to_predicate(const string& command,
- std::list<string>::const_iterator begin,
- std::list<string>::const_iterator end,
- const bool account_regexp,
- const bool add_account_short_masks,
- const bool logical_and)
+void report_t::apply_transforms(expr::scope_t& scope)
{
- string regexps[2];
-
- assert(begin != end);
-
- // Treat the remaining command-line arguments as regular
- // expressions, used for refining report results.
-
- for (std::list<string>::const_iterator i = begin;
- i != end;
- i++)
- if ((*i)[0] == '-') {
- if (! regexps[1].empty())
- regexps[1] += "|";
- regexps[1] += (*i).substr(1);
- }
- else if ((*i)[0] == '+') {
- if (! regexps[0].empty())
- regexps[0] += "|";
- regexps[0] += (*i).substr(1);
- }
- else {
- if (! regexps[0].empty())
- regexps[0] += "|";
- regexps[0] += *i;
- }
+#if 0
+ typedef tuple<shared_ptr<transform_t>, value_t> transform_details_tuple;
- for (int i = 0; i < 2; i++) {
- if (regexps[i].empty())
- continue;
-
- if (! predicate.empty())
- predicate += logical_and ? "&" : "|";
-
- int add_predicate = 0; // 1 adds /.../, 2 adds ///.../
- if (i == 1) {
- predicate += "!";
- }
- else if (add_account_short_masks) {
- if (regexps[i].find(':') != string::npos ||
- regexps[i].find('.') != string::npos ||
- regexps[i].find('*') != string::npos ||
- regexps[i].find('+') != string::npos ||
- regexps[i].find('[') != string::npos ||
- regexps[i].find('(') != string::npos) {
- show_subtotal = true;
- add_predicate = 1;
- } else {
- add_predicate = 2;
- }
- }
- else {
- add_predicate = 1;
- }
-
- if (i != 1 && command == "b" && account_regexp) {
- if (! show_related && ! show_all_related) {
- if (! display_predicate.empty())
- display_predicate += "&";
- if (! show_empty)
- display_predicate += "T&";
-
- if (add_predicate == 2)
- display_predicate += "//";
- display_predicate += "/(?:";
- display_predicate += regexps[i];
- display_predicate += ")/";
- }
- else if (! show_empty) {
- if (! display_predicate.empty())
- display_predicate += "&";
- display_predicate += "T";
- }
- }
-
- if (! account_regexp)
- predicate += "/";
- predicate += "/(?:";
- predicate += regexps[i];
- predicate += ")/";
+ foreach (transform_details_tuple& transform_details, transforms) {
+ expr::call_scope_t call_args(scope);
+ call_args.set_args(transform_details.get<1>());
+ (*transform_details.get<0>())(call_args);
}
+#endif
}
-void report_t::process_options(const string& command,
- strings_list::iterator arg,
- strings_list::iterator args_end)
+value_t report_t::abbrev(expr::call_scope_t& args)
{
- // Configure some other options depending on report type
-
- if (command == "p" || command == "e" || command == "w") {
- show_related =
- show_all_related = true;
- }
- else if (command == "E") {
- show_subtotal = true;
- }
- else if (show_related) {
- if (command == "r") {
- show_inverted = true;
- } else {
- show_subtotal = true;
- show_all_related = true;
- }
- }
-
- if (command != "b" && command != "r")
- amount_t::keep_base = true;
-
- // Process remaining command-line arguments
-
- if (command != "e") {
- // Treat the remaining command-line arguments as regular
- // expressions, used for refining report results.
-
- std::list<string>::iterator i = arg;
- for (; i != args_end; i++)
- if (*i == "--")
- break;
-
- if (i != arg)
- regexps_to_predicate(command, arg, i, true,
- (command == "b" && ! show_subtotal &&
- display_predicate.empty()));
- if (i != args_end && ++i != args_end)
- regexps_to_predicate(command, i, args_end);
- }
+ if (args.size() < 2)
+ throw_(std::logic_error, "usage: abbrev(STRING, WIDTH [, STYLE, ABBREV_LEN])");
+
+ string str = args[0].as_string();
+ long wid = args[1];
+
+#if 0
+ elision_style_t style = session.elision_style;
+ if (args.size() == 3)
+ style = static_cast<elision_style_t>(args[2].as_long());
+#endif
+
+ long abbrev_len = session.abbrev_length;
+ if (args.size() == 4)
+ abbrev_len = args[3].as_long();
+
+#if 0
+ return value_t(abbreviate(str, wid, style, true,
+ static_cast<int>(abbrev_len)), true);
+#else
+ return value_t();
+#endif
+}
- // Setup the default value for the display predicate
+value_t report_t::ftime(expr::call_scope_t& args)
+{
+ if (args.size() < 1)
+ throw_(std::logic_error, "usage: ftime(DATE [, DATE_FORMAT])");
+
+ datetime_t date = args[0].as_datetime();
+
+ string date_format;
+ if (args.size() == 2)
+ date_format = args[1].as_string();
+#if 0
+ // jww (2007-04-18): Need to setup an output facet here
+ else
+ date_format = moment_t::output_format;
+
+ return value_t(date.as_string(date_format), true);
+#else
+ return NULL_VALUE;
+#endif
+}
- if (display_predicate.empty()) {
- if (command == "b") {
- if (! show_empty)
- display_predicate = "T";
- if (! show_subtotal) {
- if (! display_predicate.empty())
- display_predicate += "&";
- display_predicate += "l<=1";
- }
- }
- else if (command == "E") {
- display_predicate = "t";
+#if 0
+optional<value_t>
+report_t::resolve(const string& name, expr::call_scope_t& args)
+{
+ const char * p = name.c_str();
+ switch (*p) {
+ case 'a':
+ if (name == "abbrev") {
+ return abbrev(args);
}
- else if (command == "r" && ! show_empty) {
- display_predicate = "a";
+ break;
+
+ case 'f':
+ if (name == "ftime") {
+ return ftime(args);
}
+ break;
}
-
- DEBUG("ledger.config.predicates", "Predicate: " << predicate);
- DEBUG("ledger.config.predicates", "Display P: " << display_predicate);
-
- // Setup the values of %t and %T, used in format strings
-
- if (! amount_expr.empty())
- ledger::amount_expr = amount_expr;
- if (! total_expr.empty())
- ledger::total_expr = total_expr;
-
- // Now setup the various formatting strings
-
- if (! date_output_format.empty())
- output_time_format = date_output_format;
-
- amount_t::keep_price = keep_price;
- amount_t::keep_date = keep_date;
- amount_t::keep_tag = keep_tag;
-
- if (! report_period.empty() && ! sort_all)
- entry_sort = true;
+ return expr::scope_t::resolve(name, args);
}
+#endif
-item_handler<transaction_t> *
-report_t::chain_xact_handlers(const string& command,
- item_handler<transaction_t> * base_formatter,
- journal_t * journal,
- account_t * master,
- std::list<item_handler<transaction_t> *>& ptrs)
+expr::ptr_op_t report_t::lookup(const string& name)
{
- bool remember_components = false;
-
- item_handler<transaction_t> * formatter = NULL;
-
- ptrs.push_back(formatter = base_formatter);
-
- // format_transactions write each transaction received to the
- // output stream.
- if (! (command == "b" || command == "E")) {
- // truncate_entries cuts off a certain number of _entries_ from
- // being displayed. It does not affect calculation.
- if (head_entries || tail_entries)
- ptrs.push_back(formatter =
- new truncate_entries(formatter,
- head_entries, tail_entries));
-
- // filter_transactions will only pass through transactions
- // matching the `display_predicate'.
- if (! display_predicate.empty())
- ptrs.push_back(formatter =
- new filter_transactions(formatter,
- display_predicate));
-
- // calc_transactions computes the running total. When this
- // appears will determine, for example, whether filtered
- // transactions are included or excluded from the running total.
- ptrs.push_back(formatter = new calc_transactions(formatter));
-
- // component_transactions looks for reported transaction that
- // match the given `descend_expr', and then reports the
- // transactions which made up the total for that reported
- // transaction.
- if (! descend_expr.empty()) {
- std::list<string> descend_exprs;
-
- string::size_type beg = 0;
- for (string::size_type pos = descend_expr.find(';');
- pos != string::npos;
- beg = pos + 1, pos = descend_expr.find(';', beg))
- descend_exprs.push_back(string(descend_expr, beg, pos - beg));
- descend_exprs.push_back(string(descend_expr, beg));
+ const char * p = name.c_str();
+ switch (*p) {
+ case 'o':
+ if (std::strncmp(p, "option_", 7) == 0) {
+ p = p + 7;
+ switch (*p) {
+ case 'a':
+#if 0
+ if (std::strcmp(p, "accounts") == 0)
+ return MAKE_FUNCTOR(report_t::option_accounts);
+ else
+#endif
+ if (std::strcmp(p, "amount") == 0)
+ return MAKE_FUNCTOR(report_t::option_amount);
+ break;
- for (std::list<string>::reverse_iterator i =
- descend_exprs.rbegin();
- i != descend_exprs.rend();
- i++)
- ptrs.push_back(formatter =
- new component_transactions(formatter, *i));
+ case 'b':
+ if (std::strcmp(p, "bar") == 0)
+ return MAKE_FUNCTOR(report_t::option_bar);
+ break;
- remember_components = true;
- }
+#if 0
+ case 'c':
+ if (std::strcmp(p, "clean") == 0)
+ return MAKE_FUNCTOR(report_t::option_clean);
+ else if (std::strcmp(p, "compact") == 0)
+ return MAKE_FUNCTOR(report_t::option_compact);
+ break;
+#endif
+
+ case 'e':
+#if 0
+ if (std::strcmp(p, "entries") == 0)
+ return MAKE_FUNCTOR(report_t::option_entries);
+ else if (std::strcmp(p, "eval") == 0)
+ return MAKE_FUNCTOR(report_t::option_eval);
+ else if (std::strcmp(p, "exclude") == 0)
+ return MAKE_FUNCTOR(report_t::option_remove);
+#endif
+ break;
- // reconcile_transactions will pass through only those
- // transactions which can be reconciled to a given balance
- // (calculated against the transactions which it receives).
- if (! reconcile_balance.empty()) {
- datetime_t cutoff = current_moment;
- if (! reconcile_date.empty())
- cutoff = parse_datetime(reconcile_date);
- ptrs.push_back(formatter =
- new reconcile_transactions
- (formatter, value_t(reconcile_balance), cutoff));
- }
+ case 'f':
+#if 0
+ if (std::strcmp(p, "foo") == 0)
+ return MAKE_FUNCTOR(report_t::option_foo);
+ else
+#endif
+ if (std::strcmp(p, "format") == 0)
+ return MAKE_FUNCTOR(report_t::option_format);
+ break;
- // filter_transactions will only pass through transactions
- // matching the `secondary_predicate'.
- if (! secondary_predicate.empty())
- ptrs.push_back(formatter =
- new filter_transactions(formatter,
- secondary_predicate));
+ case 'i':
+#if 0
+ if (std::strcmp(p, "include") == 0)
+ return MAKE_FUNCTOR(report_t::option_select);
+#endif
+ break;
- // sort_transactions will sort all the transactions it sees, based
- // on the `sort_order' value expression.
- if (! sort_string.empty()) {
- if (entry_sort)
- ptrs.push_back(formatter =
- new sort_entries(formatter, sort_string));
- else
- ptrs.push_back(formatter =
- new sort_transactions(formatter, sort_string));
- }
+ case 'l':
+#if 0
+ if (! *(p + 1) || std::strcmp(p, "limit") == 0)
+ return MAKE_FUNCTOR(report_t::option_limit);
+#endif
+ break;
- // changed_value_transactions adds virtual transactions to the
- // list to account for changes in market value of commodities,
- // which otherwise would affect the running total unpredictably.
- if (show_revalued)
- ptrs.push_back(formatter =
- new changed_value_transactions(formatter,
- show_revalued_only));
+#if 0
+ case 'm':
+ if (std::strcmp(p, "merge") == 0)
+ return MAKE_FUNCTOR(report_t::option_merge);
+ break;
+#endif
- // collapse_transactions causes entries with multiple transactions
- // to appear as entries with a subtotaled transaction for each
- // commodity used.
- if (show_collapsed)
- ptrs.push_back(formatter = new collapse_transactions(formatter));
+ case 'r':
+#if 0
+ if (std::strcmp(p, "remove") == 0)
+ return MAKE_FUNCTOR(report_t::option_remove);
+#endif
+ break;
- // subtotal_transactions combines all the transactions it receives
- // into one subtotal entry, which has one transaction for each
- // commodity in each account.
- //
- // period_transactions is like subtotal_transactions, but it
- // subtotals according to time periods rather than totalling
- // everything.
- //
- // dow_transactions is like period_transactions, except that it
- // reports all the transactions that fall on each subsequent day
- // of the week.
- if (show_subtotal)
- ptrs.push_back(formatter =
- new subtotal_transactions(formatter, remember_components));
+#if 0
+ case 's':
+ if (std::strcmp(p, "select") == 0)
+ return MAKE_FUNCTOR(report_t::option_select);
+ else if (std::strcmp(p, "split") == 0)
+ return MAKE_FUNCTOR(report_t::option_split);
+ break;
+#endif
- if (days_of_the_week)
- ptrs.push_back(formatter =
- new dow_transactions(formatter, remember_components));
- else if (by_payee)
- ptrs.push_back(formatter =
- new by_payee_transactions(formatter, remember_components));
+ case 't':
+ if (! *(p + 1))
+ return MAKE_FUNCTOR(report_t::option_amount);
+ else if (std::strcmp(p, "total") == 0)
+ return MAKE_FUNCTOR(report_t::option_total);
+ break;
- // interval_transactions groups transactions together based on a
- // time period, such as weekly or monthly.
- if (! report_period.empty()) {
- ptrs.push_back(formatter =
- new interval_transactions(formatter, report_period,
- remember_components));
- ptrs.push_back(formatter = new sort_transactions(formatter, "d"));
+ case 'T':
+ if (! *(p + 1))
+ return MAKE_FUNCTOR(report_t::option_total);
+ break;
+ }
}
+ break;
}
- // invert_transactions inverts the value of the transactions it
- // receives.
- if (show_inverted)
- ptrs.push_back(formatter = new invert_transactions(formatter));
-
- // related_transactions will pass along all transactions related
- // to the transaction received. If `show_all_related' is true,
- // then all the entry's transactions are passed; meaning that if
- // one transaction of an entry is to be printed, all the
- // transaction for that entry will be printed.
- if (show_related)
- ptrs.push_back(formatter =
- new related_transactions(formatter,
- show_all_related));
-
- // This filter_transactions will only pass through transactions
- // matching the `predicate'.
- if (! predicate.empty())
- ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
-
- // budget_transactions takes a set of transactions from a data
- // file and uses them to generate "budget transactions" which
- // balance against the reported transactions.
- //
- // forecast_transactions is a lot like budget_transactions, except
- // that it adds entries only for the future, and does not balance
- // them against anything but the future balance.
-
- if (budget_flags) {
- budget_transactions * handler
- = new budget_transactions(formatter, budget_flags);
- handler->add_period_entries(journal->period_entries);
- ptrs.push_back(formatter = handler);
-
- // Apply this before the budget handler, so that only matching
- // transactions are calculated toward the budget. The use of
- // filter_transactions above will further clean the results so
- // that no automated transactions that don't match the filter get
- // reported.
- if (! predicate.empty())
- ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
- }
- else if (! forecast_limit.empty()) {
- forecast_transactions * handler
- = new forecast_transactions(formatter, forecast_limit);
- handler->add_period_entries(journal->period_entries);
- ptrs.push_back(formatter = handler);
-
- // See above, under budget_transactions.
- if (! predicate.empty())
- ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
- }
-
- if (comm_as_payee)
- ptrs.push_back(formatter = new set_comm_as_payee(formatter));
- else if (code_as_payee)
- ptrs.push_back(formatter = new set_code_as_payee(formatter));
-
- return formatter;
+ return expr::symbol_scope_t::lookup(name);
}
} // namespace ledger
diff --git a/report.h b/report.h
index 505eafba..92a97457 100644
--- a/report.h
+++ b/report.h
@@ -1,79 +1,194 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#ifndef _REPORT_H
#define _REPORT_H
-#include "ledger.h"
-
-#include <iostream>
-#include <memory>
-#include <list>
+#include "session.h"
namespace ledger {
-class report_t
+typedef std::list<string> strings_list;
+
+class report_t : public expr::symbol_scope_t
{
- public:
- path output_file;
-
- string predicate;
- string secondary_predicate;
- string display_predicate;
- string report_period;
- string report_period_sort;
- string format_string;
- string sort_string;
- string amount_expr;
- string total_expr;
- string descend_expr;
- string forecast_limit;
- string reconcile_balance;
- string reconcile_date;
- string date_output_format;
-
- unsigned long budget_flags;
-
- int head_entries;
- int tail_entries;
-
- bool show_collapsed;
- bool show_subtotal;
- bool show_totals;
- bool show_related;
- bool show_all_related;
- bool show_inverted;
- bool show_empty;
- bool days_of_the_week;
- bool by_payee;
- bool comm_as_payee;
- bool code_as_payee;
- bool show_revalued;
- bool show_revalued_only;
- bool keep_price;
- bool keep_date;
- bool keep_tag;
- bool entry_sort;
- bool sort_all;
-
- report_t();
-
- void regexps_to_predicate(const string& command,
- std::list<string>::const_iterator begin,
- std::list<string>::const_iterator end,
- const bool account_regexp = false,
- const bool add_account_short_masks = false,
- const bool logical_and = true);
-
- void process_options(const string& command,
- strings_list::iterator arg,
- strings_list::iterator args_end);
-
- item_handler<transaction_t> *
- chain_xact_handlers(const string& command,
- item_handler<transaction_t> * base_formatter,
- journal_t * journal,
- account_t * master,
- std::list<item_handler<transaction_t> *>& ptrs);
+public:
+ optional<path> output_file;
+ string format_string;
+ string amount_expr;
+ string total_expr;
+ string date_output_format;
+
+ unsigned long budget_flags;
+
+ string account;
+ optional<path> pager;
+
+ bool show_totals;
+ bool raw_mode;
+
+ session_t& session;
+#if 0
+ transform_t * last_transform;
+
+ std::list<tuple<shared_ptr<transform_t>, value_t> > transforms;
+#endif
+
+ explicit report_t(session_t& _session)
+ : expr::symbol_scope_t(downcast<expr::scope_t>(_session)),
+ show_totals(false),
+ raw_mode(false),
+ session(_session)
+#if 0
+ ,
+ last_transform(NULL)
+#endif
+ {
+ TRACE_CTOR(report_t, "session_t&");
+#if 0
+ eval("t=total,TOT=0,T()=(TOT=TOT+t,TOT)");
+#endif
+ }
+
+ virtual ~report_t();
+
+ void apply_transforms(expr::scope_t& scope);
+
+ //
+ // Utility functions for value expressions
+ //
+
+ value_t ftime(expr::call_scope_t& args);
+ value_t abbrev(expr::call_scope_t& args);
+
+ //
+ // Config options
+ //
+
+ void eval(const string& expr) {
+#if 0
+ expr(expr).compile((xml::document_t *)NULL, this);
+#endif
+ }
+ value_t option_eval(expr::call_scope_t& args) {
+ eval(args[0].as_string());
+ return NULL_VALUE;
+ }
+
+ value_t option_amount(expr::call_scope_t& args) {
+ eval(string("t=") + args[0].as_string());
+ return NULL_VALUE;
+ }
+ value_t option_total(expr::call_scope_t& args) {
+ eval(string("T()=") + args[0].as_string());
+ return NULL_VALUE;
+ }
+
+ value_t option_format(expr::call_scope_t& args) {
+ format_string = args[0].as_string();
+ return NULL_VALUE;
+ }
+
+ value_t option_raw(expr::call_scope_t& args) {
+ raw_mode = true;
+ return NULL_VALUE;
+ }
+
+ value_t option_foo(expr::call_scope_t& args) {
+ std::cout << "This is foo" << std::endl;
+ return NULL_VALUE;
+ }
+ value_t option_bar(expr::call_scope_t& args) {
+ std::cout << "This is bar: " << args[0] << std::endl;
+ return NULL_VALUE;
+ }
+
+ //
+ // Transform options
+ //
+
+#if 0
+ value_t option_select(expr::call_scope_t& args) {
+ transforms.push_back(new select_transform(args[0].as_string()));
+ return NULL_VALUE;
+ }
+ value_t option_limit(expr::call_scope_t& args) {
+ string expr = (string("//xact[") +
+ args[0].as_string() + "]");
+ transforms.push_back(new select_transform(expr));
+ return NULL_VALUE;
+ }
+
+ value_t option_remove(expr::call_scope_t& args) {
+ transforms.push_back(new remove_transform(args[0].as_string()));
+ return NULL_VALUE;
+ }
+
+ value_t option_accounts(expr::call_scope_t& args) {
+ transforms.push_back(new accounts_transform);
+ return NULL_VALUE;
+ }
+ value_t option_compact(expr::call_scope_t& args) {
+ transforms.push_back(new compact_transform);
+ return NULL_VALUE;
+ }
+ value_t option_clean(expr::call_scope_t& args) {
+ transforms.push_back(new clean_transform);
+ return NULL_VALUE;
+ }
+ value_t option_entries(expr::call_scope_t& args) {
+ transforms.push_back(new entries_transform);
+ return NULL_VALUE;
+ }
+
+ value_t option_split(expr::call_scope_t& args) {
+ transforms.push_back(new split_transform);
+ return NULL_VALUE;
+ }
+ value_t option_merge(expr::call_scope_t& args) {
+ transforms.push_back(new merge_transform);
+ return NULL_VALUE;
+ }
+#endif
+
+ //
+ // Scope members
+ //
+
+ virtual expr::ptr_op_t lookup(const string& name);
};
+string abbrev(const string& str, unsigned int width,
+ const bool is_account);
+
} // namespace ledger
#endif // _REPORT_H
diff --git a/session.cc b/session.cc
new file mode 100644
index 00000000..98ae9613
--- /dev/null
+++ b/session.cc
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "session.h"
+
+namespace ledger {
+
+session_t * session_t::current = NULL;
+
+#if 0
+boost::mutex session_t::session_mutex;
+#endif
+
+static void initialize();
+static void shutdown();
+
+void set_session_context(session_t * session)
+{
+#if 0
+ session_t::session_mutex.lock();
+#endif
+
+ if (session && ! session_t::current) {
+ initialize();
+ }
+ else if (! session && session_t::current) {
+ shutdown();
+#if 0
+ session_t::session_mutex.unlock();
+#endif
+ }
+
+ session_t::current = session;
+}
+
+void release_session_context()
+{
+#if 0
+ session_t::session_mutex.unlock();
+#endif
+}
+
+session_t::session_t()
+ : symbol_scope_t(),
+
+ register_format
+ ("%((//entry)%{date} %-.20{payee}"
+ "%((./xact)%32|%-22{abbrev(account, 22)} %12.67t %12.80T\n))"),
+ wide_register_format
+ ("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
+ "%48|%-.38A %22.108t %!22.132T\n"),
+ print_format
+#if 1
+ ("%(/%(/%{date} %-.20{payee}\n%(: %-34{account} %12t\n)\n))"),
+#else
+ ("\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"),
+#endif
+ balance_format
+ ("%(/%(//%20t %{\" \" * rdepth}%{rname}\n))--------------------\n%20t\n"),
+ equity_format
+
+ ("%((/)%{ftime(now, date_format)} %-.20{\"Opening Balance\"}\n%((.//account[value != 0]) %-34{fullname} %12{value}\n)\n)"),
+ plot_amount_format
+ ("%D %(@S(@t))\n"),
+ plot_total_format
+ ("%D %(@S(@T))\n"),
+ write_hdr_format
+ ("%d %Y%C%P\n"),
+ write_xact_format
+ (" %-34W %12o%n\n"),
+ prices_format
+ ("%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n"),
+ pricesdb_format
+ ("P %[%Y/%m/%d %H:%M:%S] %A %t\n"),
+
+ pricing_leeway(24 * 3600),
+
+ download_quotes(false),
+ use_cache(false),
+ cache_dirty(false),
+
+ now(now),
+
+ elision_style(ABBREVIATE),
+ abbrev_length(2),
+
+ ansi_codes(false),
+ ansi_invert(false)
+{
+ TRACE_CTOR(session_t, "xml::xpath_t::scope_t&");
+}
+
+std::size_t session_t::read_journal(std::istream& in,
+ const path& pathname,
+ xml::builder_t& builder)
+{
+#if 0
+ if (! master)
+ master = journal->master;
+#endif
+
+ foreach (parser_t& parser, parsers)
+ if (parser.test(in))
+ return parser.parse(in, pathname, builder);
+
+ return 0;
+}
+
+std::size_t session_t::read_journal(const path& pathname,
+ xml::builder_t& builder)
+{
+#if 0
+ journal->sources.push_back(pathname);
+#endif
+
+ if (! exists(pathname))
+ throw_(std::logic_error, "Cannot read file" << pathname);
+
+ ifstream stream(pathname);
+ return read_journal(stream, pathname, builder);
+}
+
+void session_t::read_init()
+{
+ if (! init_file)
+ return;
+
+ if (! exists(*init_file))
+ throw_(std::logic_error, "Cannot read init file" << *init_file);
+
+ ifstream init(*init_file);
+
+ // jww (2006-09-15): Read initialization options here!
+}
+
+std::size_t session_t::read_data(xml::builder_t& builder,
+ journal_t * journal,
+ const string& master_account)
+{
+ if (data_file.empty())
+ throw_(parse_error, "No journal file was specified (please use -f)");
+
+ TRACE_START(parser, 1, "Parsing journal file");
+
+ std::size_t entry_count = 0;
+
+ DEBUG("ledger.cache", "3. use_cache = " << use_cache);
+
+ if (use_cache && cache_file) {
+ DEBUG("ledger.cache", "using_cache " << cache_file->string());
+ cache_dirty = true;
+ if (exists(*cache_file)) {
+ push_variable<optional<path> >
+ save_price_db(journal->price_db, price_db);
+
+ entry_count += read_journal(*cache_file, builder);
+ if (entry_count > 0)
+ cache_dirty = false;
+ }
+ }
+
+ if (entry_count == 0) {
+ account_t * acct = NULL;
+ if (! master_account.empty())
+ acct = journal->find_account(master_account);
+
+ journal->price_db = price_db;
+ if (journal->price_db && exists(*journal->price_db)) {
+ if (read_journal(*journal->price_db, builder)) {
+ throw_(parse_error, "Entries not allowed in price history file");
+ } else {
+ DEBUG("ledger.cache",
+ "read price database " << journal->price_db->string());
+ journal->sources.pop_back();
+ }
+ }
+
+ DEBUG("ledger.cache", "rejected cache, parsing " << data_file.string());
+ if (data_file == "-") {
+ use_cache = false;
+ journal->sources.push_back("<stdin>");
+ entry_count += read_journal(std::cin, "<stdin>", builder);
+ }
+ else if (exists(data_file)) {
+ entry_count += read_journal(data_file, builder);
+ if (journal->price_db)
+ journal->sources.push_back(*journal->price_db);
+ }
+ }
+
+ VERIFY(journal->valid());
+
+ TRACE_STOP(parser, 1);
+
+ return entry_count;
+}
+
+#if 0
+optional<value_t>
+session_t::resolve(const string& name, xml::xpath_t::scope_t& locals)
+{
+ const char * p = name.c_str();
+ switch (*p) {
+ case 'd':
+#if 0
+ if (name == "date_format") {
+ // jww (2007-04-18): What to do here?
+ return value_t(moment_t::output_format, true);
+ }
+#endif
+ break;
+
+ case 'n':
+ switch (*++p) {
+ case 'o':
+ if (name == "now")
+ return value_t(now);
+ break;
+ }
+ break;
+
+ case 'r':
+ if (name == "register_format")
+ return value_t(register_format, true);
+ break;
+ }
+ return xml::xpath_t::scope_t::resolve(name, locals);
+}
+#endif
+
+xml::xpath_t::ptr_op_t session_t::lookup(const string& name)
+{
+ const char * p = name.c_str();
+ switch (*p) {
+ case 'o':
+ if (std::strncmp(p, "option_", 7) == 0) {
+ p = p + 7;
+ switch (*p) {
+ case 'd':
+ if (std::strcmp(p, "debug_") == 0)
+ return MAKE_FUNCTOR(session_t::option_debug_);
+ break;
+
+ case 'f':
+ if ((*(p + 1) == '_' && ! *(p + 2)) ||
+ std::strcmp(p, "file_") == 0)
+ return MAKE_FUNCTOR(session_t::option_file_);
+ break;
+
+ case 't':
+ if (std::strcmp(p, "trace_") == 0)
+ return MAKE_FUNCTOR(session_t::option_trace_);
+ break;
+
+ case 'v':
+ if (! *(p + 1) || std::strcmp(p, "verbose") == 0)
+ return MAKE_FUNCTOR(session_t::option_verbose);
+ else if (std::strcmp(p, "verify") == 0)
+ return MAKE_FUNCTOR(session_t::option_verify);
+ break;
+ }
+ }
+ break;
+ }
+
+ return xml::xpath_t::symbol_scope_t::lookup(name);
+}
+
+// jww (2007-04-26): All of Ledger should be accessed through a
+// session_t object
+static void initialize()
+{
+ amount_t::initialize();
+ value_t::initialize();
+ xml::xpath_t::initialize();
+}
+
+static void shutdown()
+{
+ xml::xpath_t::shutdown();
+ value_t::shutdown();
+ amount_t::shutdown();
+}
+
+} // namespace ledger
diff --git a/session.h b/session.h
new file mode 100644
index 00000000..88f6b896
--- /dev/null
+++ b/session.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSION_H
+#define _SESSION_H
+
+#include "valexpr.h"
+#include "journal.h"
+#include "parser.h"
+
+namespace ledger {
+
+class session_t : public expr::symbol_scope_t
+{
+ public:
+ static session_t * current;
+
+ path data_file;
+ optional<path> init_file;
+ optional<path> cache_file;
+ optional<path> price_db;
+
+ string register_format;
+ string wide_register_format;
+ string print_format;
+ string balance_format;
+ string equity_format;
+ string plot_amount_format;
+ string plot_total_format;
+ string write_hdr_format;
+ string write_xact_format;
+ string prices_format;
+ string pricesdb_format;
+
+ unsigned long pricing_leeway;
+
+ bool download_quotes;
+ bool use_cache;
+ bool cache_dirty;
+
+ datetime_t now;
+
+#if 0
+ elision_style_t elision_style;
+#endif
+ int abbrev_length;
+
+ bool ansi_codes;
+ bool ansi_invert;
+
+ ptr_list<journal_t> journals;
+ ptr_list<parser_t> parsers;
+
+ session_t();
+ virtual ~session_t() {
+ TRACE_DTOR(session_t);
+ }
+
+ journal_t * create_journal() {
+ journal_t * journal = new journal_t;
+ journals.push_back(journal);
+ return journal;
+ }
+ void close_journal(journal_t * journal) {
+ for (ptr_list<journal_t>::iterator i = journals.begin();
+ i != journals.end();
+ i++)
+ if (&*i == journal) {
+ journals.erase(i);
+ return;
+ }
+ assert(false);
+ checked_delete(journal);
+ }
+
+#if 0
+ std::size_t read_journal(std::istream& in,
+ const path& pathname,
+ xml::builder_t& builder);
+ std::size_t read_journal(const path& pathname,
+ xml::builder_t& builder);
+
+ void read_init();
+
+ std::size_t read_data(xml::builder_t& builder,
+ journal_t * journal,
+ const string& master_account = "");
+#endif
+
+ void register_parser(parser_t * parser) {
+ parsers.push_back(parser);
+ }
+ void unregister_parser(parser_t * parser) {
+ for (ptr_list<parser_t>::iterator i = parsers.begin();
+ i != parsers.end();
+ i++)
+ if (&*i == parser) {
+ parsers.erase(i);
+ return;
+ }
+ assert(false);
+ checked_delete(parser);
+ }
+
+ //
+ // Scope members
+ //
+
+ virtual expr::ptr_op_t lookup(const string& name);
+
+ //
+ // Debug options
+ //
+
+ value_t option_trace_(expr::scope_t& locals) {
+ return NULL_VALUE;
+ }
+ value_t option_debug_(expr::scope_t& locals) {
+ return NULL_VALUE;
+ }
+
+ value_t option_verify(expr::scope_t&) {
+ return NULL_VALUE;
+ }
+ value_t option_verbose(expr::scope_t&) {
+#if defined(LOGGING_ON)
+ if (_log_level < LOG_INFO)
+ _log_level = LOG_INFO;
+#endif
+ return NULL_VALUE;
+ }
+
+ //
+ // Option handlers
+ //
+
+ value_t option_file_(expr::call_scope_t& args) {
+ assert(args.size() == 1);
+ data_file = args[0].as_string();
+ return NULL_VALUE;
+ }
+
+#if 0
+#if defined(USE_BOOST_PYTHON)
+ value_t option_import_(expr::call_scope_t& args) {
+ python_import(optarg);
+ return NULL_VALUE;
+ }
+ value_t option_import_stdin(expr::call_scope_t& args) {
+ python_eval(std::cin, PY_EVAL_MULTI);
+ return NULL_VALUE;
+ }
+#endif
+#endif
+};
+
+/**
+ * This sets the current session context, transferring all static
+ * globals to point at the data structures related to this session.
+ * Although Ledger itself is not thread-safe, by locking, switching
+ * session context, then unlocking after the operation is done,
+ * multiple threads can sequentially make use of the library. Thus, a
+ * session_t maintains all of the information relating to a single
+ * usage of the Ledger library.
+ */
+void set_session_context(session_t * session = NULL);
+
+} // namespace ledger
+
+#endif // _SESSION_H
diff --git a/textual.cc b/textual.cc
index 5517a6b2..0ab1e88d 100644
--- a/textual.cc
+++ b/textual.cc
@@ -64,7 +64,7 @@ static value_expr parse_amount_expr(std::istream& in, amount_t& amount,
throw new parse_error("Amount expression failed to compute");
#if 0
- if (expr->kind == value_expr_t::CONSTANT) {
+ if (expr->kind == expr::node_t::CONSTANT) {
expr = NULL;
} else {
DEBUG_IF("ledger.textual.parse") {
diff --git a/valexpr.cc b/valexpr.cc
index 85024ddf..28240aeb 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -16,8 +16,8 @@ details_t::details_t(const transaction_t& _xact)
DEBUG("ledger.memory.ctors", "ctor details_t");
}
-bool compute_amount(value_expr_t * expr, amount_t& amt,
- const transaction_t * xact, value_expr_t * context)
+bool compute_amount(ptr_op_t expr, amount_t& amt,
+ const transaction_t * xact, ptr_op_t context)
{
value_t result;
try {
@@ -39,9 +39,9 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
return true;
}
-value_expr_t::~value_expr_t()
+op_t::~op_t()
{
- DEBUG("ledger.memory.dtors", "dtor value_expr_t " << this);
+ DEBUG("ledger.memory.dtors", "dtor op_t " << this);
DEBUG("ledger.valexpr.memory", "Destroying " << this);
assert(refc == 0);
@@ -73,10 +73,10 @@ value_expr_t::~value_expr_t()
}
namespace {
- int count_leaves(value_expr_t * expr)
+ int count_leaves(ptr_op_t expr)
{
int count = 0;
- if (expr->kind != value_expr_t::O_COM) {
+ if (expr->kind != op_t::O_COM) {
count = 1;
} else {
count += count_leaves(expr->left);
@@ -85,40 +85,40 @@ namespace {
return count;
}
- value_expr_t * reduce_leaves(value_expr_t * expr, const details_t& details,
- value_expr_t * context)
+ ptr_op_t reduce_leaves(ptr_op_t expr, const details_t& details,
+ ptr_op_t context)
{
if (! expr)
return NULL;
value_expr temp;
- if (expr->kind != value_expr_t::O_COM) {
- if (expr->kind < value_expr_t::TERMINALS) {
+ if (expr->kind != op_t::O_COM) {
+ if (expr->kind < op_t::TERMINALS) {
temp.reset(expr);
} else {
- temp.reset(new value_expr_t(value_expr_t::CONSTANT));
+ temp.reset(new op_t(op_t::CONSTANT));
temp->value = new value_t;
expr->compute(*(temp->value), details, context);
}
} else {
- temp.reset(new value_expr_t(value_expr_t::O_COM));
+ temp.reset(new op_t(op_t::O_COM));
temp->set_left(reduce_leaves(expr->left, details, context));
temp->set_right(reduce_leaves(expr->right, details, context));
}
return temp.release();
}
- value_expr_t * find_leaf(value_expr_t * context, int goal, int& found)
+ ptr_op_t find_leaf(ptr_op_t context, int goal, int& found)
{
if (! context)
return NULL;
- if (context->kind != value_expr_t::O_COM) {
+ if (context->kind != op_t::O_COM) {
if (goal == found++)
return context;
} else {
- value_expr_t * expr = find_leaf(context->left, goal, found);
+ ptr_op_t expr = find_leaf(context->left, goal, found);
if (expr)
return expr;
expr = find_leaf(context->right, goal, found);
@@ -129,8 +129,8 @@ namespace {
}
}
-void value_expr_t::compute(value_t& result, const details_t& details,
- value_expr_t * context) const
+void op_t::compute(value_t& result, const details_t& details,
+ ptr_op_t context) const
{
try {
switch (kind) {
@@ -342,7 +342,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_PRICE: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result = result.value();
break;
@@ -350,7 +350,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_DATE: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result = result.as_datetime_lval();
break;
@@ -358,7 +358,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_DATECMP: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result = result.as_datetime_lval();
if (! result)
@@ -383,7 +383,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_MONTH:
case F_DAY: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
if (! result.is_type(value_t::DATETIME))
@@ -409,7 +409,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_ARITH_MEAN: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
if (details.xact && transaction_has_xdata(*details.xact)) {
expr->compute(result, details, context);
result /= amount_t(long(transaction_xdata_(*details.xact).index + 1));
@@ -432,7 +432,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_ABS: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result.abs();
break;
@@ -440,7 +440,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_ROUND: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result.round();
break;
@@ -448,7 +448,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_COMMODITY: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
if (! result.is_type(value_t::AMOUNT))
throw new compute_error("Argument to commodity() must be a commoditized amount",
@@ -461,7 +461,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_SET_COMMODITY: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
value_t temp;
expr->compute(temp, details, context);
@@ -482,7 +482,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_QUANTITY: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
balance_t * bal = NULL;
@@ -573,7 +573,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
int arg_index = 0;
assert(left);
assert(left->kind == ARG_INDEX);
- value_expr_t * expr = find_leaf(context, left->arg_index, arg_index);
+ ptr_op_t expr = find_leaf(context, left->arg_index, arg_index);
if (expr)
expr->compute(result, details, context);
else
@@ -609,7 +609,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_VALUE: {
int arg_index = 0;
- value_expr_t * expr = find_leaf(context, 0, arg_index);
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
arg_index = 0;
@@ -747,16 +747,16 @@ static inline void unexpected(char c, char wanted = '\0') {
}
}
-value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
+ptr_op_t parse_value_term(std::istream& in, scope_t * scope,
const short flags);
-inline value_expr_t * parse_value_term(const char * p, scope_t * scope,
+inline ptr_op_t parse_value_term(const char * p, scope_t * scope,
const short flags) {
std::istringstream stream(p);
return parse_value_term(stream, scope, flags);
}
-value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
+ptr_op_t parse_value_term(std::istream& in, scope_t * scope,
const short flags)
{
value_expr node;
@@ -799,7 +799,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
throw err;
}
}
- node.reset(new value_expr_t(value_expr_t::CONSTANT));
+ node.reset(new op_t(op_t::CONSTANT));
node->value = new value_t(temp);
goto parsed;
}
@@ -810,7 +810,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.');
amount_t temp;
temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
- node.reset(new value_expr_t(value_expr_t::CONSTANT));
+ node.reset(new op_t(op_t::CONSTANT));
node->value = new value_t(temp);
goto parsed;
}
@@ -875,8 +875,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
// Define the parameter so that on lookup the parser will find
// an O_ARG value.
- node.reset(new value_expr_t(value_expr_t::O_ARG));
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node.reset(new op_t(op_t::O_ARG));
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = arg_index++;
params->define(ident, node.release());
}
@@ -893,15 +893,15 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
if (! def.get())
throw new value_expr_error(string("Definition failed for '") + buf + "'");
- node.reset(new value_expr_t(value_expr_t::O_DEF));
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node.reset(new op_t(op_t::O_DEF));
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = arg_index;
node->set_right(def.release());
scope->define(buf, node.get());
} else {
assert(scope);
- value_expr_t * def = scope->lookup(buf);
+ ptr_op_t def = scope->lookup(buf);
if (! def) {
if (buf[1] == '\0' &&
(buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' ||
@@ -912,8 +912,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
throw new value_expr_error(string("Unknown identifier '") +
buf + "'");
}
- else if (def->kind == value_expr_t::O_DEF) {
- node.reset(new value_expr_t(value_expr_t::O_REF));
+ else if (def->kind == op_t::O_DEF) {
+ node.reset(new op_t(op_t::O_REF));
node->set_left(def->right);
unsigned int count = 0;
@@ -954,7 +954,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
switch (c) {
// Functions
case '^':
- node.reset(new value_expr_t(value_expr_t::F_PARENT));
+ node.reset(new op_t(op_t::F_PARENT));
node->set_left(parse_value_term(in, scope, flags));
break;
@@ -994,23 +994,23 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
if (c != '/')
unexpected(c, '/');
- value_expr_t::kind_t kind;
+ op_t::kind_t kind;
if (short_account_mask)
- kind = value_expr_t::F_SHORT_ACCOUNT_MASK;
+ kind = op_t::F_SHORT_ACCOUNT_MASK;
else if (code_mask)
- kind = value_expr_t::F_CODE_MASK;
+ kind = op_t::F_CODE_MASK;
else if (commodity_mask)
- kind = value_expr_t::F_COMMODITY_MASK;
+ kind = op_t::F_COMMODITY_MASK;
else if (payee_mask)
- kind = value_expr_t::F_PAYEE_MASK;
+ kind = op_t::F_PAYEE_MASK;
else if (note_mask)
- kind = value_expr_t::F_NOTE_MASK;
+ kind = op_t::F_NOTE_MASK;
else
- kind = value_expr_t::F_ACCOUNT_MASK;
+ kind = op_t::F_ACCOUNT_MASK;
in.get(c);
- node.reset(new value_expr_t(kind));
+ node.reset(new op_t(kind));
node->mask = new mask_t(buf);
break;
}
@@ -1022,7 +1022,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
if (c != '}')
unexpected(c, '}');
- node.reset(new value_expr_t(value_expr_t::CONSTANT));
+ node.reset(new op_t(op_t::CONSTANT));
node->value = new value_t(temp);
break;
}
@@ -1044,7 +1044,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
in.get(c);
interval_t timespan(buf);
- node.reset(new value_expr_t(value_expr_t::CONSTANT));
+ node.reset(new op_t(op_t::CONSTANT));
node->value = new value_t(timespan.first());
break;
}
@@ -1058,7 +1058,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
return node.release();
}
-value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope,
+ptr_op_t parse_mul_expr(std::istream& in, scope_t * scope,
const short flags)
{
value_expr node;
@@ -1066,7 +1066,7 @@ value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope,
if (peek_next_nonws(in) == '%') {
char c;
in.get(c);
- node.reset(new value_expr_t(value_expr_t::O_PERC));
+ node.reset(new op_t(op_t::O_PERC));
node->set_left(parse_value_term(in, scope, flags));
return node.release();
}
@@ -1080,7 +1080,7 @@ value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope,
switch (c) {
case '*': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_MUL));
+ node.reset(new op_t(op_t::O_MUL));
node->set_left(prev.release());
node->set_right(parse_value_term(in, scope, flags));
break;
@@ -1088,7 +1088,7 @@ value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope,
case '/': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_DIV));
+ node.reset(new op_t(op_t::O_DIV));
node->set_left(prev.release());
node->set_right(parse_value_term(in, scope, flags));
break;
@@ -1101,7 +1101,7 @@ value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope,
return node.release();
}
-value_expr_t * parse_add_expr(std::istream& in, scope_t * scope,
+ptr_op_t parse_add_expr(std::istream& in, scope_t * scope,
const short flags)
{
value_expr node;
@@ -1110,11 +1110,11 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope,
char c;
in.get(c);
value_expr expr(parse_mul_expr(in, scope, flags));
- if (expr->kind == value_expr_t::CONSTANT) {
+ if (expr->kind == op_t::CONSTANT) {
expr->value->negate();
return expr.release();
}
- node.reset(new value_expr_t(value_expr_t::O_NEG));
+ node.reset(new op_t(op_t::O_NEG));
node->set_left(expr.release());
return node.release();
}
@@ -1128,7 +1128,7 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope,
switch (c) {
case '+': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_ADD));
+ node.reset(new op_t(op_t::O_ADD));
node->set_left(prev.release());
node->set_right(parse_mul_expr(in, scope, flags));
break;
@@ -1136,7 +1136,7 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope,
case '-': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_SUB));
+ node.reset(new op_t(op_t::O_SUB));
node->set_left(prev.release());
node->set_right(parse_mul_expr(in, scope, flags));
break;
@@ -1149,7 +1149,7 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope,
return node.release();
}
-value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope,
+ptr_op_t parse_logic_expr(std::istream& in, scope_t * scope,
const short flags)
{
value_expr node;
@@ -1157,7 +1157,7 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope,
if (peek_next_nonws(in) == '!') {
char c;
in.get(c);
- node.reset(new value_expr_t(value_expr_t::O_NOT));
+ node.reset(new op_t(op_t::O_NOT));
node->set_left(parse_add_expr(in, scope, flags));
return node.release();
}
@@ -1177,8 +1177,8 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope,
else
unexpected(c, '=');
value_expr prev(node.release());
- node.reset(new value_expr_t(negate ? value_expr_t::O_NEQ :
- value_expr_t::O_EQ));
+ node.reset(new op_t(negate ? op_t::O_NEQ :
+ op_t::O_EQ));
node->set_left(prev.release());
node->set_right(parse_add_expr(in, scope, flags));
break;
@@ -1186,10 +1186,10 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope,
case '<': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_LT));
+ node.reset(new op_t(op_t::O_LT));
if (peek_next_nonws(in) == '=') {
in.get(c);
- node->kind = value_expr_t::O_LTE;
+ node->kind = op_t::O_LTE;
}
node->set_left(prev.release());
node->set_right(parse_add_expr(in, scope, flags));
@@ -1198,10 +1198,10 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope,
case '>': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_GT));
+ node.reset(new op_t(op_t::O_GT));
if (peek_next_nonws(in) == '=') {
in.get(c);
- node->kind = value_expr_t::O_GTE;
+ node->kind = op_t::O_GTE;
}
node->set_left(prev.release());
node->set_right(parse_add_expr(in, scope, flags));
@@ -1219,7 +1219,7 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope,
return node.release();
}
-value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope,
+ptr_op_t parse_boolean_expr(std::istream& in, scope_t * scope,
const short flags)
{
value_expr node(parse_logic_expr(in, scope, flags));
@@ -1231,7 +1231,7 @@ value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope,
switch (c) {
case '&': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_AND));
+ node.reset(new op_t(op_t::O_AND));
node->set_left(prev.release());
node->set_right(parse_logic_expr(in, scope, flags));
break;
@@ -1239,7 +1239,7 @@ value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope,
case '|': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_OR));
+ node.reset(new op_t(op_t::O_OR));
node->set_left(prev.release());
node->set_right(parse_logic_expr(in, scope, flags));
break;
@@ -1247,9 +1247,9 @@ value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope,
case '?': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_QUES));
+ node.reset(new op_t(op_t::O_QUES));
node->set_left(prev.release());
- node->set_right(new value_expr_t(value_expr_t::O_COL));
+ node->set_right(new op_t(op_t::O_COL));
node->right->set_left(parse_logic_expr(in, scope, flags));
c = peek_next_nonws(in);
if (c != ':')
@@ -1276,131 +1276,131 @@ void init_value_expr()
global_scope.reset(new scope_t());
scope_t * globals = global_scope.get();
- value_expr_t * node;
+ ptr_op_t node;
// Basic terms
- node = new value_expr_t(value_expr_t::F_NOW);
+ node = new op_t(op_t::F_NOW);
globals->define("m", node);
globals->define("now", node);
globals->define("today", node);
- node = new value_expr_t(value_expr_t::AMOUNT);
+ node = new op_t(op_t::AMOUNT);
globals->define("a", node);
globals->define("amount", node);
- node = new value_expr_t(value_expr_t::PRICE);
+ node = new op_t(op_t::PRICE);
globals->define("i", node);
globals->define("price", node);
- node = new value_expr_t(value_expr_t::COST);
+ node = new op_t(op_t::COST);
globals->define("b", node);
globals->define("cost", node);
- node = new value_expr_t(value_expr_t::DATE);
+ node = new op_t(op_t::DATE);
globals->define("d", node);
globals->define("date", node);
- node = new value_expr_t(value_expr_t::ACT_DATE);
+ node = new op_t(op_t::ACT_DATE);
globals->define("act_date", node);
globals->define("actual_date", node);
- node = new value_expr_t(value_expr_t::EFF_DATE);
+ node = new op_t(op_t::EFF_DATE);
globals->define("eff_date", node);
globals->define("effective_date", node);
- node = new value_expr_t(value_expr_t::CLEARED);
+ node = new op_t(op_t::CLEARED);
globals->define("X", node);
globals->define("cleared", node);
- node = new value_expr_t(value_expr_t::PENDING);
+ node = new op_t(op_t::PENDING);
globals->define("Y", node);
globals->define("pending", node);
- node = new value_expr_t(value_expr_t::REAL);
+ node = new op_t(op_t::REAL);
globals->define("R", node);
globals->define("real", node);
- node = new value_expr_t(value_expr_t::ACTUAL);
+ node = new op_t(op_t::ACTUAL);
globals->define("L", node);
globals->define("actual", node);
- node = new value_expr_t(value_expr_t::INDEX);
+ node = new op_t(op_t::INDEX);
globals->define("n", node);
globals->define("index", node);
- node = new value_expr_t(value_expr_t::COUNT);
+ node = new op_t(op_t::COUNT);
globals->define("N", node);
globals->define("count", node);
- node = new value_expr_t(value_expr_t::DEPTH);
+ node = new op_t(op_t::DEPTH);
globals->define("l", node);
globals->define("depth", node);
- node = new value_expr_t(value_expr_t::TOTAL);
+ node = new op_t(op_t::TOTAL);
globals->define("O", node);
globals->define("total", node);
- node = new value_expr_t(value_expr_t::PRICE_TOTAL);
+ node = new op_t(op_t::PRICE_TOTAL);
globals->define("I", node);
globals->define("total_price", node);
- node = new value_expr_t(value_expr_t::COST_TOTAL);
+ node = new op_t(op_t::COST_TOTAL);
globals->define("B", node);
globals->define("total_cost", node);
// Relating to format_t
- globals->define("t", new value_expr_t(value_expr_t::VALUE_EXPR));
- globals->define("T", new value_expr_t(value_expr_t::TOTAL_EXPR));
+ globals->define("t", new op_t(op_t::VALUE_EXPR));
+ globals->define("T", new op_t(op_t::TOTAL_EXPR));
// Functions
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_ABS));
+ node->set_right(new op_t(op_t::F_ABS));
globals->define("U", node);
globals->define("abs", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_ROUND));
+ node->set_right(new op_t(op_t::F_ROUND));
globals->define("round", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_QUANTITY));
+ node->set_right(new op_t(op_t::F_QUANTITY));
globals->define("S", node);
globals->define("quant", node);
globals->define("quantity", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_COMMODITY));
+ node->set_right(new op_t(op_t::F_COMMODITY));
globals->define("comm", node);
globals->define("commodity", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 2;
- node->set_right(new value_expr_t(value_expr_t::F_SET_COMMODITY));
+ node->set_right(new op_t(op_t::F_SET_COMMODITY));
globals->define("setcomm", node);
globals->define("set_commodity", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_ARITH_MEAN));
+ node->set_right(new op_t(op_t::F_ARITH_MEAN));
globals->define("A", node);
globals->define("avg", node);
globals->define("mean", node);
globals->define("average", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 2;
- node->set_right(new value_expr_t(value_expr_t::F_VALUE));
+ node->set_right(new op_t(op_t::F_VALUE));
globals->define("P", node);
parse_value_definition("@value=@P(@t,@m)", globals);
@@ -1408,40 +1408,40 @@ void init_value_expr()
parse_value_definition("@valueof(x)=@P(@x,@m)", globals);
parse_value_definition("@datedvalueof(x,y)=@P(@x,@y)", globals);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_PRICE));
+ node->set_right(new op_t(op_t::F_PRICE));
globals->define("priceof", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_DATE));
+ node->set_right(new op_t(op_t::F_DATE));
globals->define("dateof", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 2;
- node->set_right(new value_expr_t(value_expr_t::F_DATECMP));
+ node->set_right(new op_t(op_t::F_DATECMP));
globals->define("datecmp", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_YEAR));
+ node->set_right(new op_t(op_t::F_YEAR));
globals->define("yearof", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_MONTH));
+ node->set_right(new op_t(op_t::F_MONTH));
globals->define("monthof", node);
- node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node = new op_t(op_t::O_DEF);
+ node->set_left(new op_t(op_t::ARG_INDEX));
node->left->arg_index = 1;
- node->set_right(new value_expr_t(value_expr_t::F_DAY));
+ node->set_right(new op_t(op_t::F_DAY));
globals->define("dayof", node);
parse_value_definition("@year=@yearof(@d)", globals);
@@ -1469,7 +1469,7 @@ void init_value_expr()
parse_value_definition("@max(x,y)=@x>@y?@x:@y", globals);
}
-value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
+ptr_op_t parse_value_expr(std::istream& in, scope_t * scope,
const short flags)
{
if (! global_scope.get())
@@ -1487,7 +1487,7 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
switch (c) {
case ',': {
value_expr prev(node.release());
- node.reset(new value_expr_t(value_expr_t::O_COM));
+ node.reset(new op_t(op_t::O_COM));
node->set_left(prev.release());
node->set_right(parse_logic_expr(in, this_scope.get(), flags));
break;
@@ -1520,7 +1520,7 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
return node.release();
}
-valexpr_context::valexpr_context(const ledger::value_expr_t * _expr,
+valexpr_context::valexpr_context(const ledger::ptr_op_t _expr,
const string& desc) throw()
: error_context(desc), expr(_expr), error_node(_expr)
{
@@ -1563,15 +1563,15 @@ void valexpr_context::describe(std::ostream& out) const throw()
}
bool print_value_expr(std::ostream& out,
- const value_expr_t * node,
+ const ptr_op_t node,
const bool relaxed,
- const value_expr_t * node_to_find,
+ const ptr_op_t op_to_find,
unsigned long * start_pos,
unsigned long * end_pos)
{
bool found = false;
- if (start_pos && node == node_to_find) {
+ if (start_pos && node == op_to_find) {
*start_pos = (long)out.tellp() - 1;
found = true;
}
@@ -1579,11 +1579,11 @@ bool print_value_expr(std::ostream& out,
string symbol;
switch (node->kind) {
- case value_expr_t::ARG_INDEX:
+ case op_t::ARG_INDEX:
out << node->arg_index;
break;
- case value_expr_t::CONSTANT:
+ case op_t::CONSTANT:
switch (node->value->type()) {
case value_t::BOOLEAN:
assert(false);
@@ -1607,274 +1607,274 @@ bool print_value_expr(std::ostream& out,
}
break;
- case value_expr_t::AMOUNT:
+ case op_t::AMOUNT:
symbol = "amount"; break;
- case value_expr_t::PRICE:
+ case op_t::PRICE:
symbol = "price"; break;
- case value_expr_t::COST:
+ case op_t::COST:
symbol = "cost"; break;
- case value_expr_t::DATE:
+ case op_t::DATE:
symbol = "date"; break;
- case value_expr_t::ACT_DATE:
+ case op_t::ACT_DATE:
symbol = "actual_date"; break;
- case value_expr_t::EFF_DATE:
+ case op_t::EFF_DATE:
symbol = "effective_date"; break;
- case value_expr_t::CLEARED:
+ case op_t::CLEARED:
symbol = "cleared"; break;
- case value_expr_t::PENDING:
+ case op_t::PENDING:
symbol = "pending"; break;
- case value_expr_t::REAL:
+ case op_t::REAL:
symbol = "real"; break;
- case value_expr_t::ACTUAL:
+ case op_t::ACTUAL:
symbol = "actual"; break;
- case value_expr_t::INDEX:
+ case op_t::INDEX:
symbol = "index"; break;
- case value_expr_t::COUNT:
+ case op_t::COUNT:
symbol = "count"; break;
- case value_expr_t::DEPTH:
+ case op_t::DEPTH:
symbol = "depth"; break;
- case value_expr_t::TOTAL:
+ case op_t::TOTAL:
symbol = "total"; break;
- case value_expr_t::PRICE_TOTAL:
+ case op_t::PRICE_TOTAL:
symbol = "total_price"; break;
- case value_expr_t::COST_TOTAL:
+ case op_t::COST_TOTAL:
symbol = "total_cost"; break;
- case value_expr_t::F_NOW:
+ case op_t::F_NOW:
symbol = "now"; break;
- case value_expr_t::VALUE_EXPR:
+ case op_t::VALUE_EXPR:
if (print_value_expr(out, amount_expr.get(), relaxed,
- node_to_find, start_pos, end_pos))
+ op_to_find, start_pos, end_pos))
found = true;
break;
- case value_expr_t::TOTAL_EXPR:
+ case op_t::TOTAL_EXPR:
if (print_value_expr(out, total_expr.get(), relaxed,
- node_to_find, start_pos, end_pos))
+ op_to_find, start_pos, end_pos))
found = true;
break;
- case value_expr_t::F_ARITH_MEAN:
+ case op_t::F_ARITH_MEAN:
symbol = "average"; break;
- case value_expr_t::F_ABS:
+ case op_t::F_ABS:
symbol = "abs"; break;
- case value_expr_t::F_QUANTITY:
+ case op_t::F_QUANTITY:
symbol = "quantity"; break;
- case value_expr_t::F_COMMODITY:
+ case op_t::F_COMMODITY:
symbol = "commodity"; break;
- case value_expr_t::F_SET_COMMODITY:
+ case op_t::F_SET_COMMODITY:
symbol = "set_commodity"; break;
- case value_expr_t::F_VALUE:
+ case op_t::F_VALUE:
symbol = "valueof"; break;
- case value_expr_t::F_PRICE:
+ case op_t::F_PRICE:
symbol = "priceof"; break;
- case value_expr_t::F_DATE:
+ case op_t::F_DATE:
symbol = "dateof"; break;
- case value_expr_t::F_DATECMP:
+ case op_t::F_DATECMP:
symbol = "datecmp"; break;
- case value_expr_t::F_YEAR:
+ case op_t::F_YEAR:
symbol = "yearof"; break;
- case value_expr_t::F_MONTH:
+ case op_t::F_MONTH:
symbol = "monthof"; break;
- case value_expr_t::F_DAY:
+ case op_t::F_DAY:
symbol = "dayof"; break;
- case value_expr_t::F_CODE_MASK:
+ case op_t::F_CODE_MASK:
out << "c/" << node->mask->expr.str() << "/";
break;
- case value_expr_t::F_PAYEE_MASK:
+ case op_t::F_PAYEE_MASK:
out << "p/" << node->mask->expr.str() << "/";
break;
- case value_expr_t::F_NOTE_MASK:
+ case op_t::F_NOTE_MASK:
out << "e/" << node->mask->expr.str() << "/";
break;
- case value_expr_t::F_ACCOUNT_MASK:
+ case op_t::F_ACCOUNT_MASK:
out << "W/" << node->mask->expr.str() << "/";
break;
- case value_expr_t::F_SHORT_ACCOUNT_MASK:
+ case op_t::F_SHORT_ACCOUNT_MASK:
out << "w/" << node->mask->expr.str() << "/";
break;
- case value_expr_t::F_COMMODITY_MASK:
+ case op_t::F_COMMODITY_MASK:
out << "C/" << node->mask->expr.str() << "/";
break;
- case value_expr_t::O_NOT:
+ case op_t::O_NOT:
out << "!";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
- case value_expr_t::O_NEG:
+ case op_t::O_NEG:
out << "-";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
- case value_expr_t::O_PERC:
+ case op_t::O_PERC:
out << "%";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
- case value_expr_t::O_ARG:
+ case op_t::O_ARG:
out << "@arg" << node->arg_index;
break;
- case value_expr_t::O_DEF:
+ case op_t::O_DEF:
out << "<def args=\"";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << "\" value=\"";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << "\">";
break;
- case value_expr_t::O_REF:
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ case op_t::O_REF:
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
if (node->right) {
out << "(";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
}
break;
- case value_expr_t::O_COM:
+ case op_t::O_COM:
if (node->left &&
- print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ", ";
if (node->right &&
- print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
- case value_expr_t::O_QUES:
+ case op_t::O_QUES:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " ? ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_COL:
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ case op_t::O_COL:
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " : ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
- case value_expr_t::O_AND:
+ case op_t::O_AND:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " & ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_OR:
+ case op_t::O_OR:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " | ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_NEQ:
+ case op_t::O_NEQ:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " != ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_EQ:
+ case op_t::O_EQ:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " == ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_LT:
+ case op_t::O_LT:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " < ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_LTE:
+ case op_t::O_LTE:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " <= ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_GT:
+ case op_t::O_GT:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " > ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_GTE:
+ case op_t::O_GTE:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " >= ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_ADD:
+ case op_t::O_ADD:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " + ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_SUB:
+ case op_t::O_SUB:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " - ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_MUL:
+ case op_t::O_MUL:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " * ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_DIV:
+ case op_t::O_DIV:
out << "(";
- if (print_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->left, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " / ";
- if (print_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ if (print_value_expr(out, node->right, relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::LAST:
+ case op_t::LAST:
default:
assert(false);
break;
@@ -1886,13 +1886,13 @@ bool print_value_expr(std::ostream& out,
out << symbol;
}
- if (end_pos && node == node_to_find)
+ if (end_pos && node == op_to_find)
*end_pos = (long)out.tellp() - 1;
return found;
}
-void dump_value_expr(std::ostream& out, const value_expr_t * node,
+void dump_value_expr(std::ostream& out, const ptr_op_t node,
const int depth)
{
out.setf(std::ios::left);
@@ -1903,79 +1903,79 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
out << " ";
switch (node->kind) {
- case value_expr_t::ARG_INDEX:
+ case op_t::ARG_INDEX:
out << "ARG_INDEX - " << node->arg_index;
break;
- case value_expr_t::CONSTANT:
+ case op_t::CONSTANT:
out << "CONSTANT - " << *(node->value);
break;
- case value_expr_t::AMOUNT: out << "AMOUNT"; break;
- case value_expr_t::PRICE: out << "PRICE"; break;
- case value_expr_t::COST: out << "COST"; break;
- case value_expr_t::DATE: out << "DATE"; break;
- case value_expr_t::ACT_DATE: out << "ACT_DATE"; break;
- case value_expr_t::EFF_DATE: out << "EFF_DATE"; break;
- case value_expr_t::CLEARED: out << "CLEARED"; break;
- case value_expr_t::PENDING: out << "PENDING"; break;
- case value_expr_t::REAL: out << "REAL"; break;
- case value_expr_t::ACTUAL: out << "ACTUAL"; break;
- case value_expr_t::INDEX: out << "INDEX"; break;
- case value_expr_t::COUNT: out << "COUNT"; break;
- case value_expr_t::DEPTH: out << "DEPTH"; break;
- case value_expr_t::TOTAL: out << "TOTAL"; break;
- case value_expr_t::PRICE_TOTAL: out << "PRICE_TOTAL"; break;
- case value_expr_t::COST_TOTAL: out << "COST_TOTAL"; break;
-
- case value_expr_t::VALUE_EXPR: out << "VALUE_EXPR"; break;
- case value_expr_t::TOTAL_EXPR: out << "TOTAL_EXPR"; break;
-
- case value_expr_t::F_NOW: out << "F_NOW"; break;
- case value_expr_t::F_ARITH_MEAN: out << "F_ARITH_MEAN"; break;
- case value_expr_t::F_ABS: out << "F_ABS"; break;
- case value_expr_t::F_QUANTITY: out << "F_QUANTITY"; break;
- case value_expr_t::F_COMMODITY: out << "F_COMMODITY"; break;
- case value_expr_t::F_SET_COMMODITY: out << "F_SET_COMMODITY"; break;
- case value_expr_t::F_CODE_MASK: out << "F_CODE_MASK"; break;
- case value_expr_t::F_PAYEE_MASK: out << "F_PAYEE_MASK"; break;
- case value_expr_t::F_NOTE_MASK: out << "F_NOTE_MASK"; break;
- case value_expr_t::F_ACCOUNT_MASK:
+ case op_t::AMOUNT: out << "AMOUNT"; break;
+ case op_t::PRICE: out << "PRICE"; break;
+ case op_t::COST: out << "COST"; break;
+ case op_t::DATE: out << "DATE"; break;
+ case op_t::ACT_DATE: out << "ACT_DATE"; break;
+ case op_t::EFF_DATE: out << "EFF_DATE"; break;
+ case op_t::CLEARED: out << "CLEARED"; break;
+ case op_t::PENDING: out << "PENDING"; break;
+ case op_t::REAL: out << "REAL"; break;
+ case op_t::ACTUAL: out << "ACTUAL"; break;
+ case op_t::INDEX: out << "INDEX"; break;
+ case op_t::COUNT: out << "COUNT"; break;
+ case op_t::DEPTH: out << "DEPTH"; break;
+ case op_t::TOTAL: out << "TOTAL"; break;
+ case op_t::PRICE_TOTAL: out << "PRICE_TOTAL"; break;
+ case op_t::COST_TOTAL: out << "COST_TOTAL"; break;
+
+ case op_t::VALUE_EXPR: out << "VALUE_EXPR"; break;
+ case op_t::TOTAL_EXPR: out << "TOTAL_EXPR"; break;
+
+ case op_t::F_NOW: out << "F_NOW"; break;
+ case op_t::F_ARITH_MEAN: out << "F_ARITH_MEAN"; break;
+ case op_t::F_ABS: out << "F_ABS"; break;
+ case op_t::F_QUANTITY: out << "F_QUANTITY"; break;
+ case op_t::F_COMMODITY: out << "F_COMMODITY"; break;
+ case op_t::F_SET_COMMODITY: out << "F_SET_COMMODITY"; break;
+ case op_t::F_CODE_MASK: out << "F_CODE_MASK"; break;
+ case op_t::F_PAYEE_MASK: out << "F_PAYEE_MASK"; break;
+ case op_t::F_NOTE_MASK: out << "F_NOTE_MASK"; break;
+ case op_t::F_ACCOUNT_MASK:
out << "F_ACCOUNT_MASK"; break;
- case value_expr_t::F_SHORT_ACCOUNT_MASK:
+ case op_t::F_SHORT_ACCOUNT_MASK:
out << "F_SHORT_ACCOUNT_MASK"; break;
- case value_expr_t::F_COMMODITY_MASK:
+ case op_t::F_COMMODITY_MASK:
out << "F_COMMODITY_MASK"; break;
- case value_expr_t::F_VALUE: out << "F_VALUE"; break;
- case value_expr_t::F_PRICE: out << "F_PRICE"; break;
- case value_expr_t::F_DATE: out << "F_DATE"; break;
- case value_expr_t::F_DATECMP: out << "F_DATECMP"; break;
- case value_expr_t::F_YEAR: out << "F_YEAR"; break;
- case value_expr_t::F_MONTH: out << "F_MONTH"; break;
- case value_expr_t::F_DAY: out << "F_DAY"; break;
-
- case value_expr_t::O_NOT: out << "O_NOT"; break;
- case value_expr_t::O_ARG: out << "O_ARG"; break;
- case value_expr_t::O_DEF: out << "O_DEF"; break;
- case value_expr_t::O_REF: out << "O_REF"; break;
- case value_expr_t::O_COM: out << "O_COM"; break;
- case value_expr_t::O_QUES: out << "O_QUES"; break;
- case value_expr_t::O_COL: out << "O_COL"; break;
- case value_expr_t::O_AND: out << "O_AND"; break;
- case value_expr_t::O_OR: out << "O_OR"; break;
- case value_expr_t::O_NEQ: out << "O_NEQ"; break;
- case value_expr_t::O_EQ: out << "O_EQ"; break;
- case value_expr_t::O_LT: out << "O_LT"; break;
- case value_expr_t::O_LTE: out << "O_LTE"; break;
- case value_expr_t::O_GT: out << "O_GT"; break;
- case value_expr_t::O_GTE: out << "O_GTE"; break;
- case value_expr_t::O_NEG: out << "O_NEG"; break;
- case value_expr_t::O_ADD: out << "O_ADD"; break;
- case value_expr_t::O_SUB: out << "O_SUB"; break;
- case value_expr_t::O_MUL: out << "O_MUL"; break;
- case value_expr_t::O_DIV: out << "O_DIV"; break;
- case value_expr_t::O_PERC: out << "O_PERC"; break;
-
- case value_expr_t::LAST:
+ case op_t::F_VALUE: out << "F_VALUE"; break;
+ case op_t::F_PRICE: out << "F_PRICE"; break;
+ case op_t::F_DATE: out << "F_DATE"; break;
+ case op_t::F_DATECMP: out << "F_DATECMP"; break;
+ case op_t::F_YEAR: out << "F_YEAR"; break;
+ case op_t::F_MONTH: out << "F_MONTH"; break;
+ case op_t::F_DAY: out << "F_DAY"; break;
+
+ case op_t::O_NOT: out << "O_NOT"; break;
+ case op_t::O_ARG: out << "O_ARG"; break;
+ case op_t::O_DEF: out << "O_DEF"; break;
+ case op_t::O_REF: out << "O_REF"; break;
+ case op_t::O_COM: out << "O_COM"; break;
+ case op_t::O_QUES: out << "O_QUES"; break;
+ case op_t::O_COL: out << "O_COL"; break;
+ case op_t::O_AND: out << "O_AND"; break;
+ case op_t::O_OR: out << "O_OR"; break;
+ case op_t::O_NEQ: out << "O_NEQ"; break;
+ case op_t::O_EQ: out << "O_EQ"; break;
+ case op_t::O_LT: out << "O_LT"; break;
+ case op_t::O_LTE: out << "O_LTE"; break;
+ case op_t::O_GT: out << "O_GT"; break;
+ case op_t::O_GTE: out << "O_GTE"; break;
+ case op_t::O_NEG: out << "O_NEG"; break;
+ case op_t::O_ADD: out << "O_ADD"; break;
+ case op_t::O_SUB: out << "O_SUB"; break;
+ case op_t::O_MUL: out << "O_MUL"; break;
+ case op_t::O_DIV: out << "O_DIV"; break;
+ case op_t::O_PERC: out << "O_PERC"; break;
+
+ case op_t::LAST:
default:
assert(false);
break;
@@ -1983,7 +1983,7 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
out << " (" << node->refc << ')' << std::endl;
- if (node->kind > value_expr_t::TERMINALS) {
+ if (node->kind > op_t::TERMINALS) {
if (node->left) {
dump_value_expr(out, node->left, depth + 1);
if (node->right)
diff --git a/valexpr.h b/valexpr.h
index ce1af8f3..cbb232d0 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -13,9 +13,59 @@ class entry_t;
class transaction_t;
class account_t;
+namespace expr {
+
+#if 0
+struct context_t
+{
+ const entry_t * entry() {
+ return NULL;
+ }
+ const transaction_t * xact() {
+ return NULL;
+ }
+ const account_t * account() {
+ return NULL;
+ }
+};
+
+struct entry_context_t : public context_t
+{
+ const entry_t * entry_;
+
+ const entry_t * entry() {
+ return entry_;
+ }
+};
+
+struct xact_context_t : public context_t
+{
+ const transaction_t * xact_;
+
+ const entry_t * entry() {
+ return xact_->entry;
+ }
+ const transaction_t * xact() {
+ return xact_;
+ }
+ const account_t * account() {
+ return xact_->account;
+ }
+};
+
+struct account_context_t : public context_t
+{
+ const account_t * account_;
+
+ const account_t * account() {
+ return account_;
+ }
+};
+#endif
+
struct details_t
{
- const entry_t * entry;
+ const entry_t * entry;
const transaction_t * xact;
const account_t * account;
@@ -36,11 +86,217 @@ struct details_t
#endif
};
-struct value_expr_t
+struct op_t;
+typedef intrusive_ptr<op_t> ptr_op_t;
+
+class call_scope_t;
+
+typedef function<value_t (call_scope_t&)> function_t;
+
+#define MAKE_FUNCTOR(x) expr::op_t::wrap_functor(bind(&x, this, _1))
+#define WRAP_FUNCTOR(x) expr::op_t::wrap_functor(x)
+
+class scope_t : public noncopyable
+{
+public:
+ enum type_t {
+ CHILD_SCOPE,
+ SYMBOL_SCOPE,
+ CALL_SCOPE,
+ CONTEXT_SCOPE
+ } type_;
+
+ explicit scope_t(type_t _type) : type_(_type) {
+ TRACE_CTOR(expr::scope_t, "type_t");
+ }
+ virtual ~scope_t() {
+ TRACE_DTOR(expr::scope_t);
+ }
+
+ const type_t type() const {
+ return type_;
+ }
+
+ virtual void define(const string& name, ptr_op_t def) = 0;
+ void define(const string& name, const value_t& val);
+ virtual ptr_op_t lookup(const string& name) = 0;
+ value_t resolve(const string& name) {
+#if 0
+ return lookup(name)->calc(*this);
+#else
+ return value_t();
+#endif
+ }
+
+ virtual optional<scope_t&> find_scope(const type_t _type,
+ bool skip_this = false) = 0;
+ virtual optional<scope_t&> find_first_scope(const type_t _type1,
+ const type_t _type2,
+ bool skip_this = false) = 0;
+
+ template <typename T>
+ T& find_scope(bool skip_this = false) {
+ assert(false);
+ }
+ template <typename T>
+ optional<T&> maybe_find_scope(bool skip_this = false) {
+ assert(false);
+ }
+};
+
+class child_scope_t : public scope_t
+{
+ scope_t * parent;
+
+public:
+ explicit child_scope_t(type_t _type = CHILD_SCOPE)
+ : scope_t(_type), parent(NULL) {
+ TRACE_CTOR(expr::child_scope_t, "type_t");
+ }
+ explicit child_scope_t(scope_t& _parent, type_t _type = CHILD_SCOPE)
+ : scope_t(_type), parent(&_parent) {
+ TRACE_CTOR(expr::child_scope_t, "scope_t&, type_t");
+ }
+ virtual ~child_scope_t() {
+ TRACE_DTOR(expr::child_scope_t);
+ }
+public:
+ virtual void define(const string& name, ptr_op_t def) {
+ if (parent)
+ parent->define(name, def);
+ }
+ virtual ptr_op_t lookup(const string& name) {
+ if (parent)
+ return parent->lookup(name);
+ return ptr_op_t();
+ }
+
+ virtual optional<scope_t&> find_scope(type_t _type,
+ bool skip_this = false) {
+ for (scope_t * ptr = (skip_this ? parent : this); ptr; ) {
+ if (ptr->type() == _type)
+ return *ptr;
+
+ ptr = polymorphic_downcast<child_scope_t *>(ptr)->parent;
+ }
+ return none;
+ }
+
+ virtual optional<scope_t&> find_first_scope(const type_t _type1,
+ const type_t _type2,
+ bool skip_this = false) {
+ for (scope_t * ptr = (skip_this ? parent : this); ptr; ) {
+ if (ptr->type() == _type1 || ptr->type() == _type2)
+ return *ptr;
+
+ ptr = polymorphic_downcast<child_scope_t *>(ptr)->parent;
+ }
+ return none;
+ }
+};
+
+class symbol_scope_t : public child_scope_t
+{
+ typedef std::map<const string, ptr_op_t> symbol_map;
+ symbol_map symbols;
+
+public:
+ explicit symbol_scope_t()
+ : child_scope_t(SYMBOL_SCOPE) {
+ TRACE_CTOR(expr::symbol_scope_t, "");
+ }
+ explicit symbol_scope_t(scope_t& _parent)
+ : child_scope_t(_parent, SYMBOL_SCOPE) {
+ TRACE_CTOR(expr::symbol_scope_t, "scope_t&");
+ }
+ virtual ~symbol_scope_t() {
+ TRACE_DTOR(expr::symbol_scope_t);
+ }
+
+ virtual void define(const string& name, ptr_op_t def);
+ void define(const string& name, const value_t& val) {
+ scope_t::define(name, val);
+ }
+ virtual ptr_op_t lookup(const string& name);
+};
+
+class call_scope_t : public child_scope_t
+{
+ value_t args;
+
+public:
+ explicit call_scope_t(scope_t& _parent)
+ : child_scope_t(_parent, CALL_SCOPE) {
+ TRACE_CTOR(expr::call_scope_t, "scope_t&");
+ }
+ virtual ~call_scope_t() {
+ TRACE_DTOR(expr::call_scope_t);
+ }
+
+ void set_args(const value_t& _args) {
+ args = _args;
+ }
+
+ value_t& value() {
+ return args;
+ }
+
+ value_t& operator[](const int index) {
+ return args[index];
+ }
+ const value_t& operator[](const int index) const {
+ return args[index];
+ }
+
+ void push_back(const value_t& val) {
+ args.push_back(val);
+ }
+ void pop_back() {
+ args.pop_back();
+ }
+
+ const std::size_t size() const {
+ return args.size();
+ }
+};
+
+class context_scope_t : public child_scope_t
+{
+public:
+ value_t current_element;
+ std::size_t element_index;
+ std::size_t sequence_size;
+
+ explicit context_scope_t(scope_t& _parent,
+ const value_t& _element = NULL_VALUE,
+ const std::size_t _element_index = 0,
+ const std::size_t _sequence_size = 0)
+ : child_scope_t(_parent, CONTEXT_SCOPE), current_element(_element),
+ element_index(_element_index), sequence_size(_sequence_size)
+ {
+ TRACE_CTOR(expr::context_scope_t, "scope_t&, const value_t&, ...");
+ }
+ virtual ~context_scope_t() {
+ TRACE_DTOR(expr::context_scope_t);
+ }
+
+ const std::size_t index() const {
+ return element_index;
+ }
+ const std::size_t size() const {
+ return sequence_size;
+ }
+
+ value_t& value() {
+ return current_element;
+ }
+};
+
+struct op_t : public noncopyable
{
enum kind_t {
// Constants
- CONSTANT,
+ VALUE,
ARG_INDEX,
CONSTANTS,
@@ -70,6 +326,8 @@ struct value_expr_t
TOTAL_EXPR,
// Functions
+ FUNCTION,
+
F_NOW,
F_ARITH_MEAN,
F_QUANTITY,
@@ -121,82 +379,231 @@ struct value_expr_t
LAST
};
- kind_t kind;
- mutable short refc;
- value_expr_t * left;
+ kind_t kind;
+ mutable short refc;
+ ptr_op_t left_;
- union {
- value_t * value;
- mask_t * mask;
- unsigned int arg_index; // used by ARG_INDEX and O_ARG
- value_expr_t * right;
- };
+ variant<unsigned int, // used by ARG_INDEX and O_ARG
+ value_t, // used by constant VALUE
+ mask_t, // used by constant MASK
+ function_t, // used by terminal FUNCTION
+#if 0
+ node_t::nameid_t, // used by NODE_ID and ATTR_ID
+#endif
+ ptr_op_t> // used by all binary operators
+ data;
- value_expr_t(const kind_t _kind)
- : kind(_kind), refc(0), left(NULL), right(NULL) {
- DEBUG("ledger.memory.ctors", "ctor value_expr_t " << this);
+ explicit op_t(const kind_t _kind) : kind(_kind), refc(0){
+ TRACE_CTOR(expr::op_t, "const kind_t");
}
- ~value_expr_t();
+ ~op_t() {
+ TRACE_DTOR(expr::op_t);
- void release() const {
- DEBUG("ledger.valexpr.memory",
- "Releasing " << this << ", refc now " << refc - 1);
- assert(refc > 0);
- if (--refc == 0)
- delete this;
+ DEBUG("ledger.xpath.memory", "Destroying " << this);
+ assert(refc == 0);
+ }
+
+ bool is_long() const {
+ return data.type() == typeid(unsigned int);
+ }
+ unsigned int& as_long() {
+ assert(kind == ARG_INDEX || kind == O_ARG);
+ return boost::get<unsigned int>(data);
}
- value_expr_t * acquire() {
- DEBUG("ledger.valexpr.memory",
- "Acquiring " << this << ", refc now " << refc + 1);
+ const unsigned int& as_long() const {
+ return const_cast<op_t *>(this)->as_long();
+ }
+ void set_long(unsigned int val) {
+ data = val;
+ }
+
+ bool is_value() const {
+ if (kind == VALUE) {
+ assert(data.type() == typeid(value_t));
+ return true;
+ }
+ return false;
+ }
+ value_t& as_value() {
+ assert(is_value());
+ return boost::get<value_t>(data);
+ }
+ const value_t& as_value() const {
+ return const_cast<op_t *>(this)->as_value();
+ }
+ void set_value(const value_t& val) {
+ data = val;
+ }
+
+ bool is_string() const {
+ if (kind == VALUE) {
+ assert(data.type() == typeid(value_t));
+ return boost::get<value_t>(data).is_string();
+ }
+ return false;
+ }
+ string& as_string() {
+ assert(is_string());
+ return boost::get<value_t>(data).as_string_lval();
+ }
+ const string& as_string() const {
+ return const_cast<op_t *>(this)->as_string();
+ }
+ void set_string(const string& val) {
+ data = value_t(val);
+ }
+
+ bool is_function() const {
+ return kind == FUNCTION;
+ }
+ function_t& as_function() {
+ assert(kind == FUNCTION);
+ return boost::get<function_t>(data);
+ }
+ const function_t& as_function() const {
+ return const_cast<op_t *>(this)->as_function();
+ }
+ void set_function(const function_t& val) {
+ data = val;
+ }
+
+#if 0
+ bool is_name() const {
+ return data.type() == typeid(node_t::nameid_t);
+ }
+ node_t::nameid_t& as_name() {
+ assert(kind == NODE_ID || kind == ATTR_ID);
+ return boost::get<node_t::nameid_t>(data);
+ }
+ const node_t::nameid_t& as_name() const {
+ return const_cast<op_t *>(this)->as_name();
+ }
+ void set_name(const node_t::nameid_t& val) {
+ data = val;
+ }
+#endif
+
+ ptr_op_t& as_op() {
+ assert(kind > TERMINALS);
+ return boost::get<ptr_op_t>(data);
+ }
+ const ptr_op_t& as_op() const {
+ return const_cast<op_t *>(this)->as_op();
+ }
+
+ void acquire() const {
+ DEBUG("ledger.xpath.memory",
+ "Acquiring " << this << ", refc now " << refc + 1);
assert(refc >= 0);
refc++;
- return this;
}
- const value_expr_t * acquire() const {
- DEBUG("ledger.valexpr.memory",
- "Acquiring " << this << ", refc now " << refc + 1);
- refc++;
- return this;
+ void release() const {
+ DEBUG("ledger.xpath.memory",
+ "Releasing " << this << ", refc now " << refc - 1);
+ assert(refc > 0);
+ if (--refc == 0)
+ checked_delete(this);
}
- void set_left(value_expr_t * expr) {
+ ptr_op_t& left() {
+ return left_;
+ }
+ const ptr_op_t& left() const {
+ assert(kind > TERMINALS);
+ return left_;
+ }
+ void set_left(const ptr_op_t& expr) {
assert(kind > TERMINALS);
- if (left)
- left->release();
- left = expr ? expr->acquire() : NULL;
+ left_ = expr;
}
- void set_right(value_expr_t * expr) {
+ ptr_op_t& right() {
+ assert(kind > TERMINALS);
+ return as_op();
+ }
+ const ptr_op_t& right() const {
+ assert(kind > TERMINALS);
+ return as_op();
+ }
+ void set_right(const ptr_op_t& expr) {
assert(kind > TERMINALS);
- if (right)
- right->release();
- right = expr ? expr->acquire() : NULL;
+ data = expr;
}
+ static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL,
+ ptr_op_t _right = NULL);
+ ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const {
+ return new_node(kind, _left, _right);
+ }
+
+ static ptr_op_t wrap_value(const value_t& val);
+ static ptr_op_t wrap_functor(const function_t& fobj);
+
+ ptr_op_t compile(scope_t& scope);
+ value_t current_value(scope_t& scope);
+#if 0
+ node_t& current_xml_node(scope_t& scope);
+#endif
+ value_t calc(scope_t& scope);
+
void compute(value_t& result,
const details_t& details = details_t(),
- value_expr_t * context = NULL) const;
+ ptr_op_t context = NULL) const;
value_t compute(const details_t& details = details_t(),
- value_expr_t * context = NULL) const {
+ ptr_op_t context = NULL) const {
value_t temp;
compute(temp, details, context);
return temp;
}
- private:
- value_expr_t(const value_expr_t&) {
- DEBUG("ledger.memory.ctors", "ctor value_expr_t (copy) " << this);
+ struct print_context_t
+ {
+ scope_t& scope;
+ const bool relaxed;
+ const ptr_op_t& op_to_find;
+ unsigned long * start_pos;
+ unsigned long * end_pos;
+
+ print_context_t(scope_t& _scope,
+ const bool _relaxed = false,
+ const ptr_op_t& _op_to_find = ptr_op_t(),
+ unsigned long * _start_pos = NULL,
+ unsigned long * _end_pos = NULL)
+ : scope(_scope), relaxed(_relaxed), op_to_find(_op_to_find),
+ start_pos(_start_pos), end_pos(_end_pos) {}
+ };
+
+ bool print(std::ostream& out, print_context_t& context) const;
+ void dump(std::ostream& out, const int depth) const;
+
+ friend inline void intrusive_ptr_add_ref(op_t * op) {
+ op->acquire();
+ }
+ friend inline void intrusive_ptr_release(op_t * op) {
+ op->release();
+ }
+};
+
+#if 0
+class op_predicate {
+ ptr_op_t op;
+
+public:
+ explicit op_predicate(ptr_op_t _op) : op(_op) {}
+ bool operator()(scope_t& scope) {
+ return op->calc(scope).to_boolean();
}
};
+#endif
class valexpr_context : public error_context {
public:
- const ledger::value_expr_t * expr;
- const ledger::value_expr_t * error_node;
+ ptr_op_t expr;
+ ptr_op_t error_node;
- valexpr_context(const ledger::value_expr_t * _expr,
- const string& desc = "") throw();
+ valexpr_context(const ptr_op_t _expr,
+ const string& desc = "") throw();
virtual ~valexpr_context() throw();
virtual void describe(std::ostream& out) const throw();
@@ -217,53 +624,6 @@ class value_expr_error : public error {
virtual ~value_expr_error() throw() {}
};
-struct scope_t
-{
- scope_t * parent;
-
- typedef std::map<const string, value_expr_t *> symbol_map;
- typedef std::pair<const string, value_expr_t *> symbol_pair;
-
- symbol_map symbols;
-
- scope_t(scope_t * _parent = NULL) : parent(_parent) {
- DEBUG("ledger.memory.ctors", "ctor scope_t");
- }
- ~scope_t() {
- DEBUG("ledger.memory.dtors", "dtor scope_t");
- for (symbol_map::iterator i = symbols.begin();
- i != symbols.end();
- i++)
- (*i).second->release();
- }
-
- void define(const string& name, value_expr_t * def) {
- DEBUG("ledger.valexpr.syms",
- "Defining '" << name << "' = " << def);
- std::pair<symbol_map::iterator, bool> result
- = symbols.insert(symbol_pair(name, def));
- if (! result.second) {
- symbols.erase(name);
- std::pair<symbol_map::iterator, bool> result
- = symbols.insert(symbol_pair(name, def));
- if (! result.second) {
- def->release();
- throw new compute_error(string("Redefinition of '") +
- name + "' in same scope");
- }
- }
- def->acquire();
- }
- value_expr_t * lookup(const string& name) {
- symbol_map::const_iterator i = symbols.find(name);
- if (i != symbols.end())
- return (*i).second;
- else if (parent)
- return parent->lookup(name);
- return NULL;
- }
-};
-
extern std::auto_ptr<scope_t> global_scope;
extern datetime_t terminus;
@@ -271,9 +631,9 @@ extern bool initialized;
void init_value_expr();
-bool compute_amount(value_expr_t * expr, amount_t& amt,
+bool compute_amount(const ptr_op_t expr, amount_t& amt,
const transaction_t * xact,
- value_expr_t * context = NULL);
+ const ptr_op_t context = NULL);
#define PARSE_VALEXPR_NORMAL 0x00
#define PARSE_VALEXPR_PARTIAL 0x01
@@ -281,11 +641,11 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
#define PARSE_VALEXPR_NO_MIGRATE 0x04
#define PARSE_VALEXPR_NO_REDUCE 0x08
-value_expr_t * parse_value_expr(std::istream& in,
+ptr_op_t parse_value_expr(std::istream& in,
scope_t * scope = NULL,
const short flags = PARSE_VALEXPR_RELAXED);
-inline value_expr_t *
+inline ptr_op_t
parse_value_expr(const string& str,
scope_t * scope = NULL,
const short flags = PARSE_VALEXPR_RELAXED) {
@@ -301,29 +661,29 @@ parse_value_expr(const string& str,
}
}
-inline value_expr_t *
+inline ptr_op_t
parse_value_expr(const char * p,
scope_t * scope = NULL,
const short flags = PARSE_VALEXPR_RELAXED) {
return parse_value_expr(string(p), scope, flags);
}
-void dump_value_expr(std::ostream& out, const value_expr_t * node,
+void dump_value_expr(std::ostream& out, const ptr_op_t node,
const int depth = 0);
bool print_value_expr(std::ostream& out,
- const value_expr_t * node,
+ const ptr_op_t node,
const bool relaxed = true,
- const value_expr_t * node_to_find = NULL,
+ const ptr_op_t node_to_find = NULL,
unsigned long * start_pos = NULL,
unsigned long * end_pos = NULL);
//////////////////////////////////////////////////////////////////////
-inline void guarded_compute(const value_expr_t * expr,
- value_t& result,
- const details_t& details = details_t(),
- value_expr_t * context = NULL) {
+inline void guarded_compute(const ptr_op_t expr,
+ value_t& result,
+ const details_t& details = details_t(),
+ const ptr_op_t context = NULL) {
try {
expr->compute(result, details);
}
@@ -333,45 +693,81 @@ inline void guarded_compute(const value_expr_t * expr,
err->context.push_back(new valexpr_context(expr));
error_context * last = err->context.back();
if (valexpr_context * ctxt = dynamic_cast<valexpr_context *>(last)) {
- ctxt->expr = expr->acquire();
+ ctxt->expr = expr;
ctxt->desc = "While computing value expression:";
}
throw err;
}
}
-inline value_t guarded_compute(const value_expr_t * expr,
+inline value_t guarded_compute(const ptr_op_t expr,
const details_t& details = details_t(),
- value_expr_t * context = NULL) {
+ ptr_op_t context = NULL) {
value_t temp;
guarded_compute(expr, temp, details, context);
return temp;
}
+template<>
+inline symbol_scope_t&
+scope_t::find_scope<symbol_scope_t>(bool skip_this) {
+ optional<scope_t&> scope = find_scope(SYMBOL_SCOPE, skip_this);
+ assert(scope);
+ return downcast<symbol_scope_t>(*scope);
+}
+
+template<>
+inline call_scope_t&
+scope_t::find_scope<call_scope_t>(bool skip_this) {
+ optional<scope_t&> scope = find_scope(CALL_SCOPE, skip_this);
+ assert(scope);
+ return downcast<call_scope_t>(*scope);
+}
+
+template<>
+inline context_scope_t&
+scope_t::find_scope<context_scope_t>(bool skip_this) {
+ optional<scope_t&> scope = find_scope(CONTEXT_SCOPE, skip_this);
+ assert(scope);
+ return downcast<context_scope_t>(*scope);
+}
+
+#define FIND_SCOPE(scope_type, scope_ref) \
+ downcast<scope_t>(scope_ref).find_scope<scope_type>()
+
+#define CALL_SCOPE(scope_ref) \
+ FIND_SCOPE(call_scope_t, scope_ref)
+#define SYMBOL_SCOPE(scope_ref) \
+ FIND_SCOPE(symbol_scope_t, scope_ref)
+#define CONTEXT_SCOPE(scope_ref) \
+ FIND_SCOPE(context_scope_t, scope_ref)
+
+} // namespace expr
+
//////////////////////////////////////////////////////////////////////
class value_expr
{
- value_expr_t * ptr;
+ expr::ptr_op_t ptr;
+
public:
string expr;
+ typedef expr::details_t details_t;
+
value_expr() : ptr(NULL) {}
value_expr(const string& _expr) : expr(_expr) {
DEBUG("ledger.memory.ctors", "ctor value_expr");
if (! _expr.empty())
- ptr = parse_value_expr(expr)->acquire();
+ ptr = expr::parse_value_expr(expr);
else
- ptr = NULL;
+ ptr = expr::ptr_op_t();
}
- value_expr(value_expr_t * _ptr)
- : ptr(_ptr ? _ptr->acquire(): NULL) {
+ value_expr(const expr::ptr_op_t _ptr) : ptr(_ptr) {
DEBUG("ledger.memory.ctors", "ctor value_expr");
}
- value_expr(const value_expr& other)
- : ptr(other.ptr ? other.ptr->acquire() : NULL),
- expr(other.expr) {
+ value_expr(const value_expr& other) : ptr(other.ptr), expr(other.expr) {
DEBUG("ledger.memory.ctors", "ctor value_expr");
}
virtual ~value_expr() {
@@ -382,10 +778,10 @@ public:
value_expr& operator=(const string& _expr) {
expr = _expr;
- reset(parse_value_expr(expr));
+ reset(expr::parse_value_expr(expr));
return *this;
}
- value_expr& operator=(value_expr_t * _expr) {
+ value_expr& operator=(expr::ptr_op_t _expr) {
expr = "";
reset(_expr);
return *this;
@@ -402,50 +798,48 @@ public:
operator string() const throw() {
return expr;
}
- operator value_expr_t *() const throw() {
+ operator const expr::ptr_op_t() const throw() {
return ptr;
}
- value_expr_t& operator*() const throw() {
+ const expr::op_t& operator*() const throw() {
return *ptr;
}
- value_expr_t * operator->() const throw() {
+ const expr::ptr_op_t operator->() const throw() {
return ptr;
}
- value_expr_t * get() const throw() { return ptr; }
- value_expr_t * release() throw() {
- value_expr_t * tmp = ptr;
- ptr = 0;
+ const expr::ptr_op_t get() const throw() { return ptr; }
+ const expr::ptr_op_t release() throw() {
+ const expr::ptr_op_t tmp = ptr;
+ ptr = expr::ptr_op_t();
return tmp;
}
- void reset(value_expr_t * p = 0) throw() {
- if (p != ptr) {
- if (ptr)
- ptr->release();
- ptr = p ? p->acquire() : NULL;
- }
+ void reset(const expr::ptr_op_t p = expr::ptr_op_t()) throw() {
+ ptr = p;
}
virtual void compute(value_t& result,
const details_t& details = details_t(),
- value_expr_t * context = NULL) {
+ expr::ptr_op_t context = NULL) {
guarded_compute(ptr, result, details, context);
}
virtual value_t compute(const details_t& details = details_t(),
- value_expr_t * context = NULL) {
+ expr::ptr_op_t context = NULL) {
value_t temp;
guarded_compute(ptr, temp, details, context);
return temp;
}
friend bool print_value_expr(std::ostream& out,
- const value_expr_t * node,
- const value_expr_t * node_to_find,
+ const expr::ptr_op_t node,
+ const expr::ptr_op_t node_to_find,
unsigned long * start_pos,
unsigned long * end_pos);
};
+typedef value_expr::details_t details_t; // jww (2008-07-20): remove
+
extern value_expr amount_expr;
extern value_expr total_expr;
@@ -471,14 +865,14 @@ inline value_t compute_total(const details_t& details = details_t()) {
return total_expr->compute(details);
}
-value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope,
+expr::ptr_op_t parse_boolean_expr(std::istream& in, expr::scope_t * scope,
const short flags);
inline void parse_value_definition(const string& str,
- scope_t * scope = NULL) {
+ expr::scope_t * scope = NULL) {
std::istringstream def(str);
value_expr expr
- (parse_boolean_expr(def, scope ? scope : global_scope.get(),
+ (parse_boolean_expr(def, scope ? scope : expr::global_scope.get(),
PARSE_VALEXPR_RELAXED));
}
@@ -487,28 +881,26 @@ inline void parse_value_definition(const string& str,
template <typename T>
class item_predicate
{
- public:
- const value_expr_t * predicate;
+public:
+ value_expr predicate;
- item_predicate(const string& _predicate) : predicate(NULL) {
- DEBUG("ledger.memory.ctors", "ctor item_predicate<T>");
- if (! _predicate.empty())
- predicate = parse_value_expr(_predicate)->acquire();
+ item_predicate() {
+ TRACE_CTOR(item_predicate, "ctor item_predicate<T>()");
+ }
+ item_predicate(const value_expr& _predicate) : predicate(_predicate) {
+ TRACE_CTOR(item_predicate, "ctor item_predicate<T>(const value_expr&)");
}
- item_predicate(const value_expr_t * _predicate = NULL)
- : predicate(_predicate->acquire()) {
- DEBUG("ledger.memory.ctors", "ctor item_predicate<T>");
+ item_predicate(const string& _predicate) : predicate(_predicate) {
+ TRACE_CTOR(item_predicate, "ctor item_predicate<T>(const string&)");
}
~item_predicate() {
- DEBUG("ledger.memory.dtors", "dtor item_predicate<T>");
- if (predicate)
- predicate->release();
+ TRACE_DTOR(item_predicate);
}
bool operator()(const T& item) const {
return (! predicate ||
- predicate->compute(details_t(item)).strip_annotations());
+ predicate->compute(value_expr::details_t(item)).strip_annotations());
}
};
diff --git a/walk.cc b/walk.cc
index 46240987..1a26b134 100644
--- a/walk.cc
+++ b/walk.cc
@@ -16,14 +16,14 @@ bool compare_items<transaction_t>::operator()(const transaction_t * left,
transaction_xdata_t& lxdata(transaction_xdata(*left));
if (! (lxdata.dflags & TRANSACTION_SORT_CALC)) {
- guarded_compute(sort_order, lxdata.sort_value, details_t(*left));
+ sort_order.compute(lxdata.sort_value, details_t(*left));
lxdata.sort_value.reduce();
lxdata.dflags |= TRANSACTION_SORT_CALC;
}
transaction_xdata_t& rxdata(transaction_xdata(*right));
if (! (rxdata.dflags & TRANSACTION_SORT_CALC)) {
- guarded_compute(sort_order, rxdata.sort_value, details_t(*right));
+ sort_order.compute(rxdata.sort_value, details_t(*right));
rxdata.sort_value.reduce();
rxdata.dflags |= TRANSACTION_SORT_CALC;
}
@@ -799,13 +799,13 @@ bool compare_items<account_t>::operator()(const account_t * left,
account_xdata_t& lxdata(account_xdata(*left));
if (! (lxdata.dflags & ACCOUNT_SORT_CALC)) {
- guarded_compute(sort_order, lxdata.sort_value, details_t(*left));
+ sort_order.compute(lxdata.sort_value, details_t(*left));
lxdata.dflags |= ACCOUNT_SORT_CALC;
}
account_xdata_t& rxdata(account_xdata(*right));
if (! (rxdata.dflags & ACCOUNT_SORT_CALC)) {
- guarded_compute(sort_order, rxdata.sort_value, details_t(*right));
+ sort_order.compute(rxdata.sort_value, details_t(*right));
rxdata.dflags |= ACCOUNT_SORT_CALC;
}
@@ -841,9 +841,9 @@ void sum_accounts(account_t& account)
xdata.total_count += xdata.count;
}
-void sort_accounts(account_t& account,
- const value_expr_t * sort_order,
- accounts_deque& accounts)
+void sort_accounts(account_t& account,
+ const value_expr& sort_order,
+ accounts_deque& accounts)
{
for (accounts_map::iterator i = account.accounts.begin();
i != account.accounts.end();
@@ -854,15 +854,15 @@ void sort_accounts(account_t& account,
compare_items<account_t>(sort_order));
}
-void walk_accounts(account_t& account,
- item_handler<account_t>& handler,
- const value_expr_t * sort_order)
+void walk_accounts(account_t& account,
+ item_handler<account_t>& handler,
+ const optional<value_expr>& sort_order)
{
handler(account);
if (sort_order) {
accounts_deque accounts;
- sort_accounts(account, sort_order, accounts);
+ sort_accounts(account, *sort_order, accounts);
for (accounts_deque::const_iterator i = accounts.begin();
i != accounts.end();
i++) {
@@ -884,7 +884,7 @@ void walk_accounts(account_t& account,
if (! sort_string.empty()) {
value_expr sort_order;
sort_order.reset(parse_value_expr(sort_string));
- walk_accounts(account, handler, sort_order.get());
+ walk_accounts(account, handler, sort_order);
} else {
walk_accounts(account, handler);
}
diff --git a/walk.h b/walk.h
index 3abaac21..de98d071 100644
--- a/walk.h
+++ b/walk.h
@@ -38,12 +38,9 @@ struct item_handler {
template <typename T>
class compare_items {
- const value_expr_t * sort_order;
+ value_expr sort_order;
public:
- compare_items(const value_expr_t * _sort_order)
- : sort_order(_sort_order) {
- assert(sort_order);
- }
+ compare_items(value_expr _sort_order) : sort_order(_sort_order) {}
bool operator()(const T * left, const T * right);
};
@@ -55,8 +52,8 @@ bool compare_items<T>::operator()(const T * left, const T * right)
value_t left_result;
value_t right_result;
- guarded_compute(sort_order, left_result, details_t(*left));
- guarded_compute(sort_order, right_result, details_t(*right));
+ sort_order.compute(left_result, details_t(*left));
+ sort_order.compute(right_result, details_t(*right));
return left_result < right_result;
}
@@ -244,26 +241,19 @@ class sort_transactions : public item_handler<transaction_t>
{
typedef std::deque<transaction_t *> transactions_deque;
- transactions_deque transactions;
- const value_expr_t * sort_order;
+ transactions_deque transactions;
+ const value_expr sort_order;
public:
sort_transactions(item_handler<transaction_t> * handler,
- const value_expr_t * _sort_order)
+ const value_expr& _sort_order)
: item_handler<transaction_t>(handler),
- sort_order(_sort_order->acquire()) {}
+ sort_order(_sort_order) {}
sort_transactions(item_handler<transaction_t> * handler,
const string& _sort_order)
- : item_handler<transaction_t>(handler) {
- assert(! _sort_order.empty());
- sort_order = parse_value_expr(_sort_order)->acquire();
- }
-
- virtual ~sort_transactions() {
- assert(sort_order);
- sort_order->release();
- }
+ : item_handler<transaction_t>(handler),
+ sort_order(_sort_order) {}
virtual void post_accumulated_xacts();
@@ -284,7 +274,7 @@ class sort_entries : public item_handler<transaction_t>
public:
sort_entries(item_handler<transaction_t> * handler,
- const value_expr_t * _sort_order)
+ const value_expr& _sort_order)
: sorter(handler, _sort_order) {}
sort_entries(item_handler<transaction_t> * handler,
@@ -312,7 +302,7 @@ class filter_transactions : public item_handler<transaction_t>
public:
filter_transactions(item_handler<transaction_t> * handler,
- const value_expr_t * predicate)
+ const value_expr& predicate)
: item_handler<transaction_t>(handler), pred(predicate) {}
filter_transactions(item_handler<transaction_t> * handler,
@@ -392,7 +382,7 @@ class component_transactions : public item_handler<transaction_t>
public:
component_transactions(item_handler<transaction_t> * handler,
- const value_expr_t * predicate)
+ const value_expr& predicate)
: item_handler<transaction_t>(handler), pred(predicate) {}
component_transactions(item_handler<transaction_t> * handler,
@@ -656,7 +646,7 @@ class forecast_transactions : public generate_transactions
public:
forecast_transactions(item_handler<transaction_t> * handler,
- const value_expr_t * predicate)
+ const value_expr& predicate)
: generate_transactions(handler), pred(predicate) {}
forecast_transactions(item_handler<transaction_t> * handler,
@@ -720,12 +710,12 @@ void sum_accounts(account_t& account);
typedef std::deque<account_t *> accounts_deque;
-void sort_accounts(account_t& account,
- const value_expr_t * sort_order,
- accounts_deque& accounts);
-void walk_accounts(account_t& account,
- item_handler<account_t>& handler,
- const value_expr_t * sort_order = NULL);
+void sort_accounts(account_t& account,
+ const value_expr& sort_order,
+ accounts_deque& accounts);
+void walk_accounts(account_t& account,
+ item_handler<account_t>& handler,
+ const optional<value_expr>& sort_order = none);
void walk_accounts(account_t& account,
item_handler<account_t>& handler,
const string& sort_string);