diff options
author | John Wiegley <johnw@newartisans.com> | 2008-07-20 05:03:54 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-07-20 05:03:54 -0400 |
commit | 52fc9f2e4499e75d17e7f12d32a8391054d8634a (patch) | |
tree | 164b6b4cdb6717f2df7d154793499e90bdd3ae3e | |
parent | 59f6ffb863b7121b8a49c13dd64f9943ddaf7ed0 (diff) | |
download | ledger-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.am | 1 | ||||
-rw-r--r-- | binary.cc | 190 | ||||
-rw-r--r-- | binary.h | 4 | ||||
-rw-r--r-- | csv.cc | 3 | ||||
-rw-r--r-- | emacs.cc | 10 | ||||
-rw-r--r-- | format.cc | 32 | ||||
-rw-r--r-- | format.h | 6 | ||||
-rw-r--r-- | journal.cc | 264 | ||||
-rw-r--r-- | journal.h | 256 | ||||
-rw-r--r-- | main.cc | 698 | ||||
-rw-r--r-- | option.cc | 1135 | ||||
-rw-r--r-- | option.h | 70 | ||||
-rw-r--r-- | parser.cc | 14 | ||||
-rw-r--r-- | report.cc | 571 | ||||
-rw-r--r-- | report.h | 249 | ||||
-rw-r--r-- | session.cc | 313 | ||||
-rw-r--r-- | session.h | 198 | ||||
-rw-r--r-- | textual.cc | 2 | ||||
-rw-r--r-- | valexpr.cc | 614 | ||||
-rw-r--r-- | valexpr.h | 698 | ||||
-rw-r--r-- | walk.cc | 24 | ||||
-rw-r--r-- | walk.h | 50 |
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 \ @@ -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(); @@ -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) { @@ -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 << ','; { @@ -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; @@ -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) @@ -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> { @@ -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()) @@ -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; } @@ -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. @@ -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 @@ -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 @@ -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); } } @@ -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 @@ -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 @@ -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") { @@ -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) @@ -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()); } }; @@ -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); } @@ -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); |