diff options
53 files changed, 806 insertions, 829 deletions
diff --git a/python/py_amount.cc b/python/py_amount.cc index 26107ce3..a12104d8 100644 --- a/python/py_amount.cc +++ b/python/py_amount.cc @@ -74,7 +74,8 @@ void py_parse_str_2(amount_t& amount, const string& str, unsigned char flags) { amount.parse(str, flags); } -void py_print(amount_t& amount, object out) { +void py_print(amount_t& amount, object out) +{ if (PyFile_Check(out.ptr())) { pyofstream outstr(reinterpret_cast<PyFileObject *>(out.ptr())); amount.print(outstr); @@ -84,6 +85,10 @@ void py_print(amount_t& amount, object out) { } } +void py_amount_initialize() { + amount_t::initialize(); +} + #define EXC_TRANSLATOR(type) \ void exc_translate_ ## type(const type& err) { \ PyErr_SetString(PyExc_ArithmeticError, err.what()); \ @@ -94,7 +99,7 @@ EXC_TRANSLATOR(amount_error) void export_amount() { class_< amount_t > ("Amount") - .def("initialize", &amount_t::initialize) + .def("initialize", py_amount_initialize) // only for the PyUnitTests .staticmethod("initialize") .def("shutdown", &amount_t::shutdown) .staticmethod("shutdown") @@ -103,12 +108,6 @@ void export_amount() make_getter(&amount_t::current_pool, return_value_policy<reference_existing_object>())) - .add_static_property("keep_base", &amount_t::keep_base) - - .add_static_property("keep_price", &amount_t::keep_price) - .add_static_property("keep_date", &amount_t::keep_date) - .add_static_property("keep_tag", &amount_t::keep_tag) - .add_static_property("stream_fullstrings", make_getter(&amount_t::stream_fullstrings), make_setter(&amount_t::stream_fullstrings)) diff --git a/python/py_times.cc b/python/py_times.cc index 23532cc2..6beafe39 100644 --- a/python/py_times.cc +++ b/python/py_times.cc @@ -133,8 +133,6 @@ void export_times() scope().attr("parse_datetime") = &py_parse_datetime; scope().attr("parse_date") = &py_parse_date; - scope().attr("current_time") = current_time; - scope().attr("current_date") = current_date; } } // namespace ledger diff --git a/python/py_value.cc b/python/py_value.cc index 80952d7d..59be1d33 100644 --- a/python/py_value.cc +++ b/python/py_value.cc @@ -54,15 +54,15 @@ boost::optional<value_t> py_value_2(const value_t& amount, return amount.value(moment, in_terms_of); } -string py_print(const value_t& value) { +string py_dump(const value_t& value) { std::ostringstream buf; - value.print(buf); + value.dump(buf); return buf.str(); } -string py_print_relaxed(const value_t& value) { +string py_dump_relaxed(const value_t& value) { std::ostringstream buf; - value.print(buf, true); + value.dump(buf, true); return buf.str(); } @@ -255,8 +255,8 @@ void export_value() .def("to_string", &value_t::to_string) .def("to_sequence", &value_t::to_sequence) - .def("__str__", py_print_relaxed) - .def("__repr__", py_print) + .def("__str__", py_dump_relaxed) + .def("__repr__", py_dump) .def("cast", &value_t::cast) .def("in_place_cast", &value_t::in_place_cast) diff --git a/src/account.cc b/src/account.cc index 9d7bf1e5..376097f2 100644 --- a/src/account.cc +++ b/src/account.cc @@ -30,7 +30,6 @@ */ #include "account.h" -#include "report.h" namespace ledger { @@ -87,6 +86,25 @@ account_t * account_t::find_account(const string& name, return account; } +namespace { + account_t * find_account_re_(account_t * account, const mask_t& regexp) + { + if (regexp.match(account->fullname())) + return account; + + foreach (accounts_map::value_type& pair, account->accounts) + if (account_t * a = find_account_re_(pair.second, regexp)) + return a; + + return NULL; + } +} + +account_t * account_t::find_account_re(const string& regexp) +{ + return find_account_re_(this, mask_t(regexp)); +} + string account_t::fullname() const { if (! _fullname.empty()) { @@ -114,7 +132,8 @@ std::ostream& operator<<(std::ostream& out, const account_t& account) } namespace { - value_t get_partial_name(account_t& account) { + value_t get_partial_name(account_t& account) + { string name; for (account_t * acct = &account; @@ -152,7 +171,8 @@ namespace { return long(account.depth); } - value_t get_depth_spacer(account_t& account) { + value_t get_depth_spacer(account_t& account) + { std::ostringstream out; for (account_t * acct = &account; acct; @@ -197,7 +217,7 @@ expr_t::ptr_op_t account_t::lookup(const string& name) break; } - return session_t::current->global_scope->lookup(name); + return expr_t::ptr_op_t(); } bool account_t::valid() const @@ -222,12 +242,13 @@ bool account_t::valid() const return true; } -void account_t::calculate_sums(expr_t& amount_expr) +void account_t::calculate_sums(expr_t& amount_expr, + scope_t& scope) { xdata_t& xd(xdata()); foreach (accounts_map::value_type& pair, accounts) { - (*pair.second).calculate_sums(amount_expr); + (*pair.second).calculate_sums(amount_expr, scope); xdata_t& child_xd((*pair.second).xdata()); if (! child_xd.total.is_null()) { @@ -239,7 +260,8 @@ void account_t::calculate_sums(expr_t& amount_expr) } } - call_scope_t args(*this); + bind_scope_t bound_scope(scope, *this); + call_scope_t args(bound_scope); value_t amount(amount_expr.calc(args)); if (! amount.is_null()) { add_or_set_value(xd.total, amount); diff --git a/src/account.h b/src/account.h index ad59a5b8..565a11af 100644 --- a/src/account.h +++ b/src/account.h @@ -112,6 +112,7 @@ class account_t : public scope_t } account_t * find_account(const string& name, bool auto_create = true); + account_t * find_account_re(const string& regexp); virtual expr_t::ptr_op_t lookup(const string& name); @@ -177,7 +178,7 @@ class account_t : public scope_t return *xdata_; } - void calculate_sums(expr_t& amount_expr); + void calculate_sums(expr_t& amount_expr, scope_t& scope); }; std::ostream& operator<<(std::ostream& out, const account_t& account); diff --git a/src/amount.cc b/src/amount.cc index 9a1dd12b..bfbb591e 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -33,14 +33,6 @@ namespace ledger { -commodity_pool_t * amount_t::current_pool = NULL; - -bool amount_t::keep_base = false; - -bool amount_t::keep_price = false; -bool amount_t::keep_date = false; -bool amount_t::keep_tag = false; - bool amount_t::stream_fullstrings = false; #if !defined(THREADSAFE) @@ -93,39 +85,31 @@ struct amount_t::bigint_t : public supports_flags<> } }; -void amount_t::initialize() +shared_ptr<commodity_pool_t> amount_t::current_pool; + +void amount_t::initialize(shared_ptr<commodity_pool_t> pool) { mpz_init(temp); mpq_init(tempq); mpfr_init(tempf); mpfr_init(tempfb); - if (! current_pool) - current_pool = new commodity_pool_t; - - // Add time commodity conversions, so that timelog's may be parsed - // in terms of seconds, but reported as minutes or hours. - if (commodity_t * commodity = current_pool->create("s")) { - commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); + current_pool = pool; +} - parse_conversion("1.0m", "60s"); - parse_conversion("1.0h", "60m"); - } else { - assert(false); - } +void amount_t::initialize() +{ + initialize(shared_ptr<commodity_pool_t>(new commodity_pool_t)); } void amount_t::shutdown() { + current_pool.reset(); + mpz_clear(temp); mpq_clear(tempq); mpfr_clear(tempf); mpfr_clear(tempfb); - - if (current_pool) { - checked_delete(current_pool); - current_pool = NULL; - } } void amount_t::_copy(const amount_t& amt) @@ -426,8 +410,7 @@ void amount_t::set_keep_precision(const bool keep) const quantity->drop_flags(BIGINT_KEEP_PREC); } -amount_t::precision_t -amount_t::display_precision(const bool full_precision) const +amount_t::precision_t amount_t::display_precision() const { if (! quantity) throw_(amount_error, @@ -435,7 +418,7 @@ amount_t::display_precision(const bool full_precision) const commodity_t& comm(commodity()); - if (! comm || full_precision || keep_precision()) + if (! comm || keep_precision()) return quantity->prec; else if (comm.precision() != quantity->prec) return comm.precision(); @@ -728,22 +711,18 @@ annotation_t& amount_t::annotation() return ann_comm.details; } -amount_t amount_t::strip_annotations(const bool _keep_price, - const bool _keep_date, - const bool _keep_tag) const +amount_t amount_t::strip_annotations(const keep_details_t& what_to_keep) const { if (! quantity) throw_(amount_error, "Cannot strip commodity annotations from an uninitialized amount"); - if (! commodity().annotated || - (_keep_price && _keep_date && _keep_tag)) - return *this; - - amount_t t(*this); - t.set_commodity(as_annotated_commodity(commodity()). - strip_annotations(_keep_price, _keep_date, _keep_tag)); - return t; + if (! what_to_keep.keep_all(commodity())) { + amount_t t(*this); + t.set_commodity(commodity().strip_annotations(what_to_keep)); + return t; + } + return *this; } @@ -964,8 +943,7 @@ void amount_t::parse_conversion(const string& larger_str, smaller.commodity().set_larger(larger); } -void amount_t::print(std::ostream& _out, bool omit_commodity, - bool full_precision) const +void amount_t::print(std::ostream& _out) const { assert(valid()); @@ -974,25 +952,20 @@ void amount_t::print(std::ostream& _out, bool omit_commodity, return; } - amount_t base(*this); - if (! amount_t::keep_base) - base.in_place_unreduce(); - std::ostringstream out; - commodity_t& comm(base.commodity()); + commodity_t& comm(commodity()); precision_t precision = 0; - if (! omit_commodity && ! comm.has_flags(COMMODITY_STYLE_SUFFIXED)) { + if (! comm.has_flags(COMMODITY_STYLE_SUFFIXED)) { comm.print(out); if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) out << " "; } - stream_out_mpq(out, MP(quantity), base.display_precision(full_precision), - omit_commodity ? optional<commodity_t&>() : comm); + stream_out_mpq(out, MP(quantity), display_precision(), comm); - if (! omit_commodity && comm.has_flags(COMMODITY_STYLE_SUFFIXED)) { + if (comm.has_flags(COMMODITY_STYLE_SUFFIXED)) { if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) out << " "; comm.print(out); @@ -1001,7 +974,7 @@ void amount_t::print(std::ostream& _out, bool omit_commodity, // If there are any annotations associated with this commodity, // output them now. - if (! omit_commodity && comm.annotated) { + if (comm.annotated) { annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm)); assert(&*ann.details.price != this); ann.write_annotations(out); diff --git a/src/amount.h b/src/amount.h index 8229b015..4405e058 100644 --- a/src/amount.h +++ b/src/amount.h @@ -59,6 +59,7 @@ namespace ledger { class commodity_t; class annotation_t; +class keep_details_t; class commodity_pool_t; DECLARE_EXCEPTION(amount_error, std::runtime_error); @@ -80,8 +81,12 @@ class amount_t ordered_field_operators<amount_t, long> > > > { public: + /** Indicates which commodity pool should be used. */ + static shared_ptr<commodity_pool_t> current_pool; + /** Ready the amount subsystem for use. @note Normally called by session_t::initialize(). */ + static void initialize(shared_ptr<commodity_pool_t> pool); static void initialize(); /** Shutdown the amount subsystem and free all resources. @note Normally called by session_t::shutdown(). */ @@ -94,47 +99,9 @@ public: avoid losing precision during division and multiplication. */ static const std::size_t extend_by_digits = 6U; - /** Indicates which commodity pool should be used. */ - static commodity_pool_t * current_pool; - - /** If scalable commodities are automatically converted to their most - reduced form for printing. - - For example, Ledger supports time values specified in seconds, - hours or minutes. Internally, such amounts are always kept as - quantities of seconds. However, when streaming the amount, Ledger - converts it to its "least representation", which is \c 5.2h in the - second case. If \c keep_base is \c true this amount is displayed - as \c 18720s. */ - static bool keep_base; - - /** If the lot price is considered whenever working with commoditized - values. - - Let's say a user adds two values of the following form: - @code - 10 AAPL + 10 AAPL {$20} - @endcode - - This expression adds ten shares of Apple stock with another ten - shares that were purchased for \c $20 a share. If \c keep_price - is false, the result of this expression is an amount equal to - <tt>20 AAPL</tt>. If \c keep_price is \c true the expression - yields an exception for adding amounts with different commodities. - In that case, a \link balance_t \endlink object must be used to - store the combined sum. */ - static bool keep_price; - - /** If the lot date is considered whenever working with commoditized - values. @see keep_date */ - static bool keep_date; - /** If the lot note is considered whenever working with commoditized - values. @see keep_tag */ - static bool keep_tag; - /** If amounts should be streamed using to_fullstring() rather than - to_string(), so that complete precision is always displayed no - matter what the precision of an individual commodity may be. */ + to_string(), so that complete precision is always displayed no matter + what the precision of an individual commodity may be. */ static bool stream_fullstrings; protected: @@ -317,7 +284,7 @@ public: precision_t precision() const; bool keep_precision() const; void set_keep_precision(const bool keep = true) const; - precision_t display_precision(const bool full_precision = false) const; + precision_t display_precision() const; /** Returns the negated value of an amount. @see operator-() @@ -556,12 +523,8 @@ public: commodity's annotations. The structure returns will evaluate as boolean false if there are no details. - strip_annotations([keep_price, keep_date, keep_tag]) returns an - amount whose commodity's annotations have been stripped. The - three `keep_' arguments determine which annotation detailed are - kept, meaning that the default is to follow whatever - amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag - have been set to (which all default to false). + strip_annotations() returns an amount whose commodity's annotations have + been stripped. */ void annotate(const annotation_t& details); bool is_annotated() const; @@ -571,9 +534,22 @@ public: return const_cast<amount_t&>(*this).annotation(); } - amount_t strip_annotations(const bool _keep_price = keep_price, - const bool _keep_date = keep_date, - const bool _keep_tag = keep_tag) const; + /** If the lot price is considered whenever working with commoditized + values. + + Let's say a user adds two values of the following form: + @code + 10 AAPL + 10 AAPL {$20} + @endcode + + This expression adds ten shares of Apple stock with another ten + shares that were purchased for \c $20 a share. If \c keep_price + is false, the result of this expression is an amount equal to + <tt>20 AAPL</tt>. If \c keep_price is \c true the expression + yields an exception for adding amounts with different commodities. + In that case, a \link balance_t \endlink object must be used to + store the combined sum. */ + amount_t strip_annotations(const keep_details_t& what_to_keep) const; /*@}*/ @@ -649,23 +625,20 @@ public: */ /*@{*/ - /** An amount may be output to a stream using the - `print' method. There is also a global operator<< defined which - simply calls print for an amount on the given stream. There is - one form of the print method, which takes one required argument - and two arguments with default values: - - print(ostream, bool omit_commodity = false, bool full_precision = - false) prints an amounts to the given output stream, using its - commodity's default display characteristics. If `omit_commodity' - is true, the commodity will not be displayed, only the amount - (although the commodity's display precision is still used). If - `full_precision' is true, the full internal precision of the - amount is displayed, regardless of its commodity's display - precision. + /** An amount may be output to a stream using the `print' method. There is + also a global operator<< defined which simply calls print for an amount + on the given stream. There is one form of the print method, which takes + one required argument and two arguments with default values: + + print(ostream, bool omit_commodity = false, bool full_precision = false) + prints an amounts to the given output stream, using its commodity's + default display characteristics. If `omit_commodity' is true, the + commodity will not be displayed, only the amount (although the + commodity's display precision is still used). If `full_precision' is + true, the full internal precision of the amount is displayed, regardless + of its commodity's display precision. */ - void print(std::ostream& out, bool omit_commodity = false, - bool full_precision = false) const; + void print(std::ostream& out) const; /*@}*/ @@ -709,18 +682,21 @@ inline string amount_t::to_string() const { inline string amount_t::to_fullstring() const { std::ostringstream bufstream; - print(bufstream, false, true); + unrounded().print(bufstream); return bufstream.str(); } inline string amount_t::quantity_string() const { std::ostringstream bufstream; - print(bufstream, true); + number().print(bufstream); return bufstream.str(); } inline std::ostream& operator<<(std::ostream& out, const amount_t& amt) { - amt.print(out, false, amount_t::stream_fullstrings); + if (amount_t::stream_fullstrings) + amt.unrounded().print(out); + else + amt.print(out); return out; } inline std::istream& operator>>(std::istream& in, amount_t& amt) { diff --git a/src/balance.cc b/src/balance.cc index 7b1ed4dd..c9ca148d 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -181,6 +181,7 @@ balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const if (amounts.size() == 1) { return amounts.begin()->second; } +#if 0 else if (amounts.size() > 1) { // Try stripping annotations before giving an error. balance_t temp(strip_annotations()); @@ -190,6 +191,7 @@ balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const throw_(amount_error, "Requested amount of a balance with multiple commodities: " << temp); } +#endif } else if (amounts.size() > 0) { amounts_map::const_iterator i = amounts.find(&*commodity); @@ -199,14 +201,13 @@ balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const return none; } -balance_t balance_t::strip_annotations(const bool keep_price, - const bool keep_date, - const bool keep_tag) const +balance_t +balance_t::strip_annotations(const keep_details_t& what_to_keep) const { balance_t temp; foreach (const amounts_map::value_type& pair, amounts) - temp += pair.second.strip_annotations(keep_price, keep_date, keep_tag); + temp += pair.second.strip_annotations(what_to_keep); return temp; } diff --git a/src/balance.h b/src/balance.h index d1e4301b..a0231717 100644 --- a/src/balance.h +++ b/src/balance.h @@ -446,10 +446,7 @@ public: * their commodity annotations likewise stripped. See * amount_t::strip_annotations for more details. */ - balance_t - strip_annotations(const bool keep_price = amount_t::keep_price, - const bool keep_date = amount_t::keep_date, - const bool keep_tag = amount_t::keep_tag) const; + balance_t strip_annotations(const keep_details_t& what_to_keep) const; /** * Printing methods. A balance may be output to a stream using the diff --git a/src/chain.cc b/src/chain.cc index d3ccf11c..f79da543 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -55,7 +55,9 @@ xact_handler_ptr chain_xact_handlers(report_t& report, // filter_xacts will only pass through xacts matching the // `display_predicate'. if (! report.display_predicate.empty()) - handler.reset(new filter_xacts(handler, report.display_predicate)); + handler.reset(new filter_xacts + (handler, item_predicate<xact_t>(report.display_predicate, + report.what_to_keep))); // calc_xacts computes the running total. When this // appears will determine, for example, whether filtered @@ -81,7 +83,9 @@ xact_handler_ptr chain_xact_handlers(report_t& report, descend_exprs.rbegin(); i != descend_exprs.rend(); i++) - handler.reset(new component_xacts(handler, *i)); + handler.reset(new component_xacts + (handler, + item_predicate<xact_t>(*i, report.what_to_keep))); remember_components = true; } @@ -90,7 +94,7 @@ xact_handler_ptr chain_xact_handlers(report_t& report, // xacts which can be reconciled to a given balance // (calculated against the xacts which it receives). if (! report.reconcile_balance.empty()) { - date_t cutoff = current_date; + date_t cutoff = CURRENT_DATE(); if (! report.reconcile_date.empty()) cutoff = parse_date(report.reconcile_date); handler.reset(new reconcile_xacts @@ -100,7 +104,9 @@ xact_handler_ptr chain_xact_handlers(report_t& report, // filter_xacts will only pass through xacts // matching the `secondary_predicate'. if (! report.secondary_predicate.empty()) - handler.reset(new filter_xacts(handler, report.secondary_predicate)); + handler.reset(new filter_xacts + (handler, item_predicate<xact_t>(report.secondary_predicate, + report.what_to_keep))); // sort_xacts will sort all the xacts it sees, based // on the `sort_order' value expression. @@ -176,7 +182,9 @@ xact_handler_ptr chain_xact_handlers(report_t& report, if (! report.predicate.empty()) { DEBUG("report.predicate", "Report predicate expression = " << report.predicate); - handler.reset(new filter_xacts(handler, report.predicate)); + handler.reset(new filter_xacts + (handler, item_predicate<xact_t>(report.predicate, + report.what_to_keep))); } #if 0 diff --git a/src/commodity.cc b/src/commodity.cc index ef705065..d651f8ae 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -721,27 +721,26 @@ bool annotated_commodity_t::operator==(const commodity_t& comm) const } commodity_t& -annotated_commodity_t::strip_annotations(const bool _keep_price, - const bool _keep_date, - const bool _keep_tag) +annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) { DEBUG("commodity.annotated.strip", "Reducing commodity " << *this << std::endl - << " keep price " << _keep_price << " " - << " keep date " << _keep_date << " " - << " keep tag " << _keep_tag); + << " keep price " << what_to_keep.keep_price << " " + << " keep date " << what_to_keep.keep_date << " " + << " keep tag " << what_to_keep.keep_tag); commodity_t * new_comm; - if ((_keep_price && details.price) || - (_keep_date && details.date) || - (_keep_tag && details.tag)) + if (what_to_keep.keep_any(*this) && + ((what_to_keep.keep_price && details.price) || + (what_to_keep.keep_date && details.date) || + (what_to_keep.keep_tag && details.tag))) { new_comm = parent().find_or_create (referent(), - annotation_t(_keep_price ? details.price : none, - _keep_date ? details.date : none, - _keep_tag ? details.tag : none)); + annotation_t(what_to_keep.keep_price ? details.price : none, + what_to_keep.keep_date ? details.date : none, + what_to_keep.keep_tag ? details.tag : none)); } else { new_comm = parent().find_or_create(base_symbol()); } diff --git a/src/commodity.h b/src/commodity.h index c8a1e815..ac1d15ba 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -49,6 +49,8 @@ namespace ledger { +class keep_details_t; + DECLARE_EXCEPTION(commodity_error, std::runtime_error); /** @@ -211,6 +213,17 @@ public: return base.get() == comm.base.get(); } + virtual commodity_t& referent() { + return *this; + } + virtual const commodity_t& referent() const { + return *this; + } + virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep) { + return *this; + } + virtual void write_annotations(std::ostream& out) const {} + commodity_pool_t& parent() const { return *parent_; } @@ -421,6 +434,49 @@ struct annotation_t : public equality_comparable<annotation_t> } }; +struct keep_details_t +{ + bool keep_price; + bool keep_date; + bool keep_tag; + bool keep_base; + + explicit keep_details_t(bool _keep_price = false, + bool _keep_date = false, + bool _keep_tag = false, + bool _keep_base = false) + : keep_price(_keep_price), + keep_date(_keep_date), + keep_tag(_keep_tag), + keep_base(_keep_base) + { + TRACE_CTOR(keep_details_t, "bool, bool, bool, bool"); + } + keep_details_t(const keep_details_t& other) + : keep_price(other.keep_price), keep_date(other.keep_date), + keep_tag(other.keep_tag), keep_base(other.keep_base) { + TRACE_CTOR(keep_details_t, "copy"); + } + + ~keep_details_t() throw() { + TRACE_DTOR(keep_details_t); + } + + bool keep_all() const { + return keep_price && keep_date && keep_tag; + } + bool keep_all(const commodity_t& comm) const { + return ! comm.annotated || (keep_price && keep_date && keep_tag); + } + + bool keep_any() const { + return keep_price || keep_date || keep_tag; + } + bool keep_any(const commodity_t& comm) const { + return comm.annotated && (keep_price || keep_date || keep_tag); + } +}; + inline std::ostream& operator<<(std::ostream& out, const annotation_t& details) { details.print(out); return out; @@ -456,18 +512,16 @@ public: return *this == static_cast<const commodity_t&>(comm); } - commodity_t& referent() { + virtual commodity_t& referent() { return *ptr; } - const commodity_t& referent() const { + virtual const commodity_t& referent() const { return *ptr; } - commodity_t& strip_annotations(const bool _keep_price, - const bool _keep_date, - const bool _keep_tag); + virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); - void write_annotations(std::ostream& out) const { + virtual void write_annotations(std::ostream& out) const { annotated_commodity_t::write_annotations(out, details); } diff --git a/src/derive.cc b/src/derive.cc index 7ee530d1..712fd807 100644 --- a/src/derive.cc +++ b/src/derive.cc @@ -35,7 +35,7 @@ namespace ledger { -entry_t * derive_new_entry(report_t& report, +entry_t * derive_new_entry(report_t& report, strings_list::iterator i, strings_list::iterator end) { @@ -71,12 +71,12 @@ entry_t * derive_new_entry(report_t& report, if (! matching) { account_t * acct; if (i == end || ((*i)[0] == '-' || std::isdigit((*i)[0]))) { - acct = session.find_account("Expenses"); + acct = session.master->find_account("Expenses"); } else if (i != end) { - acct = session.find_account_re(*i); + acct = session.master->find_account_re(*i); if (! acct) - acct = session.find_account(*i); + acct = session.master->find_account(*i); assert(acct); i++; } @@ -106,16 +106,16 @@ entry_t * derive_new_entry(report_t& report, if (i != end) { if (! acct) - acct = session.find_account_re(*i); + acct = session.master->find_account_re(*i); if (! acct) - acct = session.find_account(*i); + acct = session.master->find_account(*i); } if (! acct) { if (matching && matching->journal->basket) acct = matching->journal->basket; else - acct = session.find_account("Equity"); + acct = session.master->find_account("Equity"); } added->add_xact(new xact_t(acct)); @@ -144,9 +144,9 @@ entry_t * derive_new_entry(report_t& report, added->add_xact(xact); if (i != end) { - account_t * acct = session.find_account_re(*i); + account_t * acct = session.master->find_account_re(*i); if (! acct) - acct = session.find_account(*i); + acct = session.master->find_account(*i); assert(acct); added->xacts.back()->account = acct; } @@ -185,16 +185,16 @@ entry_t * derive_new_entry(report_t& report, strings_list::iterator x = i; if (i != end && ++x == end) { - draw_acct = session.find_account_re(*i); + draw_acct = session.master->find_account_re(*i); if (! draw_acct) - draw_acct = session.find_account(*i); + draw_acct = session.master->find_account(*i); i++; } if (! acct) - acct = session.find_account_re(re_pat); + acct = session.master->find_account_re(re_pat); if (! acct) - acct = session.find_account(re_pat); + acct = session.master->find_account(re_pat); xact = new xact_t(acct, amount); if (! xact->amount.commodity()) { diff --git a/src/emacs.cc b/src/emacs.cc index 714302c5..3c3c1b7b 100644 --- a/src/emacs.cc +++ b/src/emacs.cc @@ -39,7 +39,7 @@ void format_emacs_xacts::write_entry(entry_t& entry) out << "\"" << entry.pathname << "\" " << (static_cast<std::size_t>(entry.beg_line) + 1) << " "; - tm when = gregorian::to_tm(*entry.date()); + tm when = gregorian::to_tm(entry.date()); std::time_t date = std::mktime(&when); // jww (2008-04-20): Is this GMT or local? out << "(" << (date / 65536) << " " << (date % 65536) << " 0) "; diff --git a/src/entry.cc b/src/entry.cc index 27968614..815186f0 100644 --- a/src/entry.cc +++ b/src/entry.cc @@ -247,8 +247,7 @@ bool entry_base_t::finalize() DEBUG("entry.finalize.totals", "Total for " << xact->account->fullname() << " + " - << xact->amount.strip_annotations() << ": " - << xact->account->xdata().value.strip_annotations()); + << xact->amount << ": " << xact->account->xdata().value); } } if (all_null) diff --git a/src/entry.h b/src/entry.h index 37456199..ceea6aed 100644 --- a/src/entry.h +++ b/src/entry.h @@ -135,10 +135,10 @@ public: : entry_base_t(), predicate(other.predicate) { TRACE_CTOR(auto_entry_t, "copy"); } - auto_entry_t(const string& _predicate) + auto_entry_t(const item_predicate<xact_t>& _predicate) : predicate(_predicate) { - TRACE_CTOR(auto_entry_t, "const string&"); + TRACE_CTOR(auto_entry_t, "const item_predicate<xact_t>&"); } virtual ~auto_entry_t() { diff --git a/src/expr.cc b/src/expr.cc index da45bb82..a655edf0 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -35,8 +35,6 @@ namespace ledger { -std::auto_ptr<expr_t::parser_t> expr_t::parser; - expr_t::expr_t() : compiled(false) { TRACE_CTOR(expr_t, ""); @@ -52,17 +50,15 @@ expr_t::expr_t(const string& _str, const uint_least8_t flags) : str(_str), compiled(false) { TRACE_CTOR(expr_t, "const string&"); - if (! _str.empty()) - ptr = parser->parse(str, flags); + parse(str, flags); } expr_t::expr_t(std::istream& in, const uint_least8_t flags) : compiled(false) { TRACE_CTOR(expr_t, "std::istream&"); - - ptr = parser->parse(in, flags); + parse(in, flags); } expr_t::expr_t(const ptr_op_t& _ptr, const string& _str) @@ -88,22 +84,18 @@ expr_t& expr_t::operator=(const expr_t& _expr) void expr_t::parse(const string& _str, const uint32_t flags) { - if (! parser.get()) - throw_(parse_error, "Value expression parser not initialized"); - + parser_t parser; str = _str; - ptr = parser->parse(str, flags); + ptr = parser.parse(str, flags); compiled = false; } void expr_t::parse(std::istream& in, const uint32_t flags, const string * original_string) { - if (! parser.get()) - throw_(parse_error, "Value expression parser not initialized"); - + parser_t parser; str = "<stream>"; - ptr = parser->parse(in, flags, original_string); + ptr = parser.parse(in, flags, original_string); compiled = false; } @@ -131,7 +123,18 @@ value_t expr_t::calc(scope_t& scope) dump(*_log_stream); } } - return ptr->calc(scope); + + ptr_op_t context; + try { + return ptr->calc(scope, &context); + } + catch (const std::exception& err) { + if (context) { + add_error_context("While evaluating value expression:"); + add_error_context(op_context(ptr, context)); + } + throw; + } } return NULL_VALUE; } @@ -182,16 +185,6 @@ void expr_t::dump(std::ostream& out) const if (ptr) ptr->dump(out, 0); } -void expr_t::initialize() -{ - parser.reset(new expr_t::parser_t); -} - -void expr_t::shutdown() -{ - parser.reset(); -} - std::ostream& operator<<(std::ostream& out, const expr_t& expr) { expr.print(out); return out; @@ -68,9 +68,7 @@ typedef function<value_t (call_scope_t&)> function_t; class expr_t { struct token_t; - - class parser_t; - static std::auto_ptr<parser_t> parser; + class parser_t; public: class op_t; @@ -92,14 +90,6 @@ private: bool compiled; public: - /** - * The initialize and shutdown methods ready the amount subsystem for - * use. Normally they are called by `session_t::initialize' and - * `session_t::shutdown'. - */ - static void initialize(); - static void shutdown(); - expr_t(); expr_t(const expr_t& other); expr_t(const ptr_op_t& _ptr, const string& _str = ""); diff --git a/src/filters.cc b/src/filters.cc index 9be7e486..b3d1e2fe 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -102,7 +102,9 @@ void set_account_value::operator()(xact_t& xact) account_t * acct = xact.reported_account(); account_t::xdata_t& xdata(acct->xdata()); + DEBUG("account.sums", "Account value was = " << xdata.value); xact.add_to_value(xdata.value); + DEBUG("account.sums", "Account value is = " << xdata.value); xdata.count++; if (xact.has_flags(XACT_VIRTUAL)) @@ -218,73 +220,74 @@ void invert_xacts::operator()(xact_t& xact) } -static inline -void handle_value(const value_t& value, - account_t * account, - entry_t * entry, - unsigned int flags, - std::list<xact_t>& temps, - item_handler<xact_t>& handler, - const date_t& date = date_t(), - xacts_list * component_xacts = NULL) -{ - temps.push_back(xact_t(account)); - xact_t& xact(temps.back()); - xact.entry = entry; - xact.add_flags(ITEM_TEMP); - entry->add_xact(&xact); - - // If there are component xacts to associate with this - // temporary, do so now. - - if (component_xacts) - xact.xdata().copy_component_xacts(*component_xacts); - - // If the account for this xact is all virtual, then report - // the xact as such. This allows subtotal reports to show - // "(Account)" for accounts that contain only virtual xacts. - - if (account && account->has_xdata()) - if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS)) { - xact.add_flags(XACT_VIRTUAL); - if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_UNB_VIRTUALS)) - xact.add_flags(XACT_MUST_BALANCE); - } +namespace { + void handle_value(const value_t& value, + account_t * account, + entry_t * entry, + unsigned int flags, + std::list<xact_t>& temps, + item_handler<xact_t>& handler, + const date_t& date = date_t(), + xacts_list * component_xacts = NULL) + { + temps.push_back(xact_t(account)); + xact_t& xact(temps.back()); + xact.entry = entry; + xact.add_flags(ITEM_TEMP); + entry->add_xact(&xact); + + // If there are component xacts to associate with this temporary, do so + // now. + + if (component_xacts) + xact.xdata().copy_component_xacts(*component_xacts); + + // If the account for this xact is all virtual, then report the xact as + // such. This allows subtotal reports to show "(Account)" for accounts + // that contain only virtual xacts. + + if (account && account->has_xdata()) + if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS)) { + xact.add_flags(XACT_VIRTUAL); + if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_UNB_VIRTUALS)) + xact.add_flags(XACT_MUST_BALANCE); + } + + xact_t::xdata_t& xdata(xact.xdata()); - xact_t::xdata_t& xdata(xact.xdata()); + if (is_valid(date)) + xdata.date = date; - if (is_valid(date)) - xdata.date = date; - - value_t temp(value); - - switch (value.type()) { - case value_t::BOOLEAN: - case value_t::DATETIME: - case value_t::DATE: - case value_t::INTEGER: - temp.cast(value_t::AMOUNT); - // fall through... - - case value_t::AMOUNT: - xact.amount = temp.as_amount(); - break; - - case value_t::BALANCE: - case value_t::BALANCE_PAIR: - xdata.value = temp; - flags |= XACT_EXT_COMPOUND; - break; - - default: - assert(false); // jww (2008-04-24): What to do here? - break; - } + value_t temp(value); - if (flags) - xdata.add_flags(flags); + switch (value.type()) { + case value_t::BOOLEAN: + case value_t::DATETIME: + case value_t::DATE: + case value_t::INTEGER: + temp.cast(value_t::AMOUNT); + // fall through... - handler(xact); + case value_t::AMOUNT: + xact.amount = temp.as_amount(); + break; + + case value_t::BALANCE: + case value_t::BALANCE_PAIR: + xdata.value = temp; + flags |= XACT_EXT_COMPOUND; + break; + + default: + assert(false); // jww (2008-04-24): What to do here? + break; + } + + if (flags) + xdata.add_flags(flags); + + handler(xact); + } } void collapse_xacts::report_subtotal() @@ -405,12 +408,16 @@ void component_xacts::operator()(xact_t& xact) void subtotal_xacts::report_subtotal(const char * spec_fmt) { std::ostringstream out_date; - if (! spec_fmt) { + if (spec_fmt) { + out_date << format_date(finish, string(spec_fmt)); + } + else if (date_format) { string fmt = "- "; - fmt += output_date_format; + fmt += *date_format; out_date << format_date(finish, string(fmt)); - } else { - out_date << format_date(finish, string(spec_fmt)); + } + else { + out_date << format_date(finish); } entry_temps.push_back(entry_t()); @@ -428,9 +435,9 @@ void subtotal_xacts::report_subtotal(const char * spec_fmt) void subtotal_xacts::operator()(xact_t& xact) { if (! is_valid(start) || xact.date() < start) - start = *xact.date(); + start = xact.date(); if (! is_valid(finish) || xact.date() > finish) - finish = *xact.date(); + finish = xact.date(); account_t * acct = xact.reported_account(); assert(acct); @@ -470,7 +477,7 @@ void interval_xacts::report_subtotal(const date_t& date) if (is_valid(date)) finish = date - gregorian::days(1); else - finish = *last_xact->date(); + finish = last_xact->date(); subtotal_xacts::report_subtotal(); @@ -479,7 +486,7 @@ void interval_xacts::report_subtotal(const date_t& date) void interval_xacts::operator()(xact_t& xact) { - const date_t& date(*xact.date()); + const date_t& date(xact.date()); if ((is_valid(interval.begin) && date < interval.begin) || (is_valid(interval.end) && date >= interval.end)) @@ -550,7 +557,7 @@ void by_payee_xacts::operator()(xact_t& xact) } if (xact.date() > (*i).second->start) - (*i).second->start = *xact.date(); + (*i).second->start = xact.date(); (*(*i).second)(xact); } @@ -691,7 +698,7 @@ void budget_xacts::operator()(xact_t& xact) handle: if (xact_in_budget && flags & BUDGET_BUDGETED) { - report_budget_items(*xact.date()); + report_budget_items(xact.date()); item_handler<xact_t>::operator()(xact); } else if (! xact_in_budget && flags & BUDGET_UNBUDGETED) { @@ -705,10 +712,10 @@ void forecast_xacts::add_xact(const interval_t& period, xact_t& xact) interval_t& i = pending_xacts.back().first; if (! is_valid(i.begin)) { - i.set_start(current_date); + i.set_start(CURRENT_DATE()); i.begin = i.increment(i.begin); } else { - while (i.begin < current_date) + while (i.begin < CURRENT_DATE()) i.begin = i.increment(i.begin); } } @@ -758,7 +765,7 @@ void forecast_xacts::flush() temp.xdata().has_flags(XACT_EXT_MATCHES)) { if (! pred(temp)) break; - last = *temp.date(); + last = temp.date(); passed.clear(); } else { bool found = false; @@ -779,15 +786,16 @@ void forecast_xacts::flush() item_handler<xact_t>::flush(); } -pass_down_accounts::pass_down_accounts(acct_handler_ptr handler, - accounts_iterator& iter, - const expr_t& predicate) +pass_down_accounts::pass_down_accounts + (acct_handler_ptr handler, + accounts_iterator& iter, + const optional<item_predicate<account_t> >& predicate) : item_handler<account_t>(handler), pred(predicate) { TRACE_CTOR(pass_down_accounts, "acct_handler_ptr, accounts_iterator"); for (account_t * account = iter(); account; account = iter()) - if (pred(*account)) + if (! pred || (*pred)(*account)) item_handler<account_t>::operator()(*account); item_handler<account_t>::flush(); diff --git a/src/filters.h b/src/filters.h index 50e02a50..c6ae1a4d 100644 --- a/src/filters.h +++ b/src/filters.h @@ -273,18 +273,11 @@ class filter_xacts : public item_handler<xact_t> filter_xacts(); public: - filter_xacts(xact_handler_ptr handler, - const expr_t& predicate) + filter_xacts(xact_handler_ptr handler, + const item_predicate<xact_t>& predicate) : item_handler<xact_t>(handler), pred(predicate) { TRACE_CTOR(filter_xacts, - "xact_handler_ptr, const value_expr&"); - } - - filter_xacts(xact_handler_ptr handler, - const string& predicate) - : item_handler<xact_t>(handler), pred(predicate) { - TRACE_CTOR(filter_xacts, - "xact_handler_ptr, const string&"); + "xact_handler_ptr, const item_predicate<xact_t>&"); } virtual ~filter_xacts() { TRACE_DTOR(filter_xacts); @@ -421,17 +414,11 @@ class component_xacts : public item_handler<xact_t> component_xacts(); public: - component_xacts(xact_handler_ptr handler, - const expr_t& predicate) - : item_handler<xact_t>(handler), pred(predicate) { - TRACE_CTOR(component_xacts, - "xact_handler_ptr, const value_expr&"); - } - component_xacts(xact_handler_ptr handler, - const string& predicate) + component_xacts(xact_handler_ptr handler, + const item_predicate<xact_t>& predicate) : item_handler<xact_t>(handler), pred(predicate) { TRACE_CTOR(component_xacts, - "xact_handler_ptr, const string&"); + "xact_handler_ptr, const item_predicate<xact_t>&"); } virtual ~component_xacts() throw() { TRACE_DTOR(component_xacts); @@ -507,7 +494,7 @@ public: virtual void flush() { if (last_xact) { - output_diff(current_date); + output_diff(CURRENT_DATE()); last_xact = NULL; } item_handler<xact_t>::flush(); @@ -557,9 +544,9 @@ class subtotal_xacts : public item_handler<xact_t> subtotal_xacts(); protected: - values_map values; - bool remember_components; - + values_map values; + bool remember_components; + optional<string> date_format; std::list<entry_t> entry_temps; std::list<xact_t> xact_temps; @@ -568,9 +555,11 @@ public: date_t finish; subtotal_xacts(xact_handler_ptr handler, - bool _remember_components = false) + bool _remember_components = false, + optional<string> _date_format = none) : item_handler<xact_t>(handler), - remember_components(_remember_components) { + remember_components(_remember_components), + date_format(_date_format) { TRACE_CTOR(subtotal_xacts, "xact_handler_ptr, bool"); } @@ -735,7 +724,7 @@ public: virtual void flush(); virtual void operator()(xact_t& xact) { - days_of_the_week[xact.date()->day_of_week()].push_back(&xact); + days_of_the_week[xact.date().day_of_week()].push_back(&xact); } }; @@ -812,13 +801,11 @@ class forecast_xacts : public generate_xacts item_predicate<xact_t> pred; public: - forecast_xacts(xact_handler_ptr handler, const expr_t& predicate) - : generate_xacts(handler), pred(predicate) { - TRACE_CTOR(forecast_xacts, "xact_handler_ptr, const expr_t&"); - } - forecast_xacts(xact_handler_ptr handler, const string& predicate) + forecast_xacts(xact_handler_ptr handler, + const item_predicate<xact_t>& predicate) : generate_xacts(handler), pred(predicate) { - TRACE_CTOR(forecast_xacts, "xact_handler_ptr, const string&"); + TRACE_CTOR(forecast_xacts, + "xact_handler_ptr, const item_predicate<xact_t>&"); } virtual ~forecast_xacts() throw() { TRACE_DTOR(forecast_xacts); @@ -858,12 +845,12 @@ class pass_down_accounts : public item_handler<account_t> { pass_down_accounts(); - item_predicate<account_t> pred; + optional<item_predicate<account_t> > pred; public: pass_down_accounts(acct_handler_ptr handler, - accounts_iterator& iter, - const expr_t& predicate = expr_t()); + accounts_iterator& iter, + const optional<item_predicate<account_t> >& predicate = none); virtual ~pass_down_accounts() { TRACE_DTOR(pass_down_accounts); diff --git a/src/format.cc b/src/format.cc index eeaf6fe4..c4683876 100644 --- a/src/format.cc +++ b/src/format.cc @@ -34,13 +34,6 @@ namespace ledger { -format_t::elision_style_t - format_t::elision_style = ABBREVIATE; -int format_t::abbrev_length = 2; - -bool format_t::ansi_codes = false; -bool format_t::ansi_invert = false; - void format_t::element_t::dump(std::ostream& out) const { out << "Element: "; @@ -295,7 +288,8 @@ void format_t::format(std::ostream& out_str, scope_t& scope) value = elem->expr.calc(scope); } DEBUG("format.expr", "value = (" << value << ")"); - value.strip_annotations().dump(out, elem->min_width); + + value.print(out, elem->min_width); } catch (const calc_error&) { add_error_context("While calculating format expression:"); @@ -326,7 +320,7 @@ void format_t::format(std::ostream& out_str, scope_t& scope) } string format_t::truncate(const unistring& ustr, std::size_t width, - const bool is_account) + const int account_abbrev_length) { assert(width < 4095); @@ -336,7 +330,11 @@ string format_t::truncate(const unistring& ustr, std::size_t width, std::ostringstream buf; - switch (elision_style) { + elision_style_t style = TRUNCATE_TRAILING; + if (account_abbrev_length > 0) + style = ABBREVIATE; + + switch (style) { case TRUNCATE_LEADING: // This method truncates at the beginning. buf << ".." << ustr.extract(len - width, width); @@ -351,7 +349,7 @@ string format_t::truncate(const unistring& ustr, std::size_t width, break; case ABBREVIATE: - if (is_account) { + if (account_abbrev_length > 0) { std::list<string> parts; string::size_type beg = 0; string strcopy(ustr.extract()); @@ -376,8 +374,8 @@ string format_t::truncate(const unistring& ustr, std::size_t width, if (newlen > width) { unistring temp(*i); - result << temp.extract(0, abbrev_length) << ":"; - newlen -= temp.length() - abbrev_length; + result << temp.extract(0, account_abbrev_length) << ":"; + newlen -= temp.length() - account_abbrev_length; } else { result << *i << ":"; } diff --git a/src/format.h b/src/format.h index 439cb70b..66c226b2 100644 --- a/src/format.h +++ b/src/format.h @@ -169,13 +169,6 @@ public: }; private: - // jww (2008-08-02): Should these four be here, or in session_t? - static elision_style_t elision_style; - static int abbrev_length; - - static bool ansi_codes; - static bool ansi_invert; - static element_t * parse_elements(const string& fmt); friend class report_t; @@ -207,7 +200,7 @@ public: } static string truncate(const unistring& str, std::size_t width, - const bool is_account = false); + const int account_abbrev_length = -1); }; } // namespace ledger diff --git a/src/item.cc b/src/item.cc index 27d28782..a4e2f2f1 100644 --- a/src/item.cc +++ b/src/item.cc @@ -30,8 +30,6 @@ */ #include "item.h" -#include "session.h" -#include "report.h" namespace ledger { @@ -69,7 +67,7 @@ void item_t::set_tag(const string& tag, assert(result.second); } -void item_t::parse_tags(const char * p) +void item_t::parse_tags(const char * p, int current_year) { if (char * b = std::strchr(p, '[')) { if (char * e = std::strchr(p, ']')) { @@ -79,10 +77,10 @@ void item_t::parse_tags(const char * p) if (char * p = std::strchr(buf, '=')) { *p++ = '\0'; - _date_eff = parse_date(p); + _date_eff = parse_date(p, current_year); } if (buf[0]) - _date = parse_date(buf); + _date = parse_date(buf, current_year); } } @@ -114,7 +112,7 @@ void item_t::parse_tags(const char * p) } } -void item_t::append_note(const char * p) +void item_t::append_note(const char * p, int current_year) { if (note) *note += p; @@ -122,7 +120,7 @@ void item_t::append_note(const char * p) note = p; *note += '\n'; - parse_tags(p); + parse_tags(p, current_year); } namespace { @@ -250,14 +248,6 @@ value_t get_comment(item_t& item) } } -optional<date_t> item_t::date() const -{ - if (use_effective_date && _date_eff) - return effective_date(); - else - return actual_date(); -} - expr_t::ptr_op_t item_t::lookup(const string& name) { switch (name[0]) { @@ -321,7 +311,7 @@ expr_t::ptr_op_t item_t::lookup(const string& name) break; } - return session_t::current->global_scope->lookup(name); + return expr_t::ptr_op_t(); } bool item_t::valid() const @@ -126,16 +126,16 @@ public: virtual optional<string> get_tag(const string& tag) const; virtual void set_tag(const string& tag, const optional<string>& value = none); - virtual void parse_tags(const char * p); - virtual void append_note(const char * p); + virtual void parse_tags(const char * p, int current_year = -1); + virtual void append_note(const char * p, int current_year = -1); - virtual optional<date_t> actual_date() const { - return _date; + virtual date_t date() const { + assert(_date); + return *_date; } virtual optional<date_t> effective_date() const { return _date_eff; } - optional<date_t> date() const; void set_state(state_t new_state) { _state = new_state; diff --git a/src/journal.cc b/src/journal.cc index c1da2fc9..403e7c81 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -36,12 +36,6 @@ namespace ledger { const string version = PACKAGE_VERSION; -journal_t::journal_t() : basket(NULL) -{ - TRACE_CTOR(journal_t, ""); - master = session_t::current->master.get(); -} - journal_t::~journal_t() { TRACE_DTOR(journal_t); @@ -70,22 +64,22 @@ journal_t::~journal_t() void journal_t::add_account(account_t * acct) { - session_t::current->add_account(acct); + master->add_account(acct); } bool journal_t::remove_account(account_t * acct) { - return session_t::current->remove_account(acct); + return master->remove_account(acct); } account_t * journal_t::find_account(const string& name, bool auto_create) { - return session_t::current->find_account(name, auto_create); + return master->find_account(name, auto_create); } account_t * journal_t::find_account_re(const string& regexp) { - return session_t::current->find_account_re(regexp); + return master->find_account_re(regexp); } bool journal_t::add_entry(entry_t * entry) diff --git a/src/journal.h b/src/journal.h index ff060d76..1b5607cc 100644 --- a/src/journal.h +++ b/src/journal.h @@ -76,7 +76,9 @@ public: hooks_t<entry_finalizer_t, entry_t> entry_finalize_hooks; - journal_t(); + journal_t(account_t * _master = NULL) : master(_master) { + TRACE_CTOR(journal_t, ""); + } ~journal_t(); // These four methods are delegated to the current session, since all @@ -72,28 +72,10 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope) return intermediate; } -namespace { - expr_t::ptr_op_t context_op_ptr; -} - -value_t expr_t::op_t::calc(scope_t& scope) +value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * context) { try { - context_op_ptr = ptr_op_t(); - return opcalc(scope); - } - catch (const std::exception& err) { - if (context_op_ptr) { - add_error_context("While evaluating value expression:"); - add_error_context(op_context(this, context_op_ptr)); - } - throw; - } -} -value_t expr_t::op_t::opcalc(scope_t& scope) -{ - try { switch (kind) { case VALUE: return as_value(); @@ -101,7 +83,7 @@ value_t expr_t::op_t::opcalc(scope_t& scope) case IDENT: if (! left()) throw_(calc_error, "Unknown identifier '" << as_ident() << "'"); - return left()->opcalc(scope); + return left()->calc(scope, context); case FUNCTION: { // Evaluating a FUNCTION is the same as calling it directly; this happens @@ -136,7 +118,7 @@ value_t expr_t::op_t::opcalc(scope_t& scope) call_scope_t call_args(scope); if (has_right()) - call_args.set_args(right()->opcalc(scope)); + call_args.set_args(right()->calc(scope, context)); ptr_op_t func = left(); const string& name(func->as_ident()); @@ -153,7 +135,8 @@ value_t expr_t::op_t::opcalc(scope_t& scope) if (! right()->is_value() || ! right()->as_value().is_mask()) throw_(calc_error, "Right-hand argument to match operator must be a regex"); #endif - return right()->opcalc(scope).as_mask().match(left()->opcalc(scope).to_string()); + return (right()->calc(scope, context).as_mask() + .match(left()->calc(scope, context).to_string())); case INDEX: { const call_scope_t& args(downcast<const call_scope_t>(scope)); @@ -166,42 +149,43 @@ value_t expr_t::op_t::opcalc(scope_t& scope) } case O_EQ: - return left()->opcalc(scope) == right()->opcalc(scope); + return left()->calc(scope, context) == right()->calc(scope, context); case O_LT: - return left()->opcalc(scope) < right()->opcalc(scope); + return left()->calc(scope, context) < right()->calc(scope, context); case O_LTE: - return left()->opcalc(scope) <= right()->opcalc(scope); + return left()->calc(scope, context) <= right()->calc(scope, context); case O_GT: - return left()->opcalc(scope) > right()->opcalc(scope); + return left()->calc(scope, context) > right()->calc(scope, context); case O_GTE: - return left()->opcalc(scope) >= right()->opcalc(scope); + return left()->calc(scope, context) >= right()->calc(scope, context); case O_ADD: - return left()->opcalc(scope) + right()->opcalc(scope); + return left()->calc(scope, context) + right()->calc(scope, context); case O_SUB: - return left()->opcalc(scope) - right()->opcalc(scope); + return left()->calc(scope, context) - right()->calc(scope, context); case O_MUL: - return left()->opcalc(scope) * right()->opcalc(scope); + return left()->calc(scope, context) * right()->calc(scope, context); case O_DIV: - return left()->opcalc(scope) / right()->opcalc(scope); + return left()->calc(scope, context) / right()->calc(scope, context); case O_NEG: - return left()->opcalc(scope).negate(); + return left()->calc(scope, context).negate(); case O_NOT: - return ! left()->opcalc(scope); + return ! left()->calc(scope, context); case O_AND: - return ! left()->opcalc(scope) ? value_t(false) : right()->opcalc(scope); + return (! left()->calc(scope, context) ? value_t(false) : + right()->calc(scope, context)); case O_OR: - if (value_t temp = left()->opcalc(scope)) + if (value_t temp = left()->calc(scope, context)) return temp; else - return right()->opcalc(scope); + return right()->calc(scope, context); case O_COMMA: { - value_t result(left()->opcalc(scope)); + value_t result(left()->calc(scope, context)); ptr_op_t next = right(); while (next) { @@ -214,7 +198,7 @@ value_t expr_t::op_t::opcalc(scope_t& scope) next = NULL; } - result.push_back(value_op->opcalc(scope)); + result.push_back(value_op->calc(scope, context)); } return result; } @@ -226,10 +210,11 @@ value_t expr_t::op_t::opcalc(scope_t& scope) } return NULL_VALUE; + } - catch (const std::exception& err) { - if (! context_op_ptr) - context_op_ptr = this; + catch (const std::exception& err) { + if (context && ! *context) + *context = this; throw; } } @@ -277,8 +277,7 @@ private: public: ptr_op_t compile(scope_t& scope); - value_t calc(scope_t& scope); - value_t opcalc(scope_t& scope); + value_t calc(scope_t& scope, ptr_op_t * context = NULL); struct context_t { diff --git a/src/output.cc b/src/output.cc index c24bf111..f833b722 100644 --- a/src/output.cc +++ b/src/output.cc @@ -62,16 +62,21 @@ void format_xacts::operator()(xact_t& xact) if (! xact.has_xdata() || ! xact.xdata().has_flags(XACT_EXT_DISPLAYED)) { if (last_entry != xact.entry) { - if (last_entry) - between_format.format(out, *last_entry); - first_line_format.format(out, xact); + if (last_entry) { + bind_scope_t bound_scope(report, *last_entry); + between_format.format(out, bound_scope); + } + bind_scope_t bound_scope(report, xact); + first_line_format.format(out, bound_scope); last_entry = xact.entry; } else if (last_xact && last_xact->date() != xact.date()) { - first_line_format.format(out, xact); + bind_scope_t bound_scope(report, xact); + first_line_format.format(out, bound_scope); } else { - next_lines_format.format(out, xact); + bind_scope_t bound_scope(report, xact); + next_lines_format.format(out, bound_scope); } xact.xdata().add_flags(XACT_EXT_DISPLAYED); @@ -88,10 +93,12 @@ void format_entries::format_last_entry() if (xact->has_xdata() && xact->xdata().has_flags(XACT_EXT_TO_DISPLAY)) { if (first) { - first_line_format.format(out, *xact); + bind_scope_t bound_scope(report, *xact); + first_line_format.format(out, bound_scope); first = false; } else { - next_lines_format.format(out, *xact); + bind_scope_t bound_scope(report, *xact); + next_lines_format.format(out, bound_scope); } xact->xdata().add_flags(XACT_EXT_DISPLAYED); } @@ -155,7 +162,8 @@ void format_accounts::flush() if (! report.show_collapsed && xdata.total) { out << "--------------------\n"; xdata.value = xdata.total; - format.format(out, *report.session.master); + bind_scope_t bound_scope(report, *report.session.master); + format.format(out, bound_scope); } } @@ -168,7 +176,8 @@ void format_accounts::operator()(account_t& account) if (! account.parent) { account.xdata().add_flags(ACCOUNT_EXT_TO_DISPLAY); } else { - format.format(report.output_stream, account); + bind_scope_t bound_scope(report, account); + format.format(report.output_stream, bound_scope); account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED); } } @@ -245,8 +254,9 @@ format_equity::format_equity(report_t& _report, const string& _format) entry_t header_entry; header_entry.payee = "Opening Balances"; - header_entry._date = current_date; - first_line_format.format(report.output_stream, header_entry); + header_entry._date = CURRENT_DATE(); + bind_scope_t bound_scope(report, header_entry); + first_line_format.format(report.output_stream, bound_scope); } void format_equity::flush() @@ -270,10 +280,12 @@ void format_equity::flush() foreach (balance_t::amounts_map::value_type pair, bal->amounts) { xdata.value = pair.second; xdata.value.negate(); - next_lines_format.format(out, summary); + bind_scope_t bound_scope(report, summary); + next_lines_format.format(out, bound_scope); } } else { - next_lines_format.format(out, summary); + bind_scope_t bound_scope(report, summary); + next_lines_format.format(out, bound_scope); } out.flush(); } @@ -297,11 +309,13 @@ void format_equity::operator()(account_t& account) foreach (balance_t::amounts_map::value_type pair, bal->amounts) { account.xdata().value = pair.second; - next_lines_format.format(out, account); + bind_scope_t bound_scope(report, account); + next_lines_format.format(out, bound_scope); } account.xdata().value = val; } else { - next_lines_format.format(out, account); + bind_scope_t bound_scope(report, account); + next_lines_format.format(out, bound_scope); } total += val; } diff --git a/src/precmd.cc b/src/precmd.cc index 5cefe435..fd6a2457 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -63,7 +63,7 @@ value_t parse_command(call_scope_t& args) out << std::endl << "--- Calculated value ---" << std::endl; value_t result(expr.calc(args)); - result.print(out); + result.dump(out); out << std::endl; return 0L; @@ -82,7 +82,8 @@ value_t eval_command(call_scope_t& args) std::ostream& out(report.output_stream); expr_t expr(*arg); - out << expr.calc(args).strip_annotations() << std::endl; + out << expr.calc(args).strip_annotations(report.what_to_keep) + << std::endl; return 0L; } @@ -150,7 +151,7 @@ value_t args_command(call_scope_t& args) value_t::sequence_t::const_iterator end = args.value().end(); out << "--- Input arguments ---" << std::endl; - args.value().print(out); + args.value().dump(out); out << std::endl << std::endl; string predicate = args_to_predicate_expr(begin, end); diff --git a/src/predicate.h b/src/predicate.h index 5e2057ca..dfdcd256 100644 --- a/src/predicate.h +++ b/src/predicate.h @@ -60,26 +60,32 @@ template <typename T> class item_predicate { public: - expr_t predicate; + expr_t predicate; + keep_details_t what_to_keep; item_predicate() { TRACE_CTOR(item_predicate, ""); } - item_predicate(const item_predicate& other) : predicate(other.predicate) { + item_predicate(const item_predicate& other) + : predicate(other.predicate), what_to_keep(other.what_to_keep) { TRACE_CTOR(item_predicate, "copy"); } - item_predicate(const expr_t& _predicate) : predicate(_predicate) { - TRACE_CTOR(item_predicate, "const expr_t&"); + item_predicate(const expr_t& _predicate, + const keep_details_t& _what_to_keep) + : predicate(_predicate), what_to_keep(_what_to_keep) { + TRACE_CTOR(item_predicate, "const expr_t&, const keep_details_t&"); } - item_predicate(const string& _predicate) : predicate(expr_t(_predicate)) { - TRACE_CTOR(item_predicate, "const string&"); + item_predicate(const string& _predicate, + const keep_details_t& _what_to_keep) + : predicate(expr_t(_predicate)), what_to_keep(_what_to_keep) { + TRACE_CTOR(item_predicate, "const string&, const keep_details_t&"); } ~item_predicate() throw() { TRACE_DTOR(item_predicate); } bool operator()(T& item) { - return ! predicate || predicate.calc(item).strip_annotations(); + return ! predicate || predicate.calc(item).strip_annotations(what_to_keep); } }; diff --git a/src/report.cc b/src/report.cc index f72798ec..6a75632d 100644 --- a/src/report.cc +++ b/src/report.cc @@ -42,18 +42,14 @@ void report_t::xacts_report(xact_handler_ptr handler) { session_xacts_iterator walker(session); pass_down_xacts(chain_xact_handlers(*this, handler), walker); - - if (DO_VERIFY()) - session.clean_xacts(); + session.clean_xacts(); } void report_t::entry_report(xact_handler_ptr handler, entry_t& entry) { entry_xacts_iterator walker(entry); pass_down_xacts(chain_xact_handlers(*this, handler), walker); - - if (DO_VERIFY()) - session.clean_xacts(entry); + session.clean_xacts(entry); } void report_t::sum_all_accounts() @@ -62,7 +58,8 @@ void report_t::sum_all_accounts() pass_down_xacts (chain_xact_handlers(*this, xact_handler_ptr(new set_account_value), false), walker); - session.master->calculate_sums(amount_expr); + + session.master->calculate_sums(amount_expr, *this); } void report_t::accounts_report(acct_handler_ptr handler) @@ -71,16 +68,16 @@ void report_t::accounts_report(acct_handler_ptr handler) if (sort_string.empty()) { basic_accounts_iterator walker(*session.master); - pass_down_accounts(handler, walker, expr_t("total")); + pass_down_accounts(handler, walker, + item_predicate<account_t>("total", what_to_keep)); } else { sorted_accounts_iterator walker(*session.master, sort_string); - pass_down_accounts(handler, walker, expr_t("total")); + pass_down_accounts(handler, walker, + item_predicate<account_t>("total", what_to_keep)); } - if (DO_VERIFY()) { - session.clean_xacts(); - session.clean_accounts(); - } + session.clean_xacts(); + session.clean_accounts(); } void report_t::commodities_report(const string& format) @@ -125,6 +122,8 @@ value_t report_t::f_market_value(call_scope_t& args) namespace { value_t print_balance(call_scope_t& args) { + report_t& report(find_scope<report_t>(args)); + var_t<long> first_width(args, 1); var_t<long> latter_width(args, 2); #if 0 @@ -132,10 +131,40 @@ namespace { #endif std::ostringstream out; - args[0].strip_annotations().dump(out, *first_width, *latter_width); + args[0].strip_annotations(report.what_to_keep) + .print(out, *first_width, *latter_width); return string_value(out.str()); } + value_t strip_annotations(call_scope_t& args) + { + report_t& report(find_scope<report_t>(args)); + return args[0].strip_annotations(report.what_to_keep); + } + + value_t truncate(call_scope_t& args) + { + report_t& report(find_scope<report_t>(args)); + + var_t<long> width(args, 1); + var_t<long> account_abbrev(args, 2); + + return string_value(format_t::truncate(args[0].as_string(), *width, + account_abbrev ? *account_abbrev : -1)); + } + + value_t display_date(call_scope_t& args) + { + report_t& report(find_scope<report_t>(args)); + item_t& item(find_scope<item_t>(args)); + + if (item.use_effective_date) { + if (optional<date_t> date = item.effective_date()) + return string_value(format_date(*date, report.output_date_format)); + } + return string_value(format_date(item.date(), report.output_date_format)); + } + template <class Type = xact_t, class handler_ptr = xact_handler_ptr, void (report_t::*report_method)(handler_ptr) = @@ -178,13 +207,14 @@ expr_t::ptr_op_t report_t::lookup(const string& name) case 'd': if (std::strcmp(p, "display_total") == 0) return MAKE_FUNCTOR(report_t::get_display_total); + else if (std::strcmp(p, "display_date") == 0) + return WRAP_FUNCTOR(display_date); break; case 'l': if (std::strncmp(p, "ledger_cmd_", 11) == 0) { -#define FORMAT(str) \ - (format_string.empty() ? session. str : format_string) +#define FORMAT(str) (format_string.empty() ? session. str : format_string) #if 0 // Commands yet to implement: @@ -313,6 +343,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return MAKE_FUNCTOR(report_t::option_dow); else if (std::strcmp(p, "date-format_") == 0) return MAKE_FUNCTOR(report_t::option_date_format_); + else if (std::strcmp(p, "debug_") == 0) + return MAKE_FUNCTOR(report_t::option_ignore_); break; case 'e': @@ -409,6 +441,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return MAKE_FUNCTOR(report_t::option_totals); else if (std::strcmp(p, "tail_") == 0) return MAKE_FUNCTOR(report_t::option_tail_); + else if (std::strcmp(p, "trace_") == 0) + return MAKE_FUNCTOR(report_t::option_ignore_); break; case 'u': @@ -416,6 +450,13 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return MAKE_FUNCTOR(report_t::option_uncleared); break; + case 'v': + if (! *(p + 1) || std::strcmp(p, "verbose") == 0) + return MAKE_FUNCTOR(report_t::option_ignore); + else if (std::strcmp(p, "verify") == 0) + return MAKE_FUNCTOR(report_t::option_ignore); + break; + case 'w': if (std::strcmp(p, "weekly") == 0) return MAKE_FUNCTOR(report_t::option_weekly); @@ -516,11 +557,16 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return WRAP_FUNCTOR(print_balance); break; + case 's': + if (std::strcmp(p, "strip") == 0) + return WRAP_FUNCTOR(strip_annotations); + break; + case 't': if (std::strcmp(p, "total_expr") == 0) return MAKE_FUNCTOR(report_t::get_total_expr); else if (std::strcmp(p, "truncate") == 0) - return MAKE_FUNCTOR(report_t::get_total_expr); + return WRAP_FUNCTOR(truncate); break; } diff --git a/src/report.h b/src/report.h index d8796741..e7e2ac0e 100644 --- a/src/report.h +++ b/src/report.h @@ -109,7 +109,7 @@ public: output_stream_t output_stream; string format_string; - string date_output_format; + string output_date_format; string predicate; string secondary_predicate; string display_predicate; @@ -143,12 +143,12 @@ public: 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; bool anonymize; + bool use_effective_date; + + keep_details_t what_to_keep; string account; optional<path> pager_path; @@ -158,7 +158,9 @@ public: session_t& session; explicit report_t(session_t& _session) - : amount_expr("amount"), + : output_date_format("%y-%b-%d"), + + amount_expr("amount"), total_expr("total"), display_total("total_expr"), @@ -178,12 +180,10 @@ public: 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), anonymize(false), + use_effective_date(false), raw_mode(false), @@ -194,6 +194,7 @@ public: virtual ~report_t() { TRACE_DTOR(report_t); + output_stream.close(); } // @@ -268,15 +269,23 @@ public: value_t option_account(call_scope_t& args) { // a: config->account = optarg; } +#endif ////////////////////////////////////////////////////////////////////// // // Report filtering + value_t option_ignore(call_scope_t& args) { + return true; + } + value_t option_ignore_(call_scope_t& args) { + return true; + } + value_t option_effective(call_scope_t& args) { - item_t::use_effective_date = true; + use_effective_date = true; + return true; } -#endif value_t option_begin_(call_scope_t& args) { // b: interval_t interval(args[0].to_string()); @@ -348,21 +357,21 @@ public: } value_t option_lots(call_scope_t& args) { - report->keep_price = - report->keep_date = - report->keep_tag = true; + what_to_keep.keep_price = true; + what_to_keep.keep_date = true; + what_to_keep.keep_tag = true; } value_t option_lot_prices(call_scope_t& args) { - report->keep_price = true; + what_to_keep.keep_price = true; } value_t option_lot_dates(call_scope_t& args) { - report->keep_date = true; + what_to_keep.keep_date = true; } value_t option_lot_tags(call_scope_t& args) { - report->keep_tag = true; + what_to_keep.keep_tag = true; } #endif @@ -376,7 +385,7 @@ public: } value_t option_date_format_(call_scope_t& args) { // y: - ledger::output_date_format = args[0].as_string(); + output_date_format = args[0].as_string(); return true; } @@ -710,14 +719,18 @@ public: } value_t option_ansi(call_scope_t& args) { +#if 0 format_t::ansi_codes = true; format_t::ansi_invert = false; +#endif return true; } value_t option_ansi_invert(call_scope_t& args) { +#if 0 format_t::ansi_codes = format_t::ansi_invert = true; +#endif return true; } @@ -726,7 +739,7 @@ public: // Commodity reporting value_t option_base(call_scope_t& args) { // : - amount_t::keep_base = true; + what_to_keep.keep_base = true; return true; } @@ -775,27 +788,25 @@ public: } #if 0 - 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--; - - std::string symbol(optarg, 0, equals - optarg); - amount_t price(equals + 1); - - if (commodity_t * commodity = commodity_t::find_or_create(symbol)) { - commodity->add_price(datetime_t::now, price); + 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--; + + std::string symbol(optarg, 0, equals - optarg); + amount_t price(equals + 1); + + if (commodity_t * commodity = commodity_t::find_or_create(symbol)) { + commodity->add_price(datetime_t::now, price); #if 0 - commodity->history()->bogus_time = datetime_t::now; + commodity->history()->bogus_time = datetime_t::now; #endif - } } } #endif diff --git a/src/scope.h b/src/scope.h index 174d52fe..3d1e5108 100644 --- a/src/scope.h +++ b/src/scope.h @@ -105,23 +105,6 @@ public: } }; - -template <typename T> -inline T& find_scope(child_scope_t& scope, bool skip_this = true) -{ - for (scope_t * ptr = (skip_this ? scope.parent : &scope); ptr; ) { - T * sought = dynamic_cast<T *>(ptr); - if (sought) - return *sought; - if (child_scope_t * scope = dynamic_cast<child_scope_t *>(ptr)) - ptr = scope->parent; - else - ptr = NULL; - } - throw_(std::runtime_error, "Could not find scope"); - return reinterpret_cast<T&>(scope); // never executed -} - /** * @brief Brief * @@ -209,6 +192,66 @@ public: * * Long. */ +class bind_scope_t : public child_scope_t +{ + bind_scope_t(); + +public: + scope_t& grandchild; + + explicit bind_scope_t(scope_t& _parent, + scope_t& _grandchild) + : child_scope_t(_parent), grandchild(_grandchild) { + TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&"); + } + virtual ~bind_scope_t() { + TRACE_DTOR(bind_scope_t); + } + + virtual expr_t::ptr_op_t lookup(const string& name) { + if (expr_t::ptr_op_t def = grandchild.lookup(name)) + return def; + return child_scope_t::lookup(name); + } +}; + +/** + * @brief Brief + * + * Long. + */ +template <typename T> +T * search_scope(scope_t * ptr) +{ + if (T * sought = dynamic_cast<T *>(ptr)) + return sought; + + if (bind_scope_t * scope = dynamic_cast<bind_scope_t *>(ptr)) { + if (T * sought = search_scope<T>(&scope->grandchild)) + return sought; + return search_scope<T>(scope->parent); + } + else if (child_scope_t * scope = dynamic_cast<child_scope_t *>(ptr)) { + return search_scope<T>(scope->parent); + } + return NULL; +} + +template <typename T> +inline T& find_scope(child_scope_t& scope, bool skip_this = true) +{ + if (T * sought = search_scope<T>(skip_this ? scope.parent : &scope)) + return *sought; + + throw_(std::runtime_error, "Could not find scope"); + return reinterpret_cast<T&>(scope); // never executed +} + +/** + * @brief Brief + * + * Long. + */ template <typename T> class ptr_t : public noncopyable { diff --git a/src/session.cc b/src/session.cc index 075324ec..25acd821 100644 --- a/src/session.cc +++ b/src/session.cc @@ -38,36 +38,32 @@ namespace ledger { -session_t * session_t::current = NULL; - #if 0 boost::mutex session_t::session_mutex; #endif void set_session_context(session_t * session) { + if (session) { #if 0 - session_t::session_mutex.lock(); + session_t::session_mutex.lock(); #endif + amount_t::initialize(session->commodity_pool); + + // jww (2009-02-04): Is amount_t the right place for parse_conversion to + // happen? + amount_t::parse_conversion("1.0m", "60s"); + amount_t::parse_conversion("1.0h", "60m"); - if (session && ! session_t::current) { - session_t::initialize(); + value_t::initialize(); } - else if (! session && session_t::current) { - session_t::shutdown(); + else if (! session) { + value_t::shutdown(); + amount_t::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() @@ -77,17 +73,17 @@ session_t::session_t() saw_price_db_from_command_line(false), register_format - ("%-.9(date) %-.20(payee) %-.23(account(23)) %!12(print_balance(amount_expr, 12, 67)) " - "%!12(print_balance(display_total, 12, 80, true))\n%/" - "%31|%-.23(account(23)) %!12(print_balance(amount_expr, 12, 67)) " - "%!12(print_balance(display_total, 12, 80, true))\n"), + ("%-.9(display_date) %-.20(payee) %-.23(truncate(account, 23, 2)) %!12(print_balance(strip(amount_expr), 12, 67)) " + "%!12(print_balance(strip(display_total), 12, 80, true))\n%/" + "%31|%-.23(truncate(account, 23, 2)) %!12(print_balance(strip(amount_expr), 12, 67)) " + "%!12(print_balance(strip(display_total), 12, 80, true))\n"), wide_register_format ("%-.9D %-.35P %-.39A %22.108t %!22.132T\n%/" "%48|%-.38A %22.108t %!22.132T\n"), print_format - ("%(date)%(cleared ? \" *\" : (uncleared ? \"\" : \" !\"))%(code ? \" (\" + code + \")\" : \"\") %(payee)%(entry.comment | \"\")\n %-34(account) %12(amount)%(comment | \"\")\n%/ %-34(account) %12(amount)%(comment | \"\")\n%/\n"), + ("%(display_date)%(cleared ? \" *\" : (uncleared ? \"\" : \" !\"))%(code ? \" (\" + code + \")\" : \"\") %(payee)%(entry.comment | \"\")\n %-34(account) %12(amount)%(comment | \"\")\n%/ %-34(account) %12(amount)%(comment | \"\")\n%/\n"), balance_format - ("%20(display_total) %(depth_spacer)%-(partial_account)\n"), + ("%20(strip(display_total)) %(depth_spacer)%-(partial_account)\n"), equity_format ("\n%D %Y%C%P\n%/ %-34W %12t\n"), plot_amount_format @@ -104,13 +100,12 @@ session_t::session_t() ("P %[%Y/%m/%d %H:%M:%S] %A %t\n"), pricing_leeway(24 * 3600), + current_year(CURRENT_DATE().year()), download_quotes(false), use_cache(true), cache_dirty(false), - now(now), - #if 0 elision_style(ABBREVIATE), #endif @@ -119,6 +114,7 @@ session_t::session_t() ansi_codes(false), ansi_invert(false), + commodity_pool(new commodity_pool_t), master(new account_t(NULL, "")) { TRACE_CTOR(session_t, ""); @@ -132,6 +128,14 @@ session_t::session_t() cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache"; register_parser(new textual_parser_t); + + // Add time commodity conversions, so that timelog's may be parsed + // in terms of seconds, but reported as minutes or hours. + if (commodity_t * commodity = commodity_pool->create("s")) { + commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); + } else { + assert(false); + } } session_t::~session_t() @@ -217,7 +221,7 @@ std::size_t session_t::read_data(journal_t& journal, } if (entry_count == 0) { - account_t * acct = NULL; + account_t * acct = journal.master; if (! master_account.empty()) acct = journal.find_account(master_account); @@ -245,7 +249,7 @@ std::size_t session_t::read_data(journal_t& journal, std::ostringstream buffer; while (std::cin.good() && ! std::cin.eof()) { - static char line[8192]; + char line[8192]; std::cin.read(line, 8192); std::streamsize count = std::cin.gcount(); buffer.write(line, count); @@ -260,7 +264,6 @@ std::size_t session_t::read_data(journal_t& journal, entry_count += read_journal(journal, pathname, acct); if (journal.price_db) journal.sources.push_back(*journal.price_db); - clean_accounts(); } else { throw_(parse_error, "Could not open journal file '" << pathname << "'"); @@ -275,25 +278,6 @@ std::size_t session_t::read_data(journal_t& journal, return entry_count; } -namespace { - account_t * find_account_re_(account_t * account, const mask_t& regexp) - { - if (regexp.match(account->fullname())) - return account; - - foreach (accounts_map::value_type& pair, account->accounts) - if (account_t * a = find_account_re_(pair.second, regexp)) - return a; - - return NULL; - } -} - -account_t * session_t::find_account_re(const string& regexp) -{ - return find_account_re_(master.get(), mask_t(regexp)); -} - void session_t::clean_xacts() { session_xacts_iterator walker(*this); @@ -310,8 +294,7 @@ void session_t::clean_xacts(entry_t& entry) void session_t::clean_accounts() { basic_accounts_iterator acct_walker(*master); - pass_down_accounts(acct_handler_ptr(new clear_account_xdata), - acct_walker); + pass_down_accounts(acct_handler_ptr(new clear_account_xdata), acct_walker); } #if 0 @@ -423,20 +406,4 @@ expr_t::ptr_op_t session_t::lookup(const string& name) return expr_t::ptr_op_t(); } -// jww (2007-04-26): All of Ledger should be accessed through a -// session_t object -void session_t::initialize() -{ - amount_t::initialize(); - value_t::initialize(); - expr_t::initialize(); -} - -void session_t::shutdown() -{ - expr_t::shutdown(); - value_t::shutdown(); - amount_t::shutdown(); -} - } // namespace ledger diff --git a/src/session.h b/src/session.h index ea5538c4..2b33cb8d 100644 --- a/src/session.h +++ b/src/session.h @@ -60,15 +60,9 @@ namespace ledger { */ class session_t : public noncopyable, public scope_t { - static void initialize(); - static void shutdown(); - friend void set_session_context(session_t * session); - friend void release_session_context(); public: - static session_t * current; - scope_t * global_scope; std::list<path> data_files; @@ -93,25 +87,22 @@ public: string pricesdb_format; std::size_t pricing_leeway; + int current_year; bool download_quotes; bool use_cache; bool cache_dirty; - datetime_t now; - date_t today; - format_t::elision_style_t elision_style; int abbrev_length; bool ansi_codes; bool ansi_invert; - ptr_list<journal_t> journals; + shared_ptr<commodity_pool_t> commodity_pool; ptr_list<journal_t::parser_t> parsers; - scoped_ptr<commodity_pool_t> commdity_pool; + ptr_list<journal_t> journals; scoped_ptr<account_t> master; - mutable accounts_map accounts_cache; session_t(); virtual ~session_t(); @@ -122,7 +113,7 @@ public: } journal_t * create_journal() { - journal_t * journal = new journal_t; + journal_t * journal = new journal_t(master.get()); journals.push_back(journal); return journal; } @@ -177,17 +168,6 @@ public: return master->remove_account(acct); } - account_t * find_account(const string& name, bool auto_create = true) { - accounts_map::iterator c = accounts_cache.find(name); - if (c != accounts_cache.end()) - return (*c).second; - - account_t * account = master->find_account(name, auto_create); - accounts_cache.insert(accounts_map::value_type(name, account)); - return account; - } - account_t * find_account_re(const string& regexp); - void clean_accounts(); void clean_xacts(); @@ -213,12 +193,6 @@ public: std::cout << "\n\nCopyright (c) 2003-2009, 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"; - std::cout << "\n(modules: gmp, pcre"; - std::cout << ", xml"; -#ifdef HAVE_LIBOFX - std::cout << ", ofx"; -#endif - std::cout << ")\n"; return NULL_VALUE; } diff --git a/src/textual.cc b/src/textual.cc index 28d8c36e..9dd79ae0 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -92,6 +92,8 @@ std::size_t textual_parser_t::parse(std::istream& in, original_file); parsing_instance.parse(); + session.clean_accounts(); // remove calculated totals + TRACE_STOP(parsing_total, 1); TRACE_FINISH(instance_parse, 1); // report per-instance timers @@ -107,7 +109,8 @@ namespace { #else void #endif - parse_amount_expr(std::istream& in, + parse_amount_expr(session_t& session, + std::istream& in, amount_t& amount, xact_t * xact, uint_least8_t flags = 0) @@ -126,7 +129,9 @@ namespace { #endif if (expr) { - value_t result(expr.calc(*xact)); + bind_scope_t bound_scope(session, *xact); + + value_t result(expr.calc(bound_scope)); if (! result.is_amount()) throw_(parse_error, "Transactions may only specify simple amounts"); @@ -369,7 +374,7 @@ void textual_parser_t::instance_t::clock_in_directive(char * line, char * p = skip_ws(line + 22); char * n = next_element(p, true); - timelog.clock_in(parse_datetime(date), + timelog.clock_in(parse_datetime(date, session.current_year), account_stack.front()->find_account(p), n ? n : ""); } @@ -381,7 +386,7 @@ void textual_parser_t::instance_t::clock_out_directive(char * line, char * p = skip_ws(line + 22); char * n = next_element(p, true); - timelog.clock_out(parse_datetime(date), + timelog.clock_out(parse_datetime(date, session.current_year), p ? account_stack.front()->find_account(p) : NULL, n); count++; } @@ -445,10 +450,11 @@ void textual_parser_t::instance_t::price_entry_directive(char * line) if (std::isdigit(time_field_ptr[0])) { symbol_and_price = next_element(time_field_ptr); if (! symbol_and_price) return; - datetime = parse_datetime(date_field + " " + time_field_ptr); + datetime = parse_datetime(date_field + " " + time_field_ptr, + session.current_year); } else { symbol_and_price = time_field_ptr; - datetime = parse_datetime(date_field); + datetime = parse_datetime(date_field, session.current_year); } string symbol; @@ -474,7 +480,7 @@ void textual_parser_t::instance_t::nomarket_directive(char * line) void textual_parser_t::instance_t::year_directive(char * line) { - current_year = std::atoi(skip_ws(line + 1)); + session.current_year = std::atoi(skip_ws(line + 1)); } void textual_parser_t::instance_t::option_directive(char * line) @@ -498,7 +504,10 @@ void textual_parser_t::instance_t::automated_entry_directive(char * line) istream_pos_type pos = curr_pos; std::size_t lnum = linenum; - std::auto_ptr<auto_entry_t> ae(new auto_entry_t(skip_ws(line + 1))); + std::auto_ptr<auto_entry_t> + ae(new auto_entry_t(item_predicate<xact_t> + (skip_ws(line + 1), + keep_details_t(true, true, true, true)))); if (parse_xacts(account_stack.front(), *ae.get(), "automated")) { journal.auto_entries.push_back(ae.get()); @@ -700,7 +709,7 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, xact->beg_pos = line_beg_pos; xact->beg_line = linenum; - static char buf[MAX_LINE + 1]; + char buf[MAX_LINE + 1]; std::strcpy(buf, line); std::size_t beg = 0; @@ -780,7 +789,7 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, if (*next != '(') // indicates a value expression xact->amount.parse(stream, amount_t::PARSE_NO_REDUCE); else - parse_amount_expr(stream, xact->amount, xact.get(), + parse_amount_expr(session, stream, xact->amount, xact.get(), static_cast<uint_least8_t>(expr_t::PARSE_NO_REDUCE) | static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN)); @@ -821,7 +830,7 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, if (*p != '(') // indicates a value expression xact->cost->parse(cstream, amount_t::PARSE_NO_MIGRATE); else - parse_amount_expr(cstream, *xact->cost, xact.get(), + parse_amount_expr(session, cstream, *xact->cost, xact.get(), static_cast<uint_least8_t>(expr_t::PARSE_NO_MIGRATE) | static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN)); @@ -835,7 +844,7 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, per_unit_cost /= xact->amount; commodity_t::exchange(xact->amount.commodity(), - per_unit_cost, datetime_t(*xact->date())); + per_unit_cost, datetime_t(xact->date())); DEBUG("textual.parse", "line " << linenum << ": " << "Total cost is " << *xact->cost); @@ -873,7 +882,7 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, if (*p != '(') // indicates a value expression xact->assigned_amount->parse(stream, amount_t::PARSE_NO_MIGRATE); else - parse_amount_expr(stream, *xact->assigned_amount, xact.get(), + parse_amount_expr(session, stream, *xact->assigned_amount, xact.get(), static_cast<uint_least8_t>(expr_t::PARSE_NO_MIGRATE)); if (xact->assigned_amount->is_null()) @@ -886,9 +895,9 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, amount_t& amt(*xact->assigned_amount); DEBUG("xact.assign", "line " << linenum << ": " - "account balance = " << xdata.value.strip_annotations()); + "account balance = " << xdata.value); DEBUG("xact.assign", "line " << linenum << ": " - "xact amount = " << amt.strip_annotations()); + "xact amount = " << amt); amount_t diff; @@ -919,9 +928,9 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, } DEBUG("xact.assign", "line " << linenum << ": " - << "diff = " << diff.strip_annotations()); + << "diff = " << diff); DEBUG("textual.parse", "line " << linenum << ": " - << "XACT assign: diff = " << diff.strip_annotations()); + << "XACT assign: diff = " << diff); if (! diff.is_zero()) { if (! xact->amount.is_null()) { @@ -953,7 +962,7 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, // Parse the optional note if (next && *next == ';') { - xact->append_note(++next); + xact->append_note(++next, session.current_year); next = line + len; DEBUG("textual.parse", "line " << linenum << ": " << "Parsed a transaction note"); @@ -1022,9 +1031,9 @@ entry_t * textual_parser_t::instance_t::parse_entry(char * line, if (char * p = std::strchr(line, '=')) { *p++ = '\0'; - curr->_date_eff = parse_date(p); + curr->_date_eff = parse_date(p, session.current_year); } - curr->_date = parse_date(line); + curr->_date = parse_date(line, session.current_year); // Parse the optional cleared flag: * @@ -1064,7 +1073,7 @@ entry_t * textual_parser_t::instance_t::parse_entry(char * line, // Parse the entry note if (next && *next == ';') - curr->append_note(next); + curr->append_note(next, session.current_year); TRACE_STOP(entry_text, 1); @@ -1089,7 +1098,7 @@ entry_t * textual_parser_t::instance_t::parse_entry(char * line, item = curr.get(); // This is a trailing note, and possibly a metadata info tag - item->append_note(p + 1); + item->append_note(p + 1, session.current_year); item->end_pos = curr_pos; item->end_line++; } diff --git a/src/timelog.cc b/src/timelog.cc index b22b2e60..96b629c8 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -113,7 +113,7 @@ time_log_t::~time_log_t() accounts.push_back(time_entry.account); foreach (account_t * account, accounts) - clock_out_from_timelog(time_entries, current_time, account, NULL, + clock_out_from_timelog(time_entries, CURRENT_TIME(), account, NULL, journal); assert(time_entries.empty()); diff --git a/src/times.cc b/src/times.cc index ba0a19a3..f93759d1 100644 --- a/src/times.cc +++ b/src/times.cc @@ -34,19 +34,6 @@ namespace ledger { namespace { -#ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK - const ptime time_now = boost::posix_time::microsec_clock::universal_time(); -#else - const ptime time_now = boost::posix_time::second_clock::universal_time(); -#endif - const date date_now = boost::gregorian::day_clock::universal_day(); -} - -const datetime_t& current_time(time_now); -const date_t& current_date(date_now); - int current_year(current_date.year()); - -namespace { const char * formats[] = { "%y/%m/%d", "%Y/%m/%d", @@ -67,7 +54,6 @@ namespace { } optional<string> input_date_format; -string output_date_format = "%y-%b-%d"; namespace { bool parse_date_mask(const char * date_str, std::tm& result) @@ -85,7 +71,7 @@ namespace { return false; } - bool parse_date(const char * date_str, std::tm& result, const int year) + bool quick_parse_date(const char * date_str, std::tm& result, const int year) { if (! parse_date_mask(date_str, result)) return false; @@ -95,7 +81,7 @@ namespace { result.tm_sec = 0; if (result.tm_year == -1) - result.tm_year = ((year == -1) ? current_year : year) - 1900; + result.tm_year = (year == -1 ? int(CURRENT_DATE().year()) : year) - 1900; if (result.tm_mon == -1) result.tm_mon = 0; @@ -105,25 +91,20 @@ namespace { return true; } - - bool quick_parse_date(const char * date_str, std::tm& result) - { - return parse_date(date_str, result, current_year); - } } -datetime_t parse_datetime(const char * str) +datetime_t parse_datetime(const char * str, int current_year) { std::tm when; - // jww (2008-08-01): This needs to look for HH:MM:SS as well. - quick_parse_date(str, when); + // jww (2008-08-01): Needs to look for HH:MM:SS as well. + quick_parse_date(str, when, current_year); return posix_time::ptime_from_tm(when); } -date_t parse_date(const char * str) +date_t parse_date(const char * str, int current_year) { std::tm when; - quick_parse_date(str, when); + quick_parse_date(str, when, current_year); return gregorian::date_from_tm(when); } @@ -168,7 +149,8 @@ date_t interval_t::increment(const date_t& moment) const namespace { void parse_inclusion_specifier(const string& word, - date_t * begin, date_t * end) + date_t * begin, + date_t * end) { struct std::tm when; @@ -185,7 +167,7 @@ namespace { bool saw_day = true; if (when.tm_year == -1) { - when.tm_year = current_year - 1900; + when.tm_year = CURRENT_DATE().year() - 1900; saw_year = false; } if (when.tm_mon == -1) { @@ -238,13 +220,14 @@ namespace { } if (word == "month") { - time_t now = to_time_t(current_time); + time_t now = to_time_t(CURRENT_TIME()); std::strftime(buf, 31, "%B", localtime(&now)); word = buf; mon_spec = true; } else if (word == "year") { - std::sprintf(buf, "%04d", current_year); + int year = CURRENT_DATE().year(); + std::sprintf(buf, "%04d", year); word = buf; } diff --git a/src/times.h b/src/times.h index 375a2555..4bcdd9e2 100644 --- a/src/times.h +++ b/src/times.h @@ -63,22 +63,25 @@ inline bool is_valid(const date_t& moment) { return ! moment.is_not_a_date(); } -extern const datetime_t& current_time; -extern const date_t& current_date; -extern int current_year; -extern optional<string> input_date_format; -extern string output_date_format; +#ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK +#define CURRENT_TIME() boost::posix_time::microsec_clock::universal_time() +#else +#define CURRENT_TIME() boost::posix_time::second_clock::universal_time() +#endif +#define CURRENT_DATE() boost::gregorian::day_clock::universal_day() -datetime_t parse_datetime(const char * str); +extern optional<string> input_date_format; -inline datetime_t parse_datetime(const string& str) { - return parse_datetime(str.c_str()); +datetime_t parse_datetime(const char * str, int current_year = -1); + +inline datetime_t parse_datetime(const string& str, int current_year = -1) { + return parse_datetime(str.c_str(), current_year); } -date_t parse_date(const char * str); +date_t parse_date(const char * str, int current_year = -1); -inline date_t parse_date(const string& str) { - return parse_date(str.c_str()); +inline date_t parse_date(const string& str, int current_year = -1) { + return parse_date(str.c_str(), current_year); } inline std::time_t to_time_t(const ptime& t) @@ -91,27 +94,22 @@ inline std::time_t to_time_t(const ptime& t) return (t-start).total_seconds(); } -inline string format_datetime(const datetime_t& when) +inline string format_datetime(const datetime_t& when, + const string format = "%Y-%m-%d %H:%M:%S") { char buf[256]; time_t moment = to_time_t(when); - std::strftime(buf, 255, (output_date_format + " %H:%M:%S").c_str(), - std::localtime(&moment)); + std::strftime(buf, 255, format.c_str(), std::localtime(&moment)); return buf; } inline string format_date(const date_t& when, - const optional<string>& format = none) + const string format = "%Y-%m-%d") { - if (format || ! output_date_format.empty()) { - char buf[256]; - std::tm moment = gregorian::to_tm(when); - std::strftime(buf, 255, format ? - format->c_str() : output_date_format.c_str(), &moment); - return buf; - } else { - return to_simple_string(when).substr(2); - } + char buf[256]; + std::tm moment = gregorian::to_tm(when); + std::strftime(buf, 255, format.c_str(), &moment); + return buf; } /** diff --git a/src/utils.cc b/src/utils.cc index 2cfc69db..74a8ea6c 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -435,12 +435,6 @@ std::ostringstream _log_buffer; uint8_t _trace_level; #endif -#ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK -#define CURRENT_TIME() boost::posix_time::microsec_clock::universal_time() -#else -#define CURRENT_TIME() boost::posix_time::second_clock::universal_time() -#endif - static inline void stream_memory_size(std::ostream& out, std::size_t size) { if (size < 1024) @@ -458,20 +452,16 @@ static ptime logger_start; bool logger_func(log_level_t level) { - std::size_t appender = 0; - if (! logger_has_run) { logger_has_run = true; logger_start = CURRENT_TIME(); IF_VERIFY() - *_log_stream << " TIME OBJSZ MEMSZ" << std::endl; - - appender = (logger_start - current_time).total_milliseconds(); + *_log_stream << " TIME OBJSZ MEMSZ" << std::endl; } *_log_stream << std::right << std::setw(5) - << (CURRENT_TIME() - logger_start).total_milliseconds(); + << (CURRENT_TIME() - logger_start).total_milliseconds() << "ms"; IF_VERIFY() { *_log_stream << std::right << std::setw(6) << std::setprecision(3); @@ -500,13 +490,7 @@ bool logger_func(log_level_t level) break; } - *_log_stream << ' ' << _log_buffer.str(); - - if (appender) - *_log_stream << " (" << appender << "ms startup)"; - - *_log_stream << std::endl; - + *_log_stream << ' ' << _log_buffer.str() << std::endl; _log_buffer.str(""); return true; diff --git a/src/value.cc b/src/value.cc index e9eaf5cb..c7ef8617 100644 --- a/src/value.cc +++ b/src/value.cc @@ -130,7 +130,7 @@ void value_t::storage_t::destroy() void value_t::initialize() { #if defined(DEBUG_ON) - LOGGER("value.initialize"); + LOGGER("value.init"); #endif true_value = new storage_t; @@ -1523,10 +1523,11 @@ value_t value_t::annotated_tag() const } #endif -value_t value_t::strip_annotations(const bool keep_price, - const bool keep_date, - const bool keep_tag) const +value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const { + if (what_to_keep.keep_all()) + return *this; + switch (type()) { case VOID: case BOOLEAN: @@ -1541,17 +1542,16 @@ value_t value_t::strip_annotations(const bool keep_price, case SEQUENCE: { sequence_t temp; foreach (const value_t& value, as_sequence()) - temp.push_back(value.strip_annotations(keep_price, keep_date, keep_tag)); + temp.push_back(value.strip_annotations(what_to_keep)); return temp; } case AMOUNT: - return as_amount().strip_annotations(keep_price, keep_date, keep_tag); + return as_amount().strip_annotations(what_to_keep); case BALANCE: - return as_balance().strip_annotations(keep_price, keep_date, keep_tag); + return as_balance().strip_annotations(what_to_keep); case BALANCE_PAIR: - return as_balance_pair().quantity().strip_annotations(keep_price, keep_date, - keep_tag); + return as_balance_pair().quantity().strip_annotations(what_to_keep); default: assert(false); @@ -1623,24 +1623,35 @@ value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost) return *this; } -void value_t::dump(std::ostream& out, const int first_width, - const int latter_width) const +void value_t::print(std::ostream& out, + const int first_width, + const int latter_width, + const optional<string>& date_format) const { switch (type()) { case VOID: - out << "VOID"; + out << ""; break; case BOOLEAN: - out << as_boolean(); + if (as_boolean()) + out << "true"; + else + out << "false"; break; case DATETIME: - out << format_datetime(as_datetime()); + if (date_format) + out << format_datetime(as_datetime(), *date_format); + else + out << format_datetime(as_datetime()); break; case DATE: - out << format_date(as_date()); + if (date_format) + out << format_date(as_date(), *date_format); + else + out << format_date(as_date()); break; case INTEGER: @@ -1655,14 +1666,6 @@ void value_t::dump(std::ostream& out, const int first_width, out << as_string(); break; - case MASK: - out << as_mask(); - break; - - case POINTER: - out << boost::unsafe_any_cast<const void *>(&as_any_pointer()); - break; - case SEQUENCE: { out << '('; bool first = true; @@ -1672,7 +1675,7 @@ void value_t::dump(std::ostream& out, const int first_width, else out << ", "; - value.dump(out, first_width, latter_width); + value.print(out, first_width, latter_width, date_format); } out << ')'; break; @@ -1684,13 +1687,13 @@ void value_t::dump(std::ostream& out, const int first_width, case BALANCE_PAIR: as_balance_pair().print(out, first_width, latter_width); break; + default: - assert(false); - break; + throw_(value_error, "Cannot print " << label()); } } -void value_t::print(std::ostream& out, const bool relaxed) const +void value_t::dump(std::ostream& out, const bool relaxed) const { switch (type()) { case VOID: @@ -1704,6 +1707,13 @@ void value_t::print(std::ostream& out, const bool relaxed) const out << "false"; break; + case DATETIME: + out << '[' << format_datetime(as_datetime()) << ']'; + break; + case DATE: + out << '[' << format_date(as_date()) << ']'; + break; + case INTEGER: out << as_long(); break; @@ -1723,13 +1733,6 @@ void value_t::print(std::ostream& out, const bool relaxed) const out << as_balance_pair(); break; - case DATETIME: - assert(false); - break; - case DATE: - out << '[' << format_date(as_date()) << ']'; - break; - case STRING: out << '"' << as_string() << '"'; break; @@ -1739,7 +1742,7 @@ void value_t::print(std::ostream& out, const bool relaxed) const break; case POINTER: - assert(false); + out << boost::unsafe_any_cast<const void *>(&as_any_pointer()); break; case SEQUENCE: { @@ -1751,11 +1754,15 @@ void value_t::print(std::ostream& out, const bool relaxed) const else out << ", "; - value.print(out, relaxed); + value.dump(out, relaxed); } out << ')'; break; } + + default: + assert(false); + break; } } diff --git a/src/value.h b/src/value.h index 8ee00a10..17ac4378 100644 --- a/src/value.h +++ b/src/value.h @@ -802,9 +802,7 @@ public: value_t annotated_tag() const; #endif - value_t strip_annotations(const bool keep_price = amount_t::keep_price, - const bool keep_date = amount_t::keep_date, - const bool keep_tag = amount_t::keep_tag) const; + value_t strip_annotations(const keep_details_t& what_to_keep) const; /** * Collection-style access methods for SEQUENCE values. @@ -925,9 +923,11 @@ public: /** * Printing methods. */ - void dump(std::ostream& out, const int first_width, - const int latter_width = -1) const; - void print(std::ostream& out, const bool relaxed = true) const; + void print(std::ostream& out, + const int first_width, + const int latter_width = -1, + const optional<string>& date_format = none) const; + void dump(std::ostream& out, const bool relaxed = true) const; /** * Debugging methods. @@ -954,7 +954,7 @@ inline string value_context(const value_t& val) { std::ostringstream buf; buf << std::right; buf.width(20); - val.print(buf); + val.dump(buf); return buf.str(); } diff --git a/src/work.cc b/src/work.cc index 9bd75c9c..e4cee9e6 100644 --- a/src/work.cc +++ b/src/work.cc @@ -124,13 +124,9 @@ void normalize_session_options(session_t& session) INFO("Initialization file is " << session.init_file->string()); INFO("Price database is " << session.price_db->string()); - INFO("Binary cache is " << session.cache_file->string()); foreach (const path& pathname, session.data_files) INFO("Journal file is " << pathname.string()); - - if (! session.use_cache) - INFO("Binary cache mechanism will not be used"); } function_t look_for_precommand(report_t& report, const string& verb) @@ -200,7 +196,7 @@ void normalize_report_options(report_t& report, const string& verb) } if (verb[0] != 'b' && verb[0] != 'r') - amount_t::keep_base = true; + report.what_to_keep.keep_base = true; // Setup the default value for the display predicate @@ -223,19 +219,6 @@ void normalize_report_options(report_t& report, const string& verb) } #endif - // Now setup the various formatting strings - - // jww (2008-08-14): I hear a song, and it's sound is "HaAaaCcK" - -#if 0 - if (! date_output_format.empty()) - date_t::output_format = date_output_format; -#endif - - amount_t::keep_price = report.keep_price; - amount_t::keep_date = report.keep_date; - amount_t::keep_tag = report.keep_tag; - if (! report.report_period.empty() && ! report.sort_all) report.entry_sort = true; } @@ -265,21 +248,4 @@ void invoke_command_verb(report_t& report, INFO_FINISH(command); } -void write_binary_cache(session_t& session, journal_t * journal) -{ - // Write out the binary cache, if need be - - if (session.use_cache && session.cache_dirty && session.cache_file) { - TRACE_START(binary_cache, 1, "Wrote binary journal file"); - - ofstream stream(*session.cache_file); -#if 0 - // jww (2009-01-31): NYI - journal->write(stream); -#endif - - TRACE_FINISH(binary_cache, 1); - } -} - } // namespace ledger @@ -56,7 +56,6 @@ void invoke_command_verb(report_t& report, function_t& command, string_iterator args_begin, string_iterator args_end); -void write_binary_cache(session_t& session, journal_t * journal); } // namespace ledger diff --git a/src/xact.cc b/src/xact.cc index fbd3e8bb..c154d31c 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -36,12 +36,13 @@ namespace ledger { -optional<date_t> xact_t::actual_date() const +date_t xact_t::date() const { - optional<date_t> date = item_t::actual_date(); - if (! date && entry) - return entry->actual_date(); - return date; + if (! _date) { + assert(entry); + return entry->date(); + } + return item_t::date(); } optional<date_t> xact_t::effective_date() const @@ -129,7 +130,7 @@ namespace { value_t get_account(call_scope_t& scope) { - xact_t& xact(downcast<xact_t>(*scope.parent)); + xact_t& xact(find_scope<xact_t>(scope)); var_t<long> max_width(scope, 0); @@ -106,7 +106,7 @@ public: TRACE_DTOR(xact_t); } - virtual optional<date_t> actual_date() const; + virtual date_t date() const; virtual optional<date_t> effective_date() const; virtual state_t state() const; @@ -203,7 +203,7 @@ public: date_t reported_date() const { if (xdata_ && is_valid(xdata_->date)) return xdata_->date; - return *date(); + return date(); } account_t * reported_account() { diff --git a/test/convert.py b/test/convert.py index 9f322b2e..278bc76f 100755 --- a/test/convert.py +++ b/test/convert.py @@ -144,6 +144,8 @@ for line in fd.readlines(): line = re.sub('\.print\(([^)]+?)\)', '.print_(\\1)', line) line = re.sub('true', 'True', line) line = re.sub('false', 'False', line) + line = re.sub('CURRENT_TIME\(\)', 'datetime.now()', line) + line = re.sub('CURRENT_DATE\(\)', 'date.today()', line) line = re.sub('([0-9]+)[FL]', '\\1', line) line = re.sub('([0-9]+)UL', '\\1L', line) line = re.sub(';', '', line) diff --git a/test/unit/t_amount.cc b/test/unit/t_amount.cc index d7858064..33e2d85b 100644 --- a/test/unit/t_amount.cc +++ b/test/unit/t_amount.cc @@ -1097,6 +1097,8 @@ void AmountTestCase::testCommodityAbs() assertValid(x2); } +#ifndef NOT_FOR_PYTHON +#if 0 void AmountTestCase::testReduction() { amount_t x0; @@ -1115,11 +1117,13 @@ void AmountTestCase::testReduction() assertThrow(x0.reduce(), amount_error); assertThrow(x0.unreduce(), amount_error); - assertEqual(x2, x5); - assertEqual(x3, x6); - assertEqual(x4, x10); + assertEqual(x2, x5.reduce()); + assertEqual(x3, x6.reduce()); + assertEqual(x10, x4.reduce()); assertEqual(string("100.0h"), x4.unreduce().to_string()); } +#endif +#endif // NOT_FOR_PYTHON void AmountTestCase::testSign() { diff --git a/test/unit/t_amount.h b/test/unit/t_amount.h index f2ab38d6..3d63b2d7 100644 --- a/test/unit/t_amount.h +++ b/test/unit/t_amount.h @@ -32,7 +32,9 @@ class AmountTestCase : public CPPUNIT_NS::TestCase CPPUNIT_TEST(testCommodityNegation); CPPUNIT_TEST(testAbs); CPPUNIT_TEST(testCommodityAbs); +#if 0 CPPUNIT_TEST(testReduction); +#endif CPPUNIT_TEST(testSign); CPPUNIT_TEST(testCommoditySign); CPPUNIT_TEST(testTruth); diff --git a/test/unit/t_commodity.cc b/test/unit/t_commodity.cc index 9818c863..5f72c195 100644 --- a/test/unit/t_commodity.cc +++ b/test/unit/t_commodity.cc @@ -73,7 +73,7 @@ void CommodityTestCase::testPriceHistory() assertTrue(amt); assertEqual(amount_t("$1831.83"), *amt); - amt = x1.value(current_time); + amt = x1.value(CURRENT_TIME()); assertTrue(amt); assertEqual(string("$2124.12"), amt->to_string()); #ifdef INTEGER_MATH @@ -82,18 +82,18 @@ void CommodityTestCase::testPriceHistory() assertEqual(string("$2124.1220"), amt->to_fullstring()); #endif - amt = x1.value(current_time, euro); + amt = x1.value(CURRENT_TIME(), euro); assertTrue(amt); assertEqual(string("EUR 1366.87"), amt->rounded().to_string()); // Add a newer Euro pricing aapl.add_price(jan17_07, amount_t("EUR 23.00")); - amt = x1.value(current_time, euro); + amt = x1.value(CURRENT_TIME(), euro); assertTrue(amt); assertEqual(string("EUR 2302.30"), amt->to_string()); - amt = x1.value(current_time, cad); + amt = x1.value(CURRENT_TIME(), cad); assertTrue(amt); assertEqual(string("CAD 3223.22"), amt->to_string()); #endif // NOT_FOR_PYTHON diff --git a/test/unit/t_expr.cc b/test/unit/t_expr.cc index 209bb934..f9096cc5 100644 --- a/test/unit/t_expr.cc +++ b/test/unit/t_expr.cc @@ -9,16 +9,10 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ValueExprTestCase, "expr"); void ValueExprTestCase::setUp() { amount_t::initialize(); -#ifndef NOT_FOR_PYTHON - expr_t::initialize(); -#endif // NOT_FOR_PYTHON } void ValueExprTestCase::tearDown() { -#ifndef NOT_FOR_PYTHON - expr_t::shutdown(); -#endif // NOT_FOR_PYTHON amount_t::shutdown(); } diff --git a/test/unit/t_times.cc b/test/unit/t_times.cc index f93a1b36..f593eeac 100644 --- a/test/unit/t_times.cc +++ b/test/unit/t_times.cc @@ -66,8 +66,8 @@ void DateTimeTestCase::testConstructors() assertFalse(d4.is_not_a_date_time()); #endif // NOT_FOR_PYTHON - assertTrue(current_time > d1); - assertTrue(current_time > d4); + assertTrue(CURRENT_TIME() > d1); + assertTrue(CURRENT_TIME() > d4); #ifndef NOT_FOR_PYTHON assertEqual(d3, d15); |