diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/account.h | 9 | ||||
-rw-r--r-- | src/accum.h | 14 | ||||
-rw-r--r-- | src/amount.cc | 25 | ||||
-rw-r--r-- | src/amount.h | 28 | ||||
-rw-r--r-- | src/annotate.cc | 2 | ||||
-rw-r--r-- | src/annotate.h | 33 | ||||
-rw-r--r-- | src/archive.cc | 2 | ||||
-rw-r--r-- | src/archive.h | 9 | ||||
-rw-r--r-- | src/balance.cc | 8 | ||||
-rw-r--r-- | src/balance.h | 2 | ||||
-rw-r--r-- | src/chain.cc | 32 | ||||
-rw-r--r-- | src/chain.h | 9 | ||||
-rw-r--r-- | src/commodity.cc | 47 | ||||
-rw-r--r-- | src/commodity.h | 23 | ||||
-rw-r--r-- | src/compare.h | 9 | ||||
-rw-r--r-- | src/derive.cc | 561 | ||||
-rw-r--r-- | src/draft.cc | 525 | ||||
-rw-r--r-- | src/draft.h | 113 | ||||
-rw-r--r-- | src/emacs.h | 9 | ||||
-rw-r--r-- | src/error.h | 4 | ||||
-rw-r--r-- | src/expr.cc | 136 | ||||
-rw-r--r-- | src/expr.h | 153 | ||||
-rw-r--r-- | src/exprbase.h | 254 | ||||
-rw-r--r-- | src/filters.cc | 8 | ||||
-rw-r--r-- | src/filters.h | 153 | ||||
-rw-r--r-- | src/flags.h | 19 | ||||
-rw-r--r-- | src/format.cc | 83 | ||||
-rw-r--r-- | src/format.h | 52 | ||||
-rw-r--r-- | src/generate.h | 4 | ||||
-rw-r--r-- | src/global.cc | 18 | ||||
-rw-r--r-- | src/global.h | 14 | ||||
-rw-r--r-- | src/hooks.h | 10 | ||||
-rw-r--r-- | src/interactive.cc | 9 | ||||
-rw-r--r-- | src/interactive.h | 9 | ||||
-rw-r--r-- | src/item.h | 9 | ||||
-rw-r--r-- | src/iterators.h | 44 | ||||
-rw-r--r-- | src/journal.h | 9 | ||||
-rw-r--r-- | src/mask.h | 11 | ||||
-rw-r--r-- | src/op.h | 26 | ||||
-rw-r--r-- | src/option.cc | 2 | ||||
-rw-r--r-- | src/option.h | 4 | ||||
-rw-r--r-- | src/output.cc | 45 | ||||
-rw-r--r-- | src/output.h | 24 | ||||
-rw-r--r-- | src/parser.cc | 5 | ||||
-rw-r--r-- | src/parser.h | 24 | ||||
-rw-r--r-- | src/pool.h | 10 | ||||
-rw-r--r-- | src/post.cc | 95 | ||||
-rw-r--r-- | src/post.h | 11 | ||||
-rw-r--r-- | src/precmd.cc | 24 | ||||
-rw-r--r-- | src/precmd.h | 4 | ||||
-rw-r--r-- | src/predicate.h | 256 | ||||
-rw-r--r-- | src/pstream.h | 4 | ||||
-rw-r--r-- | src/py_amount.cc | 15 | ||||
-rw-r--r-- | src/py_xact.cc | 2 | ||||
-rw-r--r-- | src/query.cc (renamed from src/predicate.cc) | 92 | ||||
-rw-r--r-- | src/query.h | 291 | ||||
-rw-r--r-- | src/quotes.h | 4 | ||||
-rw-r--r-- | src/report.cc | 36 | ||||
-rw-r--r-- | src/report.h | 9 | ||||
-rw-r--r-- | src/scope.h | 42 | ||||
-rw-r--r-- | src/session.h | 9 | ||||
-rw-r--r-- | src/stats.cc | 2 | ||||
-rw-r--r-- | src/stats.h | 4 | ||||
-rw-r--r-- | src/temps.h | 4 | ||||
-rw-r--r-- | src/textual.cc | 34 | ||||
-rw-r--r-- | src/timelog.h | 14 | ||||
-rw-r--r-- | src/times.h | 22 | ||||
-rw-r--r-- | src/token.cc | 19 | ||||
-rw-r--r-- | src/token.h | 11 | ||||
-rw-r--r-- | src/unistring.h | 5 | ||||
-rw-r--r-- | src/utils.h | 47 | ||||
-rw-r--r-- | src/value.cc | 53 | ||||
-rw-r--r-- | src/value.h | 6 | ||||
-rw-r--r-- | src/xact.cc | 59 | ||||
-rw-r--r-- | src/xact.h | 47 | ||||
-rw-r--r-- | src/xml.cc | 124 | ||||
-rw-r--r-- | src/xml.h (renamed from src/derive.h) | 52 |
77 files changed, 2136 insertions, 1860 deletions
diff --git a/src/account.h b/src/account.h index dc1d6c9a..0ac1aeb6 100644 --- a/src/account.h +++ b/src/account.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup data - * - * @brief Brief - * - * Long. */ #ifndef _ACCOUNT_H #define _ACCOUNT_H @@ -57,11 +53,6 @@ class post_t; typedef std::list<post_t *> posts_list; typedef std::map<const string, account_t *> accounts_map; -/** - * @brief Brief - * - * Long. - */ class account_t : public supports_flags<>, public scope_t { #define ACCOUNT_NORMAL 0x00 // no flags at all, a basic account diff --git a/src/accum.h b/src/accum.h index c29926d5..878c2b7c 100644 --- a/src/accum.h +++ b/src/accum.h @@ -38,21 +38,12 @@ * @author John Wiegley * * @ingroup util - * - * @brief Brief - * - * Full. */ #ifndef _ACCUM_H #define _ACCUM_H namespace ledger { -/** - * @brief Brief - * - * Full. - */ class straccbuf : public std::streambuf { protected: @@ -68,11 +59,6 @@ protected: friend class straccstream; }; -/** - * @brief Brief - * - * Full. - */ class straccstream : public std::ostream { protected: diff --git a/src/amount.cc b/src/amount.cc index e9b971f8..6fb0056b 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -669,7 +669,8 @@ namespace { for (const char * p = buf; *p; p++) { if (*p == '.') { - if (comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)) + if (commodity_t::european_by_default || + (comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN))) out << ','; else out << *p; @@ -682,7 +683,8 @@ namespace { out << *p; if (integer_digits > 3 && --integer_digits % 3 == 0) { - if (comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)) + if (commodity_t::european_by_default || + (comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN))) out << '.'; else out << ','; @@ -977,12 +979,14 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) } } else if (last_comma != string::npos && - commodity().has_flags(COMMODITY_STYLE_EUROPEAN)) { + (commodity_t::european_by_default || + commodity().has_flags(COMMODITY_STYLE_EUROPEAN))) { comm_flags |= COMMODITY_STYLE_EUROPEAN; quantity->prec = static_cast<precision_t>(quant.length() - last_comma - 1); } else if (last_period != string::npos && - ! (commodity().has_flags(COMMODITY_STYLE_EUROPEAN))) { + ! (commodity_t::european_by_default || + commodity().has_flags(COMMODITY_STYLE_EUROPEAN))) { quantity->prec = static_cast<precision_t>(quant.length() - last_period - 1); } else { @@ -1121,6 +1125,19 @@ bool amount_t::valid() const return true; } +void to_xml(std::ostream& out, const amount_t& amt, bool commodity_details) +{ + push_xml x(out, "amount"); + + if (amt.has_commodity()) + to_xml(out, amt.commodity(), commodity_details); + + { + push_xml y(out, "quantity"); + out << y.guard(amt.quantity_string()); + } +} + #if defined(HAVE_BOOST_SERIALIZATION) template<class Archive> diff --git a/src/amount.h b/src/amount.h index 1cbd5185..505e2ea7 100644 --- a/src/amount.h +++ b/src/amount.h @@ -66,6 +66,20 @@ class commodity_pool_t; DECLARE_EXCEPTION(amount_error, std::runtime_error); +enum parse_flags_enum_t { + PARSE_DEFAULT = 0x00, + PARSE_PARTIAL = 0x01, + PARSE_SINGLE = 0x02, + PARSE_NO_MIGRATE = 0x04, + PARSE_NO_REDUCE = 0x08, + PARSE_NO_ASSIGN = 0x10, + PARSE_NO_DATES = 0x20, + PARSE_OP_CONTEXT = 0x40, + PARSE_SOFT_FAIL = 0x80 +}; + +typedef basic_flags_t<parse_flags_enum_t, uint_least8_t> parse_flags_t; + /** * @brief Encapsulate infinite-precision commoditized amounts * @@ -612,17 +626,8 @@ public: amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes @endcode - */ - enum parse_flags_enum_t { - PARSE_DEFAULT = 0x00, - PARSE_NO_MIGRATE = 0x01, - PARSE_NO_REDUCE = 0x02, - PARSE_SOFT_FAIL = 0x04 - }; - - typedef basic_flags_t<parse_flags_enum_t, uint_least8_t> parse_flags_t; - /** The method parse() is used to parse an amount from an input stream + The method parse() is used to parse an amount from an input stream or a string. A global operator>>() is also defined which simply calls parse on the input stream. The parse() method has two forms: @@ -740,6 +745,9 @@ inline std::istream& operator>>(std::istream& in, amount_t& amt) { return in; } +void to_xml(std::ostream& out, const amount_t& amt, + bool commodity_details = false); + } // namespace ledger #endif // _AMOUNT_H diff --git a/src/annotate.cc b/src/annotate.cc index 45a4e7c8..bd5a8ef8 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -63,7 +63,7 @@ void annotation_t::parse(std::istream& in) throw_(amount_error, _("Commodity price lacks closing brace")); amount_t temp; - temp.parse(buf, amount_t::PARSE_NO_MIGRATE); + temp.parse(buf, PARSE_NO_MIGRATE); DEBUG("commodity.annotations", "Parsed annotation price: " << temp); diff --git a/src/annotate.h b/src/annotate.h index 0f940849..38ebaeae 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -48,11 +48,6 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ struct annotation_t : public supports_flags<>, public equality_comparable<annotation_t> { @@ -115,6 +110,29 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; +inline void to_xml(std::ostream& out, const annotation_t& details) +{ + push_xml x(out, "annotation"); + + if (details.price) + { + push_xml y(out, "price"); + to_xml(out, *details.price); + } + + if (details.date) + { + push_xml y(out, "date"); + to_xml(out, *details.date, false); + } + + if (details.tag) + { + push_xml y(out, "tag"); + out << y.guard(*details.tag); + } +} + struct keep_details_t { bool keep_price; @@ -174,11 +192,6 @@ inline std::ostream& operator<<(std::ostream& out, return out; } -/** - * @brief Brief - * - * Long. - */ class annotated_commodity_t : public commodity_t, public equality_comparable<annotated_commodity_t, diff --git a/src/archive.cc b/src/archive.cc index d54b1179..f76b7543 100644 --- a/src/archive.cc +++ b/src/archive.cc @@ -43,7 +43,7 @@ #include "xact.h" #define LEDGER_MAGIC 0x4c454447 -#define ARCHIVE_VERSION 0x03000003 +#define ARCHIVE_VERSION 0x03000004 //BOOST_IS_ABSTRACT(ledger::scope_t) BOOST_CLASS_EXPORT(ledger::scope_t) diff --git a/src/archive.h b/src/archive.h index 779b57f9..60ead5a9 100644 --- a/src/archive.h +++ b/src/archive.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _ARCHIVE_H #define _ARCHIVE_H @@ -50,11 +46,6 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ class archive_t { path file; diff --git a/src/balance.cc b/src/balance.cc index 1c096e01..59eb4d92 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -299,4 +299,12 @@ void balance_t::print(std::ostream& out, } } +void to_xml(std::ostream& out, const balance_t& bal) +{ + push_xml x(out, "balance"); + + foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) + to_xml(out, pair.second); +} + } // namespace ledger diff --git a/src/balance.h b/src/balance.h index a4f922a8..81a7ff13 100644 --- a/src/balance.h +++ b/src/balance.h @@ -574,6 +574,8 @@ inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) { return out; } +void to_xml(std::ostream& out, const balance_t& amt); + } // namespace ledger #endif // _BALANCE_H diff --git a/src/chain.cc b/src/chain.cc index 4a3a2343..5839bd9e 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -44,8 +44,8 @@ post_handler_ptr chain_post_handlers(report_t& report, bool only_preliminaries) { post_handler_ptr handler(base_handler); - item_predicate display_predicate; - item_predicate only_predicate; + predicate_t display_predicate; + predicate_t only_predicate; assert(report.HANDLED(amount_)); expr_t& expr(report.HANDLER(amount_).expr); @@ -55,8 +55,8 @@ post_handler_ptr chain_post_handlers(report_t& report, // Make sure only forecast postings which match are allowed through if (report.HANDLED(forecast_while_)) { handler.reset(new filter_posts - (handler, item_predicate(report.HANDLER(forecast_while_).str(), - report.what_to_keep()), + (handler, predicate_t(report.HANDLER(forecast_while_).str(), + report.what_to_keep()), report)); } @@ -73,8 +73,8 @@ post_handler_ptr chain_post_handlers(report_t& report, // filter_posts will only pass through posts matching the // `display_predicate'. if (report.HANDLED(display_)) { - display_predicate = item_predicate(report.HANDLER(display_).str(), - report.what_to_keep()); + display_predicate = predicate_t(report.HANDLER(display_).str(), + report.what_to_keep()); handler.reset(new filter_posts(handler, display_predicate, report)); } @@ -100,8 +100,8 @@ post_handler_ptr chain_post_handlers(report_t& report, // filter_posts will only pass through posts matching the // `secondary_predicate'. if (report.HANDLED(only_)) { - only_predicate = item_predicate(report.HANDLER(only_).str(), - report.what_to_keep()); + only_predicate = predicate_t(report.HANDLER(only_).str(), + report.what_to_keep()); handler.reset(new filter_posts(handler, only_predicate, report)); } @@ -187,8 +187,8 @@ post_handler_ptr chain_post_handlers(report_t& report, DEBUG("report.predicate", "Report predicate expression = " << report.HANDLER(limit_).str()); handler.reset(new filter_posts - (handler, item_predicate(report.HANDLER(limit_).str(), - report.what_to_keep()), + (handler, predicate_t(report.HANDLER(limit_).str(), + report.what_to_keep()), report)); } @@ -211,15 +211,15 @@ post_handler_ptr chain_post_handlers(report_t& report, // the filter get reported. if (report.HANDLED(limit_)) handler.reset(new filter_posts - (handler, item_predicate(report.HANDLER(limit_).str(), - report.what_to_keep()), + (handler, predicate_t(report.HANDLER(limit_).str(), + report.what_to_keep()), report)); } else if (report.HANDLED(forecast_while_)) { forecast_posts * forecast_handler = new forecast_posts(handler, - item_predicate(report.HANDLER(forecast_while_).str(), - report.what_to_keep()), + predicate_t(report.HANDLER(forecast_while_).str(), + report.what_to_keep()), report, report.HANDLED(forecast_years_) ? static_cast<std::size_t> @@ -231,8 +231,8 @@ post_handler_ptr chain_post_handlers(report_t& report, // See above, under budget_posts. if (report.HANDLED(limit_)) handler.reset(new filter_posts - (handler, item_predicate(report.HANDLER(limit_).str(), - report.what_to_keep()), + (handler, predicate_t(report.HANDLER(limit_).str(), + report.what_to_keep()), report)); } diff --git a/src/chain.h b/src/chain.h index b22bb665..0384027c 100644 --- a/src/chain.h +++ b/src/chain.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _CHAIN_H #define _CHAIN_H @@ -53,11 +49,6 @@ namespace ledger { class post_t; class account_t; -/** - * @brief Brief - * - * Long. - */ template <typename T> struct item_handler : public noncopyable { diff --git a/src/commodity.cc b/src/commodity.cc index 0d6c11c6..49e82b53 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -654,4 +654,51 @@ bool compare_amount_commodities::operator()(const amount_t * left, } } +void to_xml(std::ostream& out, const commodity_t& comm, + bool commodity_details) +{ + push_xml x(out, "commodity", true); + + out << " flags=\""; + if (! (comm.has_flags(COMMODITY_STYLE_SUFFIXED))) out << 'P'; + if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) out << 'S'; + if (comm.has_flags(COMMODITY_STYLE_THOUSANDS)) out << 'T'; + if (comm.has_flags(COMMODITY_STYLE_EUROPEAN)) out << 'E'; + out << '"'; + + x.close_attrs(); + + { + push_xml y(out, "symbol"); + out << y.guard(comm.symbol()); + } + + if (commodity_details) { + if (comm.is_annotated()) + to_xml(out, as_annotated_commodity(comm).details); + + if (comm.varied_history()) { + push_xml y(out, "varied-history"); + + foreach (const commodity_t::history_by_commodity_map::value_type& pair, + comm.varied_history()->histories) { + { + push_xml z(out, "symbol"); + out << y.guard(pair.first->symbol()); + } + { + push_xml z(out, "history"); + + foreach (const commodity_t::history_map::value_type& inner_pair, + pair.second.prices) { + push_xml w(out, "price-point"); + to_xml(out, inner_pair.first); + to_xml(out, inner_pair.second); + } + } + } + } + } +} + } // namespace ledger diff --git a/src/commodity.h b/src/commodity.h index e2a17638..42cc6d8f 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -53,11 +53,6 @@ class keep_details_t; DECLARE_EXCEPTION(commodity_error, std::runtime_error); -/** - * @brief Brief - * - * Long. - */ struct price_point_t { datetime_t when; @@ -77,11 +72,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class commodity_t : public delegates_flags<uint_least16_t>, public equality_comparable1<commodity_t, noncopyable> @@ -328,6 +318,11 @@ public: return *base->varied_history; return none; } + optional<const varied_history_t&> varied_history() const { + if (base->varied_history) + return *base->varied_history; + return none; + } optional<history_t&> history(const optional<commodity_t&>& commodity); @@ -424,15 +419,13 @@ inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { return out; } -/** - * @brief Brief - * - * Long. - */ struct compare_amount_commodities { bool operator()(const amount_t * left, const amount_t * right) const; }; +void to_xml(std::ostream& out, const commodity_t& comm, + bool commodity_details = false); + } // namespace ledger #endif // _COMMODITY_H diff --git a/src/compare.h b/src/compare.h index c0a72327..740ba275 100644 --- a/src/compare.h +++ b/src/compare.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup data - * - * @brief Brief - * - * Long. */ #ifndef _COMPARE_H #define _COMPARE_H @@ -56,11 +52,6 @@ class account_t; void push_sort_value(std::list<sort_value_t>& sort_values, expr_t::ptr_op_t node, scope_t& scope); -/** - * @brief Brief - * - * Long. - */ template <typename T> class compare_items { diff --git a/src/derive.cc b/src/derive.cc deleted file mode 100644 index 081b96b2..00000000 --- a/src/derive.cc +++ /dev/null @@ -1,561 +0,0 @@ -/* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of New Artisans LLC nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <system.hh> - -#include "derive.h" -#include "xact.h" -#include "post.h" -#include "account.h" -#include "journal.h" -#include "session.h" -#include "report.h" -#include "output.h" - -namespace ledger { - -namespace { - struct xact_template_t - { - optional<date_t> date; - optional<string> code; - optional<string> note; - mask_t payee_mask; - - struct post_template_t { - bool from; - optional<mask_t> account_mask; - optional<amount_t> amount; - optional<string> cost_operator; - optional<amount_t> cost; - - post_template_t() : from(false) {} - }; - - std::list<post_template_t> posts; - - xact_template_t() {} - - void dump(std::ostream& out) const - { - if (date) - out << _("Date: ") << *date << std::endl; - else - out << _("Date: <today>") << std::endl; - - if (code) - out << _("Code: ") << *code << std::endl; - if (note) - out << _("Note: ") << *note << std::endl; - - if (payee_mask.empty()) - out << _("Payee mask: INVALID (template expression will cause an error)") - << std::endl; - else - out << _("Payee mask: ") << payee_mask << std::endl; - - if (posts.empty()) { - out << std::endl - << _("<Posting copied from last related transaction>") - << std::endl; - } else { - bool has_only_from = true; - bool has_only_to = true; - - foreach (const post_template_t& post, posts) { - if (post.from) - has_only_to = false; - else - has_only_from = false; - } - - foreach (const post_template_t& post, posts) { - straccstream accum; - out << std::endl - << ACCUM(accum << _("[Posting \"%1\"]") - << (post.from ? _("from") : _("to"))) - << std::endl; - - if (post.account_mask) - out << _(" Account mask: ") << *post.account_mask << std::endl; - else if (post.from) - out << _(" Account mask: <use last of last related accounts>") << std::endl; - else - out << _(" Account mask: <use first of last related accounts>") << std::endl; - - if (post.amount) - out << _(" Amount: ") << *post.amount << std::endl; - - if (post.cost) - out << _(" Cost: ") << *post.cost_operator - << " " << *post.cost << std::endl; - } - } - } - }; - - xact_template_t - args_to_xact_template(value_t::sequence_t::const_iterator begin, - value_t::sequence_t::const_iterator end) - { - regex date_mask(_("([0-9]+(?:[-/.][0-9]+)?(?:[-/.][0-9]+))?")); - smatch what; - - xact_template_t tmpl; - bool check_for_date = true; - - optional<date_time::weekdays> weekday; - xact_template_t::post_template_t * post = NULL; - - for (; begin != end; begin++) { - if (check_for_date && - regex_match((*begin).to_string(), what, date_mask)) { - tmpl.date = parse_date(what[0]); - check_for_date = false; - } - else if (check_for_date && - bool(weekday = string_to_day_of_week(what[0]))) { - short dow = static_cast<short>(*weekday); - date_t date = CURRENT_DATE() - date_duration(1); - while (date.day_of_week() != dow) - date -= date_duration(1); - tmpl.date = date; - check_for_date = false; - } - else { - string arg = (*begin).to_string(); - - if (arg == "at") { - if (begin == end) - throw std::runtime_error(_("Invalid xact command arguments")); - tmpl.payee_mask = (*++begin).to_string(); - } - else if (arg == "to" || arg == "from") { - if (! post || post->account_mask) { - tmpl.posts.push_back(xact_template_t::post_template_t()); - post = &tmpl.posts.back(); - } - if (begin == end) - throw std::runtime_error(_("Invalid xact command arguments")); - post->account_mask = mask_t((*++begin).to_string()); - post->from = arg == "from"; - } - else if (arg == "on") { - if (begin == end) - throw std::runtime_error(_("Invalid xact command arguments")); - tmpl.date = parse_date((*++begin).to_string()); - check_for_date = false; - } - else if (arg == "code") { - if (begin == end) - throw std::runtime_error(_("Invalid xact command arguments")); - tmpl.code = (*++begin).to_string(); - } - else if (arg == "note") { - if (begin == end) - throw std::runtime_error(_("Invalid xact command arguments")); - tmpl.note = (*++begin).to_string(); - } - else if (arg == "rest") { - ; // just ignore this argument - } - else if (arg == "@" || arg == "@@") { - amount_t cost; - post->cost_operator = arg; - if (begin == end) - throw std::runtime_error(_("Invalid xact command arguments")); - arg = (*++begin).to_string(); - if (! cost.parse(arg, amount_t::PARSE_SOFT_FAIL | - amount_t::PARSE_NO_MIGRATE)) - throw std::runtime_error(_("Invalid xact command arguments")); - post->cost = cost; - } - else { - // Without a preposition, it is either: - // - // A payee, if we have not seen one - // An account or an amount, if we have - // An account if an amount has just been seen - // An amount if an account has just been seen - - if (tmpl.payee_mask.empty()) { - tmpl.payee_mask = arg; - } - else { - amount_t amt; - optional<mask_t> account; - - if (! amt.parse(arg, amount_t::PARSE_SOFT_FAIL | - amount_t::PARSE_NO_MIGRATE)) - account = mask_t(arg); - - if (! post || - (account && post->account_mask) || - (! account && post->amount)) { - tmpl.posts.push_back(xact_template_t::post_template_t()); - post = &tmpl.posts.back(); - } - - if (account) { - post->from = false; - post->account_mask = account; - } else { - post->amount = amt; - } - } - } - } - } - - if (! tmpl.posts.empty()) { - bool has_only_from = true; - bool has_only_to = true; - - // A single account at the end of the line is the "from" account - if (tmpl.posts.size() > 1 && - tmpl.posts.back().account_mask && ! tmpl.posts.back().amount) - tmpl.posts.back().from = true; - - foreach (xact_template_t::post_template_t& post, tmpl.posts) { - if (post.from) - has_only_to = false; - else - has_only_from = false; - } - - if (has_only_from) { - tmpl.posts.push_front(xact_template_t::post_template_t()); - } - else if (has_only_to) { - tmpl.posts.push_back(xact_template_t::post_template_t()); - tmpl.posts.back().from = true; - } - } - - return tmpl; - } - - xact_t * derive_xact_from_template(xact_template_t& tmpl, - report_t& report) - { - if (tmpl.payee_mask.empty()) - throw std::runtime_error(_("xact' command requires at least a payee")); - - xact_t * matching = NULL; - journal_t& journal(*report.session.journal.get()); - std::auto_ptr<xact_t> added(new xact_t); - - for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); - j != journal.xacts.rend(); - j++) { - if (tmpl.payee_mask.match((*j)->payee)) { - matching = *j; - DEBUG("derive.xact", - "Found payee match: transaction on line " << (*j)->pos->beg_line); - break; - } - } - - if (! tmpl.date) { - added->_date = CURRENT_DATE(); - DEBUG("derive.xact", "Setting date to current date"); - } else { - added->_date = tmpl.date; - DEBUG("derive.xact", "Setting date to template date: " << *tmpl.date); - } - - added->set_state(item_t::UNCLEARED); - - if (matching) { - added->payee = matching->payee; - added->code = matching->code; - added->note = matching->note; - -#if defined(DEBUG_ON) - DEBUG("derive.xact", "Setting payee from match: " << added->payee); - if (added->code) - DEBUG("derive.xact", "Setting code from match: " << *added->code); - if (added->note) - DEBUG("derive.xact", "Setting note from match: " << *added->note); -#endif - } else { - added->payee = tmpl.payee_mask.str(); - DEBUG("derive.xact", "Setting payee from template: " << added->payee); - } - - if (tmpl.code) { - added->code = tmpl.code; - DEBUG("derive.xact", "Now setting code from template: " << *added->code); - } - if (tmpl.note) { - added->note = tmpl.note; - DEBUG("derive.xact", "Now setting note from template: " << *added->note); - } - - if (tmpl.posts.empty()) { - if (matching) { - DEBUG("derive.xact", "Template had no postings, copying from match"); - - foreach (post_t * post, matching->posts) { - added->add_post(new post_t(*post)); - added->posts.back()->set_state(item_t::UNCLEARED); - } - } else { - throw_(std::runtime_error, - _("No accounts, and no past transaction matching '%1'") - << tmpl.payee_mask); - } - } else { - DEBUG("derive.xact", "Template had postings"); - - bool any_post_has_amount = false; - foreach (xact_template_t::post_template_t& post, tmpl.posts) { - if (post.amount) { - DEBUG("derive.xact", " and at least one has an amount specified"); - any_post_has_amount = true; - break; - } - } - - foreach (xact_template_t::post_template_t& post, tmpl.posts) { - std::auto_ptr<post_t> new_post; - - commodity_t * found_commodity = NULL; - - if (matching) { - if (post.account_mask) { - DEBUG("derive.xact", - "Looking for matching posting based on account mask"); - - foreach (post_t * x, matching->posts) { - if (post.account_mask->match(x->account->fullname())) { - new_post.reset(new post_t(*x)); - DEBUG("derive.xact", - "Founding posting from line " << x->pos->beg_line); - break; - } - } - } else { - if (post.from) { - for (posts_list::reverse_iterator j = matching->posts.rbegin(); - j != matching->posts.rend(); - j++) { - if ((*j)->must_balance()) { - new_post.reset(new post_t(**j)); - DEBUG("derive.xact", - "Copied last real posting from matching"); - break; - } - } - } else { - for (posts_list::iterator j = matching->posts.begin(); - j != matching->posts.end(); - j++) { - if ((*j)->must_balance()) { - new_post.reset(new post_t(**j)); - DEBUG("derive.xact", - "Copied first real posting from matching"); - break; - } - } - } - } - } - - if (! new_post.get()) { - new_post.reset(new post_t); - DEBUG("derive.xact", "New posting was NULL, creating a blank one"); - } - - if (! new_post->account) { - DEBUG("derive.xact", "New posting still needs an account"); - - if (post.account_mask) { - DEBUG("derive.xact", "The template has an account mask"); - - account_t * acct = NULL; - if (! acct) { - acct = journal.find_account_re(post.account_mask->str()); -#if defined(DEBUG_ON) - if (acct) - DEBUG("derive.xact", "Found account as a regular expression"); -#endif - } - if (! acct) { - acct = journal.find_account(post.account_mask->str()); -#if defined(DEBUG_ON) - if (acct) - DEBUG("derive.xact", "Found (or created) account by name"); -#endif - } - - // Find out the default commodity to use by looking at the last - // commodity used in that account - for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); - j != journal.xacts.rend(); - j++) { - foreach (post_t * x, (*j)->posts) { - if (x->account == acct && ! x->amount.is_null()) { - new_post.reset(new post_t(*x)); - DEBUG("derive.xact", - "Found account in journal postings, setting new posting"); - break; - } - } - } - - new_post->account = acct; - DEBUG("derive.xact", - "Set new posting's account to: " << acct->fullname()); - } else { - if (post.from) { - new_post->account = journal.find_account(_("Liabilities:Unknown")); - DEBUG("derive.xact", - "Set new posting's account to: Liabilities:Unknown"); - } else { - new_post->account = journal.find_account(_("Expenses:Unknown")); - DEBUG("derive.xact", - "Set new posting's account to: Expenses:Unknown"); - } - } - } - - if (new_post.get() && ! new_post->amount.is_null()) { - found_commodity = &new_post->amount.commodity(); - - if (any_post_has_amount) { - new_post->amount = amount_t(); - DEBUG("derive.xact", "New posting has an amount, but we cleared it"); - } else { - any_post_has_amount = true; - DEBUG("derive.xact", "New posting has an amount, and we're using it"); - } - } - - if (post.amount) { - new_post->amount = *post.amount; - DEBUG("derive.xact", "Copied over posting amount"); - - if (post.from) { - new_post->amount.in_place_negate(); - DEBUG("derive.xact", "Negated new posting amount"); - } - } - - if (post.cost) { - if (post.cost->sign() < 0) - throw parse_error(_("A posting's cost may not be negative")); - - post.cost->in_place_unround(); - - if (*post.cost_operator == "@") { - // For the sole case where the cost might be uncommoditized, - // guarantee that the commodity of the cost after multiplication - // is the same as it was before. - commodity_t& cost_commodity(post.cost->commodity()); - *post.cost *= new_post->amount; - post.cost->set_commodity(cost_commodity); - } - - new_post->cost = *post.cost; - DEBUG("derive.xact", "Copied over posting cost"); - } - - if (found_commodity && - ! new_post->amount.is_null() && - ! new_post->amount.has_commodity()) { - new_post->amount.set_commodity(*found_commodity); - DEBUG("derive.xact", "Set posting amount commodity to: " - << new_post->amount.commodity()); - - new_post->amount = new_post->amount.rounded(); - DEBUG("derive.xact", - "Rounded posting amount to: " << new_post->amount); - } - - added->add_post(new_post.release()); - added->posts.back()->set_state(item_t::UNCLEARED); - - DEBUG("derive.xact", "Added new posting to derived entry"); - } - } - - if (! journal.xact_finalize_hooks.run_hooks(*added.get(), false) || - ! added->finalize() || - ! journal.xact_finalize_hooks.run_hooks(*added.get(), true)) { - throw_(std::runtime_error, - _("Failed to finalize derived transaction (check commodities)")); - } - - return added.release(); - } -} - -value_t template_command(call_scope_t& args) -{ - report_t& report(find_scope<report_t>(args)); - std::ostream& out(report.output_stream); - - value_t::sequence_t::const_iterator begin = args.value().begin(); - value_t::sequence_t::const_iterator end = args.value().end(); - - out << _("--- Input arguments ---") << std::endl; - args.value().dump(out); - out << std::endl << std::endl; - - xact_template_t tmpl = args_to_xact_template(begin, end); - - out << _("--- Transaction template ---") << std::endl; - tmpl.dump(out); - - return true; -} - -value_t xact_command(call_scope_t& args) -{ - value_t::sequence_t::const_iterator begin = args.value().begin(); - value_t::sequence_t::const_iterator end = args.value().end(); - - report_t& report(find_scope<report_t>(args)); - xact_template_t tmpl = args_to_xact_template(begin, end); - std::auto_ptr<xact_t> new_xact(derive_xact_from_template(tmpl, report)); - - // Only consider actual postings for the "xact" command - report.HANDLER(limit_).on(string("#xact"), "actual"); - - report.xact_report(post_handler_ptr - (new format_posts(report, - report.HANDLER(print_format_).str())), - *new_xact.get()); - return true; -} - -} // namespace ledger diff --git a/src/draft.cc b/src/draft.cc new file mode 100644 index 00000000..b4e23322 --- /dev/null +++ b/src/draft.cc @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <system.hh> + +#include "draft.h" +#include "xact.h" +#include "post.h" +#include "account.h" +#include "journal.h" +#include "session.h" +#include "report.h" +#include "output.h" + +namespace ledger { + +void draft_t::xact_template_t::dump(std::ostream& out) const +{ + if (date) + out << _("Date: ") << *date << std::endl; + else + out << _("Date: <today>") << std::endl; + + if (code) + out << _("Code: ") << *code << std::endl; + if (note) + out << _("Note: ") << *note << std::endl; + + if (payee_mask.empty()) + out << _("Payee mask: INVALID (template expression will cause an error)") + << std::endl; + else + out << _("Payee mask: ") << payee_mask << std::endl; + + if (posts.empty()) { + out << std::endl + << _("<Posting copied from last related transaction>") + << std::endl; + } else { + bool has_only_from = true; + bool has_only_to = true; + + foreach (const post_template_t& post, posts) { + if (post.from) + has_only_to = false; + else + has_only_from = false; + } + + foreach (const post_template_t& post, posts) { + straccstream accum; + out << std::endl + << ACCUM(accum << _("[Posting \"%1\"]") + << (post.from ? _("from") : _("to"))) + << std::endl; + + if (post.account_mask) + out << _(" Account mask: ") << *post.account_mask << std::endl; + else if (post.from) + out << _(" Account mask: <use last of last related accounts>") << std::endl; + else + out << _(" Account mask: <use first of last related accounts>") << std::endl; + + if (post.amount) + out << _(" Amount: ") << *post.amount << std::endl; + + if (post.cost) + out << _(" Cost: ") << *post.cost_operator + << " " << *post.cost << std::endl; + } + } +} + +void draft_t::parse_args(const value_t& args) +{ + regex date_mask(_("([0-9]+(?:[-/.][0-9]+)?(?:[-/.][0-9]+))?")); + smatch what; + bool check_for_date = true; + + tmpl = xact_template_t(); + + optional<date_time::weekdays> weekday; + xact_template_t::post_template_t * post = NULL; + + value_t::sequence_t::const_iterator begin = args.begin(); + value_t::sequence_t::const_iterator end = args.end(); + + for (; begin != end; begin++) { + if (check_for_date && + regex_match((*begin).to_string(), what, date_mask)) { + tmpl->date = parse_date(what[0]); + check_for_date = false; + } + else if (check_for_date && + bool(weekday = string_to_day_of_week(what[0]))) { + short dow = static_cast<short>(*weekday); + date_t date = CURRENT_DATE() - date_duration(1); + while (date.day_of_week() != dow) + date -= date_duration(1); + tmpl->date = date; + check_for_date = false; + } + else { + string arg = (*begin).to_string(); + + if (arg == "at") { + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); + tmpl->payee_mask = (*++begin).to_string(); + } + else if (arg == "to" || arg == "from") { + if (! post || post->account_mask) { + tmpl->posts.push_back(xact_template_t::post_template_t()); + post = &tmpl->posts.back(); + } + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); + post->account_mask = mask_t((*++begin).to_string()); + post->from = arg == "from"; + } + else if (arg == "on") { + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); + tmpl->date = parse_date((*++begin).to_string()); + check_for_date = false; + } + else if (arg == "code") { + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); + tmpl->code = (*++begin).to_string(); + } + else if (arg == "note") { + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); + tmpl->note = (*++begin).to_string(); + } + else if (arg == "rest") { + ; // just ignore this argument + } + else if (arg == "@" || arg == "@@") { + amount_t cost; + post->cost_operator = arg; + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); + arg = (*++begin).to_string(); + if (! cost.parse(arg, PARSE_SOFT_FAIL | PARSE_NO_MIGRATE)) + throw std::runtime_error(_("Invalid xact command arguments")); + post->cost = cost; + } + else { + // Without a preposition, it is either: + // + // A payee, if we have not seen one + // An account or an amount, if we have + // An account if an amount has just been seen + // An amount if an account has just been seen + + if (tmpl->payee_mask.empty()) { + tmpl->payee_mask = arg; + } + else { + amount_t amt; + optional<mask_t> account; + + if (! amt.parse(arg, PARSE_SOFT_FAIL | PARSE_NO_MIGRATE)) + account = mask_t(arg); + + if (! post || + (account && post->account_mask) || + (! account && post->amount)) { + tmpl->posts.push_back(xact_template_t::post_template_t()); + post = &tmpl->posts.back(); + } + + if (account) { + post->from = false; + post->account_mask = account; + } else { + post->amount = amt; + } + } + } + } + } + + if (! tmpl->posts.empty()) { + bool has_only_from = true; + bool has_only_to = true; + + // A single account at the end of the line is the "from" account + if (tmpl->posts.size() > 1 && + tmpl->posts.back().account_mask && ! tmpl->posts.back().amount) + tmpl->posts.back().from = true; + + foreach (xact_template_t::post_template_t& post, tmpl->posts) { + if (post.from) + has_only_to = false; + else + has_only_from = false; + } + + if (has_only_from) { + tmpl->posts.push_front(xact_template_t::post_template_t()); + } + else if (has_only_to) { + tmpl->posts.push_back(xact_template_t::post_template_t()); + tmpl->posts.back().from = true; + } + } +} + +xact_t * draft_t::insert(journal_t& journal) +{ + if (tmpl->payee_mask.empty()) + throw std::runtime_error(_("xact' command requires at least a payee")); + + xact_t * matching = NULL; + + std::auto_ptr<xact_t> added(new xact_t); + + for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); + j != journal.xacts.rend(); + j++) { + if (tmpl->payee_mask.match((*j)->payee)) { + matching = *j; + DEBUG("derive.xact", + "Found payee match: transaction on line " << (*j)->pos->beg_line); + break; + } + } + + if (! tmpl->date) { + added->_date = CURRENT_DATE(); + DEBUG("derive.xact", "Setting date to current date"); + } else { + added->_date = tmpl->date; + DEBUG("derive.xact", "Setting date to template date: " << *tmpl->date); + } + + added->set_state(item_t::UNCLEARED); + + if (matching) { + added->payee = matching->payee; + added->code = matching->code; + added->note = matching->note; + +#if defined(DEBUG_ON) + DEBUG("derive.xact", "Setting payee from match: " << added->payee); + if (added->code) + DEBUG("derive.xact", "Setting code from match: " << *added->code); + if (added->note) + DEBUG("derive.xact", "Setting note from match: " << *added->note); +#endif + } else { + added->payee = tmpl->payee_mask.str(); + DEBUG("derive.xact", "Setting payee from template: " << added->payee); + } + + if (tmpl->code) { + added->code = tmpl->code; + DEBUG("derive.xact", "Now setting code from template: " << *added->code); + } + if (tmpl->note) { + added->note = tmpl->note; + DEBUG("derive.xact", "Now setting note from template: " << *added->note); + } + + if (tmpl->posts.empty()) { + if (matching) { + DEBUG("derive.xact", "Template had no postings, copying from match"); + + foreach (post_t * post, matching->posts) { + added->add_post(new post_t(*post)); + added->posts.back()->set_state(item_t::UNCLEARED); + } + } else { + throw_(std::runtime_error, + _("No accounts, and no past transaction matching '%1'") + << tmpl->payee_mask); + } + } else { + DEBUG("derive.xact", "Template had postings"); + + bool any_post_has_amount = false; + foreach (xact_template_t::post_template_t& post, tmpl->posts) { + if (post.amount) { + DEBUG("derive.xact", " and at least one has an amount specified"); + any_post_has_amount = true; + break; + } + } + + foreach (xact_template_t::post_template_t& post, tmpl->posts) { + std::auto_ptr<post_t> new_post; + + commodity_t * found_commodity = NULL; + + if (matching) { + if (post.account_mask) { + DEBUG("derive.xact", + "Looking for matching posting based on account mask"); + + foreach (post_t * x, matching->posts) { + if (post.account_mask->match(x->account->fullname())) { + new_post.reset(new post_t(*x)); + DEBUG("derive.xact", + "Founding posting from line " << x->pos->beg_line); + break; + } + } + } else { + if (post.from) { + for (posts_list::reverse_iterator j = matching->posts.rbegin(); + j != matching->posts.rend(); + j++) { + if ((*j)->must_balance()) { + new_post.reset(new post_t(**j)); + DEBUG("derive.xact", + "Copied last real posting from matching"); + break; + } + } + } else { + for (posts_list::iterator j = matching->posts.begin(); + j != matching->posts.end(); + j++) { + if ((*j)->must_balance()) { + new_post.reset(new post_t(**j)); + DEBUG("derive.xact", + "Copied first real posting from matching"); + break; + } + } + } + } + } + + if (! new_post.get()) { + new_post.reset(new post_t); + DEBUG("derive.xact", "New posting was NULL, creating a blank one"); + } + + if (! new_post->account) { + DEBUG("derive.xact", "New posting still needs an account"); + + if (post.account_mask) { + DEBUG("derive.xact", "The template has an account mask"); + + account_t * acct = NULL; + if (! acct) { + acct = journal.find_account_re(post.account_mask->str()); +#if defined(DEBUG_ON) + if (acct) + DEBUG("derive.xact", "Found account as a regular expression"); +#endif + } + if (! acct) { + acct = journal.find_account(post.account_mask->str()); +#if defined(DEBUG_ON) + if (acct) + DEBUG("derive.xact", "Found (or created) account by name"); +#endif + } + + // Find out the default commodity to use by looking at the last + // commodity used in that account + for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); + j != journal.xacts.rend(); + j++) { + foreach (post_t * x, (*j)->posts) { + if (x->account == acct && ! x->amount.is_null()) { + new_post.reset(new post_t(*x)); + DEBUG("derive.xact", + "Found account in journal postings, setting new posting"); + break; + } + } + } + + new_post->account = acct; + DEBUG("derive.xact", + "Set new posting's account to: " << acct->fullname()); + } else { + if (post.from) { + new_post->account = journal.find_account(_("Liabilities:Unknown")); + DEBUG("derive.xact", + "Set new posting's account to: Liabilities:Unknown"); + } else { + new_post->account = journal.find_account(_("Expenses:Unknown")); + DEBUG("derive.xact", + "Set new posting's account to: Expenses:Unknown"); + } + } + } + + if (new_post.get() && ! new_post->amount.is_null()) { + found_commodity = &new_post->amount.commodity(); + + if (any_post_has_amount) { + new_post->amount = amount_t(); + DEBUG("derive.xact", "New posting has an amount, but we cleared it"); + } else { + any_post_has_amount = true; + DEBUG("derive.xact", "New posting has an amount, and we're using it"); + } + } + + if (post.amount) { + new_post->amount = *post.amount; + DEBUG("derive.xact", "Copied over posting amount"); + + if (post.from) { + new_post->amount.in_place_negate(); + DEBUG("derive.xact", "Negated new posting amount"); + } + } + + if (post.cost) { + if (post.cost->sign() < 0) + throw parse_error(_("A posting's cost may not be negative")); + + post.cost->in_place_unround(); + + if (*post.cost_operator == "@") { + // For the sole case where the cost might be uncommoditized, + // guarantee that the commodity of the cost after multiplication + // is the same as it was before. + commodity_t& cost_commodity(post.cost->commodity()); + *post.cost *= new_post->amount; + post.cost->set_commodity(cost_commodity); + } + + new_post->cost = *post.cost; + DEBUG("derive.xact", "Copied over posting cost"); + } + + if (found_commodity && + ! new_post->amount.is_null() && + ! new_post->amount.has_commodity()) { + new_post->amount.set_commodity(*found_commodity); + DEBUG("derive.xact", "Set posting amount commodity to: " + << new_post->amount.commodity()); + + new_post->amount = new_post->amount.rounded(); + DEBUG("derive.xact", + "Rounded posting amount to: " << new_post->amount); + } + + added->add_post(new_post.release()); + added->posts.back()->set_state(item_t::UNCLEARED); + + DEBUG("derive.xact", "Added new posting to derived entry"); + } + } + + if (! journal.add_xact(added.get())) + throw_(std::runtime_error, + _("Failed to finalize derived transaction (check commodities)")); + + return added.release(); +} + +value_t template_command(call_scope_t& args) +{ + report_t& report(find_scope<report_t>(args)); + std::ostream& out(report.output_stream); + + out << _("--- Input arguments ---") << std::endl; + args.value().dump(out); + out << std::endl << std::endl; + + draft_t draft(args.value()); + + out << _("--- Transaction template ---") << std::endl; + draft.dump(out); + + return true; +} + +value_t xact_command(call_scope_t& args) +{ + report_t& report(find_scope<report_t>(args)); + draft_t draft(args.value()); + + xact_t * new_xact = draft.insert(*report.session.journal.get()); + + // Only consider actual postings for the "xact" command + report.HANDLER(limit_).on(string("#xact"), "actual"); + + report.xact_report(post_handler_ptr + (new format_posts(report, + report.HANDLER(print_format_).str())), + *new_xact); + return true; +} + +} // namespace ledger diff --git a/src/draft.h b/src/draft.h new file mode 100644 index 00000000..faefa67b --- /dev/null +++ b/src/draft.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup derive + */ + +/** + * @file derive.h + * @author John Wiegley + * + * @ingroup report + */ +#ifndef _DERIVE_H +#define _DERIVE_H + +#include "exprbase.h" +#include "value.h" + +namespace ledger { + +class journal_t; +class xact_t; + +class draft_t : public expr_base_t<value_t> +{ + typedef expr_base_t<value_t> base_type; + + struct xact_template_t + { + optional<date_t> date; + optional<string> code; + optional<string> note; + mask_t payee_mask; + + struct post_template_t { + bool from; + optional<mask_t> account_mask; + optional<amount_t> amount; + optional<string> cost_operator; + optional<amount_t> cost; + + post_template_t() : from(false) {} + }; + + std::list<post_template_t> posts; + + xact_template_t() {} + + void dump(std::ostream& out) const; + }; + + optional<xact_template_t> tmpl; + +public: + draft_t(const value_t& args) : base_type() { + TRACE_CTOR(draft_t, "value_t"); + if (! args.empty()) + parse_args(args); + } + ~draft_t() { + TRACE_DTOR(draft_t); + } + + void parse_args(const value_t& args); + + virtual result_type real_calc(scope_t&) { + assert(0); + return true; + } + + xact_t * insert(journal_t& journal); + + virtual void dump(std::ostream& out) const { + if (tmpl) + tmpl->dump(out); + } +}; + +value_t xact_command(call_scope_t& args); +value_t template_command(call_scope_t& args); + +} // namespace ledger + +#endif // _DERIVE_H diff --git a/src/emacs.h b/src/emacs.h index 1467cdaf..4b2a452a 100644 --- a/src/emacs.h +++ b/src/emacs.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _EMACS_H #define _EMACS_H @@ -52,11 +48,6 @@ namespace ledger { class xact_t; -/** - * @brief Brief - * - * Long. - */ class format_emacs_posts : public item_handler<post_t> { format_emacs_posts(); diff --git a/src/error.h b/src/error.h index 21f5bd8f..5369faf1 100644 --- a/src/error.h +++ b/src/error.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup util - * - * @brief Brief - * - * Long. */ #ifndef _ERROR_H #define _ERROR_H diff --git a/src/expr.cc b/src/expr.cc index 4f12a648..aa9a7b0e 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -36,122 +36,26 @@ namespace ledger { -expr_t::expr_t() : context(NULL), compiled(false) +void expr_t::parse(std::istream& in, const parse_flags_t& flags, + const optional<string>& original_string) { - TRACE_CTOR(expr_t, ""); -} - -expr_t::expr_t(const expr_t& other) - : ptr(other.ptr), context(other.context), str(other.str), compiled(false) -{ - TRACE_CTOR(expr_t, "copy"); -} - -expr_t::expr_t(const string& _str, const uint_least8_t flags) - : context(NULL), str(_str), compiled(false) -{ - TRACE_CTOR(expr_t, "const string&"); - if (! _str.empty()) - parse(str, flags); -} - -expr_t::expr_t(std::istream& in, const uint_least8_t flags) - : context(NULL), compiled(false) -{ - TRACE_CTOR(expr_t, "std::istream&"); - parse(in, flags); -} - -expr_t::expr_t(const ptr_op_t& _ptr, scope_t * _context, const string& _str) - : ptr(_ptr), context(_context), str(_str), compiled(false) -{ - TRACE_CTOR(expr_t, "const ptr_op_t&, scope_t *, const string&"); -} + base_type::parse(in, flags, original_string); -expr_t::~expr_t() throw() -{ - TRACE_DTOR(expr_t); -} - -expr_t::ptr_op_t expr_t::get_op() throw() -{ - return ptr; -} - -string expr_t::text() -{ - if (str.empty()) { - std::ostringstream out; - ptr->print(out); - set_text(out.str()); - } - return str; -} - -expr_t& expr_t::operator=(const expr_t& _expr) -{ - if (this != &_expr) { - str = _expr.str; - ptr = _expr.ptr; - context = _expr.context; - compiled = _expr.compiled; - } - return *this; -} - -void expr_t::parse(const string& _str, const uint32_t flags) -{ - parser_t parser; - str = _str; - ptr = parser.parse(str, parser_t::parse_flags_t - (static_cast<uint_least8_t>(flags))); - context = NULL; - compiled = false; -} - -void expr_t::parse(std::istream& in, const uint32_t flags, - const string * original_string) -{ parser_t parser; - str = "<stream>"; - ptr = parser.parse(in, parser_t::parse_flags_t - (static_cast<uint_least8_t>(flags)), original_string); - context = NULL; - compiled = false; -} - -void expr_t::recompile(scope_t& scope) -{ - if (ptr.get()) { - ptr = ptr->compile(scope); - context = &scope; - compiled = true; - } + ptr = parser.parse(in, flags, original_string); } void expr_t::compile(scope_t& scope) { - if (! compiled) - recompile(scope); + if (! compiled && ptr) { + ptr = ptr->compile(scope); + base_type::compile(scope); + } } -value_t expr_t::calc(scope_t& scope) +value_t expr_t::real_calc(scope_t& scope) { - if (ptr.get()) { - if (! compiled) { - if (SHOW_DEBUG("expr.compile")) { - DEBUG("expr.compile", "Before compilation:"); - dump(*_log_stream); - } - - compile(scope); - - if (SHOW_DEBUG("expr.compile")) { - DEBUG("expr.compile", "After compilation:"); - dump(*_log_stream); - } - } - + if (ptr) { ptr_op_t locus; try { return ptr->calc(scope, &locus); @@ -170,13 +74,13 @@ value_t expr_t::calc(scope_t& scope) bool expr_t::is_constant() const { assert(compiled); - return ptr.get() && ptr->is_value(); + return ptr && ptr->is_value(); } bool expr_t::is_function() const { assert(compiled); - return ptr.get() && ptr->is_function(); + return ptr && ptr->is_function(); } value_t& expr_t::constant_value() @@ -191,15 +95,15 @@ const value_t& expr_t::constant_value() const return ptr->as_value(); } -function_t& expr_t::get_function() +expr_t::func_t& expr_t::get_function() { assert(is_function()); return ptr->as_function_lval(); } -value_t expr_t::eval(const string& _expr, scope_t& scope) +string expr_t::context_to_str() const { - return expr_t(_expr).calc(scope); + return ptr ? op_context(ptr) : _("<empty expression>"); } void expr_t::print(std::ostream& out) const @@ -213,14 +117,4 @@ void expr_t::dump(std::ostream& out) const if (ptr) ptr->dump(out, 0); } -std::ostream& operator<<(std::ostream& out, const expr_t& expr) { - expr.print(out); - return out; -} - -string expr_context(const expr_t& expr) -{ - return expr ? op_context(expr.ptr) : _("<empty expression>"); -} - } // namespace ledger @@ -38,131 +38,95 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _EXPR_H #define _EXPR_H +#include "exprbase.h" #include "value.h" namespace ledger { -DECLARE_EXCEPTION(parse_error, std::runtime_error); -DECLARE_EXCEPTION(compile_error, std::runtime_error); -DECLARE_EXCEPTION(calc_error, std::runtime_error); -DECLARE_EXCEPTION(usage_error, std::runtime_error); - -class scope_t; -class call_scope_t; - -typedef function<value_t (call_scope_t&)> function_t; - -/** - * @brief Brief - * - * Long. - */ -class expr_t +class expr_t : public expr_base_t<value_t> { struct token_t; class parser_t; - friend string expr_context(const expr_t& expr); + typedef expr_base_t<value_t> base_type; public: class op_t; typedef intrusive_ptr<op_t> ptr_op_t; typedef intrusive_ptr<const op_t> const_ptr_op_t; - enum parse_flags_enum_t { - PARSE_NORMAL = 0x00, - PARSE_PARTIAL = 0x01, - PARSE_SINGLE = 0x02, - PARSE_NO_MIGRATE = 0x04, - PARSE_NO_REDUCE = 0x08, - PARSE_NO_ASSIGN = 0x10, - PARSE_NO_DATES = 0x20, - PARSE_OP_CONTEXT = 0x40 - }; - -private: - ptr_op_t ptr; - scope_t * context; - string str; - bool compiled; +protected: + ptr_op_t ptr; public: - expr_t(); - expr_t(const expr_t& other); - expr_t(const ptr_op_t& _ptr, scope_t * context = NULL, - const string& _str = ""); - - expr_t(const string& _str, const uint_least8_t flags = 0); - expr_t(std::istream& in, const uint_least8_t flags = 0); - - ~expr_t() throw(); - - expr_t& operator=(const expr_t& _expr); - expr_t& operator=(const string& _expr) { - parse(_expr); - return *this; + expr_t() : base_type() { + TRACE_CTOR(expr_t, ""); } - - operator bool() const throw() { - return ptr.get() != NULL; + expr_t(const expr_t& other) + : base_type(other), ptr(other.ptr) { + TRACE_CTOR(expr_t, "copy"); } - - ptr_op_t get_op() throw(); - string text(); - - // This has special use in the textual parser - void set_text(const string& txt) { - str = txt; + expr_t(ptr_op_t _ptr, scope_t * _context = NULL) + : base_type(_context), ptr(_ptr) { + TRACE_CTOR(expr_t, "const ptr_op_t&, scope_t *"); } - void parse(const string& _str, const uint32_t flags = 0); - void parse(std::istream& in, const uint32_t flags = 0, - const string * original_string = NULL); - - void mark_uncompiled() { - compiled = false; + expr_t(const string& _str, const parse_flags_t& flags = PARSE_DEFAULT) + : base_type() { + TRACE_CTOR(expr_t, "string, parse_flags_t"); + if (! _str.empty()) + parse(_str, flags); } - void recompile(scope_t& scope); - void compile(scope_t& scope); - value_t calc(scope_t& scope); - value_t calc(scope_t& scope) const; - - value_t calc() { - assert(context); - return calc(*context); + expr_t(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT) + : base_type() { + TRACE_CTOR(expr_t, "std::istream&, parse_flags_t"); + parse(in, flags); } - value_t calc() const { - assert(context); - return calc(*context); + + ~expr_t() throw() { + TRACE_DTOR(expr_t); } - scope_t * get_context() { - return context; + expr_t& operator=(const expr_t& _expr) { + if (this != &_expr) { + base_type::operator=(_expr); + ptr = _expr.ptr; + } + return *this; } - void set_context(scope_t * scope) { - context = scope; + + virtual operator bool() const throw() { + return ptr.get() != NULL; } - bool is_constant() const; - bool is_function() const; + ptr_op_t get_op() throw() { + return ptr; + } - value_t& constant_value(); - const value_t& constant_value() const; + void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) { + std::istringstream stream(str); + return parse(stream, flags, str); + } - function_t& get_function(); + virtual void parse(std::istream& in, + const parse_flags_t& flags = PARSE_DEFAULT, + const optional<string>& original_string = none); + virtual void compile(scope_t& scope); + virtual value_t real_calc(scope_t& scope); - void print(std::ostream& out) const; - void dump(std::ostream& out) const; + bool is_constant() const; + value_t& constant_value(); + const value_t& constant_value() const; + bool is_function() const; + func_t& get_function(); - static value_t eval(const string& _expr, scope_t& scope); + virtual string context_to_str() const; + virtual void print(std::ostream& out) const; + virtual void dump(std::ostream& out) const; #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -172,19 +136,12 @@ private: template<class Archive> void serialize(Archive& ar, const unsigned int /* version */) { + ar & boost::serialization::base_object<base_type>(*this); ar & ptr; - ar & context; - ar & str; - if (Archive::is_loading::value) - compiled = false; } #endif // HAVE_BOOST_SERIALIZATION }; -std::ostream& operator<<(std::ostream& out, const expr_t& expr); - -string expr_context(const expr_t& expr); - } // namespace ledger #endif // _EXPR_H diff --git a/src/exprbase.h b/src/exprbase.h new file mode 100644 index 00000000..d2bf5a6d --- /dev/null +++ b/src/exprbase.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup expr + */ + +/** + * @file exprbase.h + * @author John Wiegley + * + * @ingroup expr + * + * This class provides basic behavior for all the domain specific expression + * languages used in Leger: + * + * | Typename | Description | result_type | Derives | + * |-------------+----------------------------+-----------------+-------------| + * | expr_t | Value expressions | value_t | | + * | predicate_t | Special form of expr_t | bool | expr_t | + * | query_t | Report queries | bool | predicate_t | + * | period_t | Time periods and durations | date_interval_t | | + * | draft_t | Partially filled xacts | xact_t * | | + * | format_t | Format strings | string | | + */ +#ifndef _EXPRBASE_H +#define _EXPRBASE_H + +#include "utils.h" +#include "amount.h" + +namespace ledger { + +DECLARE_EXCEPTION(parse_error, std::runtime_error); +DECLARE_EXCEPTION(compile_error, std::runtime_error); +DECLARE_EXCEPTION(calc_error, std::runtime_error); +DECLARE_EXCEPTION(usage_error, std::runtime_error); + +class scope_t; +class call_scope_t; + +template <typename ResultType> +class expr_base_t +{ +public: + typedef ResultType result_type; + + typedef function<result_type (call_scope_t&)> func_t; + +protected: + scope_t * context; + string str; + bool compiled; + + virtual result_type real_calc(scope_t& scope) = 0; + +public: + expr_base_t(const expr_base_t& other) + : context(other.context), str(other.str), compiled(false) { + TRACE_CTOR(expr_base_t, "copy"); + } + expr_base_t(scope_t * _context = NULL) + : context(_context), compiled(false) + { + TRACE_CTOR(expr_base_t, "scope_t *"); + } + + ~expr_base_t() throw() { + TRACE_DTOR(expr_base_t); + } + + expr_base_t& operator=(const expr_base_t& _expr) { + if (this != &_expr) { + str = _expr.str; + context = _expr.context; + compiled = _expr.compiled; + } + return *this; + } + expr_base_t& operator=(const string& _expr) { + parse(_expr); + return *this; + } + + virtual operator bool() const throw() { + return ! str.empty(); + } + + virtual string text() { + return str; + } + void set_text(const string& txt) { + str = txt; + compiled = false; + } + + void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) { + std::istringstream stream(str); + return parse(stream, flags, str); + } + virtual void parse(std::istream&, + const parse_flags_t& = PARSE_DEFAULT, + const optional<string>& original_string = none) { + set_text(original_string ? *original_string : "<stream>"); + } + + void mark_uncompiled() { + compiled = false; + } + + void recompile(scope_t& scope) { + compiled = false; + compile(scope); + } + + virtual void compile(scope_t& scope) { + if (! compiled) { + // Derived classes need to do something here. + context = &scope; + compiled = true; + } + } + + result_type operator()(scope_t& scope) { + return calc(scope); + } + + result_type calc(scope_t& scope) + { + if (! compiled) { + if (SHOW_DEBUG("expr.compile")) { + DEBUG("expr.compile", "Before compilation:"); + dump(*_log_stream); + } + + compile(scope); + + if (SHOW_DEBUG("expr.compile")) { + DEBUG("expr.compile", "After compilation:"); + dump(*_log_stream); + } + } + + return real_calc(scope); + } + + result_type calc() { + assert(context); + return calc(*context); + } + + scope_t * get_context() { + return context; + } + void set_context(scope_t * scope) { + context = scope; + } + + virtual string context_to_str() const { + return empty_string; + } + + string print_to_str() const { + std::ostringstream out; + print(out); + return out.str(); + } + string dump_to_str() const { + std::ostringstream out; + dump(out); + return out.str(); + } + string preview_to_str(scope_t& scope) const { + std::ostringstream out; + preview(out); + return out.str(); + } + + virtual void print(std::ostream&) const {} + virtual void dump(std::ostream&) const {} + + result_type preview(std::ostream& out, scope_t& scope) const { + out << _("--- Input expression ---") << std::endl; + out << text() << std::endl; + + out << std::endl << _("--- Text as parsed ---") << std::endl; + print(out); + out << std::endl; + + out << std::endl << _("--- Expression tree ---") << std::endl; + dump(out); + + out << std::endl << _("--- Compiled tree ---") << std::endl; + compile(scope); + dump(out); + + out << std::endl << _("--- Result value ---") << std::endl; + return calc(); + } + +#if defined(HAVE_BOOST_SERIALIZATION) +private: + /** Serialization. */ + + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive& ar, const unsigned int /* version */) { + ar & context; + ar & str; + if (Archive::is_loading::value) + compiled = false; + } +#endif // HAVE_BOOST_SERIALIZATION +}; + +template <typename ResultType> +std::ostream& operator<<(std::ostream& out, + const expr_base_t<ResultType>& expr) { + expr.print(out); + return out; +} + +} // namespace ledger + +#endif // _EXPRBASE_H diff --git a/src/filters.cc b/src/filters.cc index 0469d6ec..4b54a0cd 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -947,10 +947,10 @@ void forecast_posts::flush() item_handler<post_t>::flush(); } -pass_down_accounts::pass_down_accounts(acct_handler_ptr handler, - accounts_iterator& iter, - const optional<item_predicate>& _pred, - const optional<scope_t&>& _context) +pass_down_accounts::pass_down_accounts(acct_handler_ptr handler, + accounts_iterator& iter, + const optional<predicate_t>& _pred, + const optional<scope_t&>& _context) : item_handler<account_t>(handler), pred(_pred), context(_context) { TRACE_CTOR(pass_down_accounts, "acct_handler_ptr, accounts_iterator, ..."); diff --git a/src/filters.h b/src/filters.h index 42503945..ba3692ac 100644 --- a/src/filters.h +++ b/src/filters.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _FILTERS_H #define _FILTERS_H @@ -59,11 +55,6 @@ namespace ledger { // Posting filters // -/** - * @brief Brief - * - * Long. - */ class ignore_posts : public item_handler<post_t> { public: @@ -72,11 +63,6 @@ public: class posts_iterator; -/** - * @brief Brief - * - * Long. - */ class pass_down_posts : public item_handler<post_t> { pass_down_posts(); @@ -89,11 +75,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class push_to_posts_list : public item_handler<post_t> { push_to_posts_list(); @@ -113,11 +94,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class truncate_xacts : public item_handler<post_t> { int head_count; @@ -145,11 +121,6 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class sort_posts : public item_handler<post_t> { typedef std::deque<post_t *> posts_deque; @@ -190,11 +161,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class sort_xacts : public item_handler<post_t> { sort_posts sorter; @@ -234,25 +200,20 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class filter_posts : public item_handler<post_t> { - item_predicate pred; - scope_t& context; + predicate_t pred; + scope_t& context; filter_posts(); public: - filter_posts(post_handler_ptr handler, - const item_predicate& predicate, - scope_t& _context) + filter_posts(post_handler_ptr handler, + const predicate_t& predicate, + scope_t& _context) : item_handler<post_t>(handler), pred(predicate), context(_context) { TRACE_CTOR(filter_posts, - "post_handler_ptr, const item_predicate&, scope_t&"); + "post_handler_ptr, const predicate_t&, scope_t&"); } virtual ~filter_posts() { TRACE_DTOR(filter_posts); @@ -267,11 +228,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class anonymize_posts : public item_handler<post_t> { temporaries_t temps; @@ -291,11 +247,6 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class calc_posts : public item_handler<post_t> { post_t * last_post; @@ -319,16 +270,11 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class collapse_posts : public item_handler<post_t> { expr_t& amount_expr; - item_predicate display_predicate; - item_predicate only_predicate; + predicate_t display_predicate; + predicate_t only_predicate; value_t subtotal; std::size_t count; xact_t * last_xact; @@ -343,8 +289,8 @@ class collapse_posts : public item_handler<post_t> public: collapse_posts(post_handler_ptr handler, expr_t& _amount_expr, - item_predicate _display_predicate, - item_predicate _only_predicate, + predicate_t _display_predicate, + predicate_t _only_predicate, bool _only_collapse_if_zero = false) : item_handler<post_t>(handler), amount_expr(_amount_expr), display_predicate(_display_predicate), @@ -368,11 +314,6 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class related_posts : public item_handler<post_t> { posts_list posts; @@ -399,11 +340,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class changed_value_posts : public item_handler<post_t> { // This filter requires that calc_posts be used at some point @@ -451,11 +387,6 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class subtotal_posts : public item_handler<post_t> { subtotal_posts(); @@ -517,11 +448,6 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class interval_posts : public subtotal_posts { date_interval_t interval; @@ -590,11 +516,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class by_payee_posts : public item_handler<post_t> { typedef std::map<string, shared_ptr<subtotal_posts> > payee_subtotals_map; @@ -618,11 +539,6 @@ class by_payee_posts : public item_handler<post_t> virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class transfer_details : public item_handler<post_t> { account_t * master; @@ -655,11 +571,6 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class dow_posts : public subtotal_posts { posts_list days_of_the_week[7]; @@ -681,11 +592,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class generate_posts : public item_handler<post_t> { generate_posts(); @@ -712,11 +618,6 @@ public: virtual void add_post(const date_interval_t& period, post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class budget_posts : public generate_posts { #define BUDGET_NO_BUDGET 0x00 @@ -743,26 +644,21 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class forecast_posts : public generate_posts { - item_predicate pred; + predicate_t pred; scope_t& context; const std::size_t forecast_years; public: - forecast_posts(post_handler_ptr handler, - const item_predicate& predicate, - scope_t& _context, - const std::size_t _forecast_years) + forecast_posts(post_handler_ptr handler, + const predicate_t& predicate, + scope_t& _context, + const std::size_t _forecast_years) : generate_posts(handler), pred(predicate), context(_context), forecast_years(_forecast_years) { TRACE_CTOR(forecast_posts, - "post_handler_ptr, item_predicate, scope_t&, std::size_t"); + "post_handler_ptr, predicate_t, scope_t&, std::size_t"); } virtual ~forecast_posts() throw() { TRACE_DTOR(forecast_posts); @@ -779,23 +675,18 @@ class forecast_posts : public generate_posts class accounts_iterator; -/** - * @brief Brief - * - * Long. - */ class pass_down_accounts : public item_handler<account_t> { pass_down_accounts(); - optional<item_predicate> pred; - optional<scope_t&> context; + optional<predicate_t> pred; + optional<scope_t&> context; public: - pass_down_accounts(acct_handler_ptr handler, - accounts_iterator& iter, - const optional<item_predicate>& _pred = none, - const optional<scope_t&>& _context = none); + pass_down_accounts(acct_handler_ptr handler, + accounts_iterator& iter, + const optional<predicate_t>& _pred = none, + const optional<scope_t&>& _context = none); virtual ~pass_down_accounts() { TRACE_DTOR(pass_down_accounts); diff --git a/src/flags.h b/src/flags.h index 791b3105..69e40e4b 100644 --- a/src/flags.h +++ b/src/flags.h @@ -38,20 +38,11 @@ * @author John Wiegley * * @ingroup util - * - * @brief Brief - * - * Long. */ #ifndef _FLAGS_H #define _FLAGS_H -/** - * @brief Brief - * - * Long. - */ template <typename T = boost::uint_least8_t, typename U = T> class supports_flags { @@ -112,11 +103,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ template <typename T = boost::uint_least8_t, typename U = T> class basic_flags_t : public supports_flags<T, U> { @@ -168,11 +154,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ template <typename T = boost::uint_least8_t> class delegates_flags : public boost::noncopyable { diff --git a/src/format.cc b/src/format.cc index 8ac14aa2..d949c350 100644 --- a/src/format.cc +++ b/src/format.cc @@ -61,8 +61,12 @@ void format_t::element_t::dump(std::ostream& out) const out << std::dec << int(max_width); switch (type) { - case STRING: out << " str: '" << chars << "'" << std::endl; break; - case EXPR: out << " expr: " << expr << std::endl; break; + case STRING: + out << " str: '" << boost::get<string>(data) << "'" << std::endl; + break; + case EXPR: + out << " expr: " << boost::get<expr_t>(data) << std::endl; + break; } } @@ -72,8 +76,7 @@ namespace { string temp(p); ptristream str(const_cast<char *&>(p)); expr_t expr; - expr.parse(str, single_expr ? expr_t::PARSE_SINGLE : expr_t::PARSE_PARTIAL, - &temp); + expr.parse(str, single_expr ? PARSE_SINGLE : PARSE_PARTIAL, temp); if (str.eof()) { expr.set_text(p); p += std::strlen(p); @@ -118,7 +121,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt, if (q != buf) { current->type = element_t::STRING; - current->chars = string(buf, q); + current->data = string(buf, q); q = buf; current->next.reset(new element_t); @@ -129,14 +132,14 @@ format_t::element_t * format_t::parse_elements(const string& fmt, p++; current->type = element_t::STRING; switch (*p) { - case 'b': current->chars = "\b"; break; - case 'f': current->chars = "\f"; break; - case 'n': current->chars = "\n"; break; - case 'r': current->chars = "\r"; break; - case 't': current->chars = "\t"; break; - case 'v': current->chars = "\v"; break; - case '\\': current->chars = "\\"; break; - default: current->chars = string(1, *p); break; + case 'b': current->data = string("\b"); break; + case 'f': current->data = string("\f"); break; + case 'n': current->data = string("\n"); break; + case 'r': current->data = string("\r"); break; + case 't': current->data = string("\t"); break; + case 'v': current->data = string("\v"); break; + case '\\': current->data = string("\\"); break; + default: current->data = string(1, *p); break; } continue; } @@ -172,8 +175,8 @@ format_t::element_t * format_t::parse_elements(const string& fmt, switch (*p) { case '%': - current->type = element_t::STRING; - current->chars = "%"; + current->type = element_t::STRING; + current->data = string("%"); break; case '$': { @@ -208,7 +211,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt, if (format_amount) p++; current->type = element_t::EXPR; - current->expr = parse_single_expression(p, ! format_amount); + current->data = parse_single_expression(p, ! format_amount); // Wrap the subexpression in calls to justify and scrub if (format_amount) { @@ -217,7 +220,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt, else p++; - expr_t::ptr_op_t op = current->expr.get_op(); + expr_t::ptr_op_t op = boost::get<expr_t>(current->data).get_op(); expr_t::ptr_op_t amount_op; expr_t::ptr_op_t colorize_op; @@ -239,8 +242,10 @@ format_t::element_t * format_t::parse_elements(const string& fmt, expr_t::ptr_op_t arg2_node(new expr_t::op_t(expr_t::op_t::VALUE)); expr_t::ptr_op_t arg3_node(new expr_t::op_t(expr_t::op_t::VALUE)); - arg1_node->set_value(current->min_width > 0 ? long(current->min_width) : -1); - arg2_node->set_value(current->max_width > 0 ? long(current->max_width) : -1); + arg1_node->set_value(current->min_width > 0 ? + long(current->min_width) : -1); + arg2_node->set_value(current->max_width > 0 ? + long(current->max_width) : -1); arg3_node->set_value(! current->has_flags(ELEMENT_ALIGN_LEFT)); current->min_width = 0; @@ -265,7 +270,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt, call2_node->set_left(justify_node); call2_node->set_right(args3_node); - string prev_expr = current->expr.text(); + string prev_expr = boost::get<expr_t>(current->data).text(); if (colorize_op) { expr_t::ptr_op_t ansify_if_node(new expr_t::op_t(expr_t::op_t::IDENT)); @@ -279,21 +284,18 @@ format_t::element_t * format_t::parse_elements(const string& fmt, call3_node->set_left(ansify_if_node); call3_node->set_right(args4_node); - current->expr = expr_t(call3_node); + current->data = expr_t(call3_node); } else { - current->expr = expr_t(call2_node); + current->data = expr_t(call2_node); } - current->expr.set_text(prev_expr); + boost::get<expr_t>(current->data).set_text(prev_expr); } break; } default: - current->type = element_t::EXPR; - current->chars = string(FMT_PREFIX) + *p; - current->expr.parse(current->chars); - break; + throw_(format_error, _("Unrecognized formatting character: %1") << *p); } } @@ -305,15 +307,17 @@ format_t::element_t * format_t::parse_elements(const string& fmt, current->next.reset(new element_t); current = current->next.get(); } - current->type = element_t::STRING; - current->chars = string(buf, q); + current->type = element_t::STRING; + current->data = string(buf, q); } return result.release(); } -void format_t::format(std::ostream& out_str, scope_t& scope) +string format_t::real_calc(scope_t& scope) { + std::ostringstream out_str; + for (element_t * elem = elements.get(); elem; elem = elem->next.get()) { std::ostringstream out; string name; @@ -327,20 +331,22 @@ void format_t::format(std::ostream& out_str, scope_t& scope) case element_t::STRING: if (elem->min_width > 0) out.width(elem->min_width); - out << elem->chars; + out << boost::get<string>(elem->data); break; - case element_t::EXPR: + case element_t::EXPR: { + expr_t& expr(boost::get<expr_t>(elem->data)); try { - elem->expr.compile(scope); + + expr.compile(scope); value_t value; - if (elem->expr.is_function()) { + if (expr.is_function()) { call_scope_t args(scope); args.push_back(long(elem->max_width)); - value = elem->expr.get_function()(args); + value = expr.get_function()(args); } else { - value = elem->expr.calc(scope); + value = expr.calc(scope); } DEBUG("format.expr", "value = (" << value << ")"); @@ -349,10 +355,11 @@ void format_t::format(std::ostream& out_str, scope_t& scope) } catch (const calc_error&) { add_error_context(_("While calculating format expression:")); - add_error_context(expr_context(elem->expr)); + add_error_context(expr.context_to_str()); throw; } break; + } default: assert(false); @@ -376,6 +383,8 @@ void format_t::format(std::ostream& out_str, scope_t& scope) out_str << out.str(); } } + + return out_str.str(); } string format_t::truncate(const unistring& ustr, diff --git a/src/format.h b/src/format.h index 8b2a7965..b3ae464f 100644 --- a/src/format.h +++ b/src/format.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _FORMAT_H #define _FORMAT_H @@ -54,25 +50,20 @@ class unistring; DECLARE_EXCEPTION(format_error, std::runtime_error); -/** - * @brief Brief - * - * Long. - */ -class format_t : public noncopyable +class format_t : public expr_base_t<string> { + typedef expr_base_t<string> base_type; + struct element_t : public supports_flags<> { #define ELEMENT_ALIGN_LEFT 0x01 enum kind_t { STRING, EXPR }; - kind_t type; - std::size_t min_width; - std::size_t max_width; - string chars; - expr_t expr; - + kind_t type; + std::size_t min_width; + std::size_t max_width; + variant<string, expr_t> data; scoped_ptr<struct element_t> next; element_t() throw() @@ -92,8 +83,7 @@ class format_t : public noncopyable type = elem.type; min_width = elem.min_width; max_width = elem.max_width; - chars = elem.chars; - expr = elem.expr; + data = elem.data; } return *this; } @@ -115,8 +105,7 @@ class format_t : public noncopyable void dump(std::ostream& out) const; }; - string format_string; - scoped_ptr<element_t> elements; + scoped_ptr<element_t> elements; public: static enum elision_style_t { @@ -133,25 +122,28 @@ private: const optional<format_t&>& tmpl); public: - format_t() { + format_t() : base_type() { TRACE_CTOR(format_t, ""); } - format_t(const string& _format) { + format_t(const string& _str, scope_t * context = NULL) + : base_type(context) { TRACE_CTOR(format_t, "const string&"); - parse(_format); + if (! _str.empty()) + parse_format(_str); } ~format_t() { TRACE_DTOR(format_t); } - void parse(const string& _format, const optional<format_t&>& tmpl = none) { + void parse_format(const string& _format, + const optional<format_t&>& tmpl = none) { elements.reset(parse_elements(_format, tmpl)); - format_string = _format; + set_text(_format); } - void format(std::ostream& out, scope_t& scope); + virtual result_type real_calc(scope_t& scope); - void dump(std::ostream& out) const { + virtual void dump(std::ostream& out) const { for (const element_t * elem = elements.get(); elem; elem = elem->next.get()) @@ -163,12 +155,6 @@ public: const std::size_t account_abbrev_length = 0); }; -#define FMT_PREFIX "fmt_" -#define FMT_PREFIX_LEN 4 - -#define WANT_FMT() \ - (std::strncmp(p, FMT_PREFIX, FMT_PREFIX_LEN) == 0) - } // namespace ledger #endif // _FORMAT_H diff --git a/src/generate.h b/src/generate.h index ee8c564d..66513fc8 100644 --- a/src/generate.h +++ b/src/generate.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _GENERATE_H #define _GENERATE_H diff --git a/src/global.cc b/src/global.cc index afc63eb0..0c8cedd8 100644 --- a/src/global.cc +++ b/src/global.cc @@ -179,9 +179,9 @@ void global_scope_t::execute_command(strings_list args, bool at_repl) // If such a command is found, create the output stream for the result and // then invoke the command. - function_t command; - bool is_precommand = false; - bind_scope_t bound_scope(*this, report()); + expr_t::func_t command; + bool is_precommand = false; + bind_scope_t bound_scope(*this, report()); if (bool(command = look_for_precommand(bound_scope, verb))) is_precommand = true; @@ -398,22 +398,22 @@ void global_scope_t::normalize_session_options() INFO("Journal file is " << pathname.string()); } -function_t global_scope_t::look_for_precommand(scope_t& scope, - const string& verb) +expr_t::func_t global_scope_t::look_for_precommand(scope_t& scope, + const string& verb) { if (expr_t::ptr_op_t def = scope.lookup(symbol_t::PRECOMMAND, verb)) return def->as_function(); else - return function_t(); + return expr_t::func_t(); } -function_t global_scope_t::look_for_command(scope_t& scope, - const string& verb) +expr_t::func_t global_scope_t::look_for_command(scope_t& scope, + const string& verb) { if (expr_t::ptr_op_t def = scope.lookup(symbol_t::COMMAND, verb)) return def->as_function(); else - return function_t(); + return expr_t::func_t(); } void global_scope_t::normalize_report_options(const string& verb) diff --git a/src/global.h b/src/global.h index 31a0a480..f7b87973 100644 --- a/src/global.h +++ b/src/global.h @@ -56,13 +56,13 @@ public: global_scope_t(char ** envp); ~global_scope_t(); - void read_init(); - void read_environment_settings(char * envp[]); - strings_list read_command_arguments(scope_t& scope, strings_list args); - void normalize_session_options(); - function_t look_for_precommand(scope_t& scope, const string& verb); - function_t look_for_command(scope_t& scope, const string& verb); - void normalize_report_options(const string& verb); + void read_init(); + void read_environment_settings(char * envp[]); + strings_list read_command_arguments(scope_t& scope, strings_list args); + void normalize_session_options(); + expr_t::func_t look_for_precommand(scope_t& scope, const string& verb); + expr_t::func_t look_for_command(scope_t& scope, const string& verb); + void normalize_report_options(const string& verb); char * prompt_string(); diff --git a/src/hooks.h b/src/hooks.h index 511749ca..da6fcf84 100644 --- a/src/hooks.h +++ b/src/hooks.h @@ -38,20 +38,10 @@ * @author John Wiegley * * @ingroup util - * - * @brief Brief - * - * Long. */ #ifndef _HOOKS_H #define _HOOKS_H - -/** - * @brief Brief - * - * Long. - */ template <typename T, typename Data> class hooks_t : public boost::noncopyable { diff --git a/src/interactive.cc b/src/interactive.cc index 61273f06..35382e8f 100644 --- a/src/interactive.cc +++ b/src/interactive.cc @@ -181,12 +181,11 @@ string join_args(call_scope_t& args) bool first = true; for (std::size_t i = 0; i < args.size(); i++) { - if (first) { - buf << args[i]; + if (first) first = false; - } else { - buf << ' ' << args[i]; - } + else + buf << ' '; + buf << args[i]; } return buf.str(); diff --git a/src/interactive.h b/src/interactive.h index 0396b1c8..199b7b71 100644 --- a/src/interactive.h +++ b/src/interactive.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _INTERACTIVE_H #define _INTERACTIVE_H @@ -50,11 +46,6 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ class interactive_t : public noncopyable { call_scope_t& args; @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup data - * - * @brief Brief - * - * Long. */ #ifndef _ITEM_H #define _ITEM_H @@ -97,11 +93,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class item_t : public supports_flags<>, public scope_t { public: diff --git a/src/iterators.h b/src/iterators.h index 8aa1b451..1cbe4c25 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup data - * - * @brief Brief - * - * Long. */ #ifndef _ITERATORS_H #define _ITERATORS_H @@ -55,11 +51,6 @@ namespace ledger { class journal_t; -/** - * @brief Brief - * - * Long. - */ class posts_iterator : public noncopyable { public: @@ -67,11 +58,6 @@ public: virtual post_t * operator()() = 0; }; -/** - * @brief Brief - * - * Long. - */ class xact_posts_iterator : public posts_iterator { posts_list::iterator posts_i; @@ -106,11 +92,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class xacts_iterator : public noncopyable { public: @@ -135,11 +116,6 @@ public: xact_t * operator()(); }; -/** - * @brief Brief - * - * Long. - */ class journal_posts_iterator : public posts_iterator { xacts_iterator xacts; @@ -162,11 +138,6 @@ public: virtual post_t * operator()(); }; -/** - * @brief Brief - * - * Long. - */ class posts_commodities_iterator : public posts_iterator { protected: @@ -193,11 +164,6 @@ public: virtual post_t * operator()(); }; -/** - * @brief Brief - * - * Long. - */ class accounts_iterator : public noncopyable { public: @@ -205,11 +171,6 @@ public: virtual account_t * operator()() = 0; }; -/** - * @brief Brief - * - * Long. - */ class basic_accounts_iterator : public accounts_iterator { std::list<accounts_map::const_iterator> accounts_i; @@ -235,11 +196,6 @@ public: virtual account_t * operator()(); }; -/** - * @brief Brief - * - * Long. - */ class sorted_accounts_iterator : public accounts_iterator { expr_t sort_cmp; diff --git a/src/journal.h b/src/journal.h index ff2b5f84..78269348 100644 --- a/src/journal.h +++ b/src/journal.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup data - * - * @brief Brief - * - * Long. */ #ifndef _JOURNAL_H #define _JOURNAL_H @@ -64,11 +60,6 @@ typedef std::list<xact_t *> xacts_list; typedef std::list<auto_xact_t *> auto_xacts_list; typedef std::list<period_xact_t *> period_xacts_list; -/** - * @brief Brief - * - * Long. - */ class journal_t : public noncopyable { public: @@ -51,11 +51,6 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ class mask_t { public: @@ -151,6 +146,12 @@ inline std::ostream& operator<<(std::ostream& out, const mask_t& mask) { return out; } +inline void to_xml(std::ostream& out, const mask_t& mask) +{ + push_xml x(out, "mask"); + out << x.guard(mask.expr.str()); +} + } // namespace ledger #endif // _MASK_H @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _OP_H #define _OP_H @@ -50,11 +46,6 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ class expr_t::op_t : public noncopyable { friend class expr_t; @@ -70,7 +61,7 @@ private: variant<ptr_op_t, // used by all binary operators value_t, // used by constant VALUE string, // used by constant IDENT - function_t // used by terminal FUNCTION + expr_t::func_t // used by terminal FUNCTION > data; public: @@ -180,14 +171,14 @@ public: bool is_function() const { return kind == FUNCTION; } - function_t& as_function_lval() { + expr_t::func_t& as_function_lval() { assert(kind == FUNCTION); - return boost::get<function_t>(data); + return boost::get<expr_t::func_t>(data); } - const function_t& as_function() const { + const expr_t::func_t& as_function() const { return const_cast<op_t *>(this)->as_function_lval(); } - void set_function(const function_t& val) { + void set_function(const expr_t::func_t& val) { data = val; } @@ -289,7 +280,7 @@ public: void dump(std::ostream& out, const int depth) const; static ptr_op_t wrap_value(const value_t& val); - static ptr_op_t wrap_functor(const function_t& fobj); + static ptr_op_t wrap_functor(const expr_t::func_t& fobj); #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -312,7 +303,7 @@ private: (! has_right() || ! right()->is_function()))) { ar & data; } else { - variant<ptr_op_t, value_t, string, function_t> temp_data; + variant<ptr_op_t, value_t, string, expr_t::func_t> temp_data; ar & temp_data; } } @@ -334,7 +325,8 @@ inline expr_t::ptr_op_t expr_t::op_t::wrap_value(const value_t& val) { return temp; } -inline expr_t::ptr_op_t expr_t::op_t::wrap_functor(const function_t& fobj) { +inline expr_t::ptr_op_t +expr_t::op_t::wrap_functor(const expr_t::func_t& fobj) { ptr_op_t temp(new op_t(op_t::FUNCTION)); temp->set_function(fobj); return temp; diff --git a/src/option.cc b/src/option.cc index 6d230939..d2ec8808 100644 --- a/src/option.cc +++ b/src/option.cc @@ -74,7 +74,7 @@ namespace { return op_bool_tuple(scope.lookup(symbol_t::OPTION, buf), false); } - void process_option(const string& whence, const function_t& opt, + void process_option(const string& whence, const expr_t::func_t& opt, scope_t& scope, const char * arg, const string& name) { try { diff --git a/src/option.h b/src/option.h index c9903c03..b81c2ce7 100644 --- a/src/option.h +++ b/src/option.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _OPTION_H #define _OPTION_H diff --git a/src/output.cc b/src/output.cc index e2bbb7ec..2a6f0c20 100644 --- a/src/output.cc +++ b/src/output.cc @@ -51,17 +51,19 @@ format_posts::format_posts(report_t& _report, const char * f = format.c_str(); if (const char * p = std::strstr(f, "%/")) { - first_line_format.parse(string(f, 0, p - f)); + first_line_format.parse_format(string(f, 0, p - f)); const char * n = p + 2; if (const char * p = std::strstr(n, "%/")) { - next_lines_format.parse(string(n, 0, p - n), first_line_format); - between_format.parse(string(p + 2), first_line_format); + next_lines_format.parse_format(string(n, 0, p - n), + first_line_format); + between_format.parse_format(string(p + 2), + first_line_format); } else { - next_lines_format.parse(n, first_line_format); + next_lines_format.parse_format(string(n), first_line_format); } } else { - first_line_format.parse(format); - next_lines_format.parse(format); + first_line_format.parse_format(format); + next_lines_format.parse_format(format); } } @@ -80,7 +82,7 @@ void format_posts::operator()(post_t& post) if (last_xact != post.xact) { if (last_xact) { bind_scope_t xact_scope(report, *last_xact); - between_format.format(out, xact_scope); + out << between_format(xact_scope); } print_item(out, *post.xact); out << '\n'; @@ -96,16 +98,16 @@ void format_posts::operator()(post_t& post) if (last_xact != post.xact) { if (last_xact) { bind_scope_t xact_scope(report, *last_xact); - between_format.format(out, xact_scope); + out << between_format(xact_scope); } - first_line_format.format(out, bound_scope); + out << first_line_format(bound_scope); last_xact = post.xact; } else if (last_post && last_post->date() != post.date()) { - first_line_format.format(out, bound_scope); + out << first_line_format(bound_scope); } else { - next_lines_format.format(out, bound_scope); + out << next_lines_format(bound_scope); } post.xdata().add_flags(POST_EXT_DISPLAYED); @@ -122,17 +124,17 @@ format_accounts::format_accounts(report_t& _report, const char * f = format.c_str(); if (const char * p = std::strstr(f, "%/")) { - account_line_format.parse(string(f, 0, p - f)); + account_line_format.parse_format(string(f, 0, p - f)); const char * n = p + 2; if (const char * p = std::strstr(n, "%/")) { - total_line_format.parse(string(n, 0, p - n), account_line_format); - separator_format.parse(string(p + 2), account_line_format); + total_line_format.parse_format(string(n, 0, p - n), account_line_format); + separator_format.parse_format(string(p + 2), account_line_format); } else { - total_line_format.parse(n, account_line_format); + total_line_format.parse_format(n, account_line_format); } } else { - account_line_format.parse(format); - total_line_format.parse(format, account_line_format); + account_line_format.parse_format(format); + total_line_format.parse_format(format, account_line_format); } } @@ -148,7 +150,8 @@ std::size_t format_accounts::post_account(account_t& account, const bool flat) account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED); bind_scope_t bound_scope(report, account); - account_line_format.format(report.output_stream, bound_scope); + static_cast<std::ostream&>(report.output_stream) + << account_line_format(bound_scope); return 1; } @@ -200,7 +203,7 @@ void format_accounts::flush() if (report.HANDLED(display_)) { DEBUG("account.display", "Account display predicate: " << report.HANDLER(display_).str()); - disp_pred.predicate.parse(report.HANDLER(display_).str()); + disp_pred.parse(report.HANDLER(display_).str()); } mark_accounts(*report.session.journal->master, report.HANDLED(flat)); @@ -213,8 +216,8 @@ void format_accounts::flush() if (displayed > 1 && ! report.HANDLED(no_total) && ! report.HANDLED(percent)) { bind_scope_t bound_scope(report, *report.session.journal->master); - separator_format.format(out, bound_scope); - total_line_format.format(out, bound_scope); + out << separator_format(bound_scope); + out << total_line_format(bound_scope); } out.flush(); diff --git a/src/output.h b/src/output.h index 5e06db9a..fedd08ff 100644 --- a/src/output.h +++ b/src/output.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _OUTPUT_H #define _OUTPUT_H @@ -57,11 +53,6 @@ class xact_t; class post_t; class report_t; -/** - * @brief Brief - * - * Long. - */ class format_posts : public item_handler<post_t> { protected: @@ -84,19 +75,14 @@ public: virtual void operator()(post_t& post); }; -/** - * @brief Brief - * - * Long. - */ class format_accounts : public item_handler<account_t> { protected: - report_t& report; - format_t account_line_format; - format_t total_line_format; - format_t separator_format; - item_predicate disp_pred; + report_t& report; + format_t account_line_format; + format_t total_line_format; + format_t separator_format; + predicate_t disp_pred; std::list<account_t *> posted_accounts; diff --git a/src/parser.cc b/src/parser.cc index a6053d69..d94cf37f 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -473,8 +473,9 @@ expr_t::parser_t::parse_value_expr(std::istream& in, } expr_t::ptr_op_t -expr_t::parser_t::parse(std::istream& in, const parse_flags_t& flags, - const string * original_string) +expr_t::parser_t::parse(std::istream& in, + const parse_flags_t& flags, + const optional<string>& original_string) { try { ptr_op_t top_node = parse_value_expr(in, flags); diff --git a/src/parser.h b/src/parser.h index 9c80cf38..7a69fe08 100644 --- a/src/parser.h +++ b/src/parser.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _PARSER_H #define _PARSER_H @@ -51,17 +47,8 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ class expr_t::parser_t : public noncopyable { -public: - typedef basic_flags_t<parse_flags_enum_t, uint_least8_t> parse_flags_t; - -private: mutable token_t lookahead; mutable bool use_lookahead; @@ -110,14 +97,9 @@ public: TRACE_DTOR(parser_t); } - ptr_op_t parse(std::istream& in, - const parse_flags_t& flags = PARSE_NORMAL, - const string * original_string = NULL); - ptr_op_t parse(const string& str, - const parse_flags_t& flags = PARSE_NORMAL) { - std::istringstream stream(str); - return parse(stream, flags, &str); - } + ptr_op_t parse(std::istream& in, + const parse_flags_t& flags = PARSE_DEFAULT, + const optional<string>& original_string = NULL); }; } // namespace ledger @@ -48,11 +48,6 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ struct cost_breakdown_t { amount_t amount; @@ -60,11 +55,6 @@ struct cost_breakdown_t amount_t basis_cost; }; -/** - * @brief Brief - * - * Long. - */ class commodity_pool_t : public noncopyable { /** diff --git a/src/post.cc b/src/post.cc index 0fd763a9..24705323 100644 --- a/src/post.cc +++ b/src/post.cc @@ -452,4 +452,99 @@ void post_t::set_reported_account(account_t * account) account->xdata().reported_posts.push_back(this); } +void to_xml(std::ostream& out, const post_t& post) +{ + push_xml x(out, "posting", true); + + if (post.state() == item_t::CLEARED) + out << " state=\"cleared\""; + else if (post.state() == item_t::PENDING) + out << " state=\"pending\""; + + if (post.has_flags(POST_VIRTUAL)) + out << " virtual=\"true\""; + if (post.has_flags(ITEM_GENERATED)) + out << " generated=\"true\""; + + x.close_attrs(); + + if (post._date) { + push_xml y(out, "date"); + to_xml(out, *post._date, false); + } + if (post._date_eff) { + push_xml y(out, "effective-date"); + to_xml(out, *post._date_eff, false); + } + + if (post.account) { + push_xml y(out, "account", true); + + out << " ref=\""; + out.width(sizeof(unsigned long) * 2); + out.fill('0'); + out << std::hex << reinterpret_cast<unsigned long>(post.account); + out << '"'; + y.close_attrs(); + + { + push_xml z(out, "name"); + out << z.guard(post.account->fullname()); + } + } + + { + push_xml y(out, "post-amount"); + if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) + to_xml(out, post.xdata().compound_value); + else + to_xml(out, post.amount); + } + + if (post.cost) { + push_xml y(out, "cost"); + to_xml(out, *post.cost); + } + + if (post.assigned_amount) { + if (post.has_flags(POST_CALCULATED)) { + push_xml y(out, "balance-assertion"); + to_xml(out, *post.assigned_amount); + } else { + push_xml y(out, "balance-assignment"); + to_xml(out, *post.assigned_amount); + } + } + + if (post.note) { + push_xml y(out, "note"); + out << y.guard(*post.note); + } + + if (post.metadata) { + push_xml y(out, "metadata"); + foreach (const item_t::string_map::value_type& pair, *post.metadata) { + if (pair.second) { + push_xml z(out, "variable"); + { + push_xml z(out, "key"); + out << y.guard(pair.first); + } + { + push_xml z(out, "value"); + out << y.guard(*pair.second); + } + } else { + push_xml z(out, "tag"); + out << y.guard(pair.first); + } + } + } + + if (post.xdata_ && ! post.xdata_->total.is_null()) { + push_xml y(out, "total"); + to_xml(out, post.xdata_->total); + } +} + } // namespace ledger @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup data - * - * @brief Brief - * - * Long. */ #ifndef _POST_H #define _POST_H @@ -53,11 +49,6 @@ namespace ledger { class xact_t; class account_t; -/** - * @brief Brief - * - * Long. - */ class post_t : public item_t { public: @@ -227,6 +218,8 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; +void to_xml(std::ostream& out, const post_t& post); + } // namespace ledger #endif // _POST_H diff --git a/src/precmd.cc b/src/precmd.cc index 4cf541f4..92483dc8 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -35,6 +35,7 @@ #include "xact.h" #include "post.h" #include "account.h" +#include "query.h" #include "session.h" #include "report.h" #include "format.h" @@ -147,7 +148,7 @@ value_t format_command(call_scope_t& args) out << std::endl << _("--- Formatted string ---") << std::endl; bind_scope_t bound_scope(args, *post); out << '"'; - fmt.format(out, bound_scope); + out << fmt(bound_scope); out << "\"\n"; return NULL_VALUE; @@ -223,37 +224,36 @@ value_t args_command(call_scope_t& args) report_t& report(find_scope<report_t>(args)); std::ostream& out(report.output_stream); - value_t::sequence_t::const_iterator begin = args.value().begin(); - value_t::sequence_t::const_iterator end = args.value().end(); - out << _("--- Input arguments ---") << std::endl; args.value().dump(out); out << std::endl << std::endl; - std::pair<expr_t, query_parser_t> info = args_to_predicate(begin, end); - if (! info.first) + query_t query(args.value(), report.what_to_keep()); + if (! query) throw_(std::runtime_error, _("Invalid query predicate: %1") << join_args(args)); call_scope_t sub_args(static_cast<scope_t&>(args)); - sub_args.push_back(string_value(info.first.text())); + sub_args.push_back(string_value(query.text())); parse_command(sub_args); - if (info.second.tokens_remaining()) { + if (query.tokens_remaining()) { out << std::endl << _("====== Display predicate ======") << std::endl << std::endl; - call_scope_t disp_sub_args(static_cast<scope_t&>(args)); - info = args_to_predicate(info.second); - if (! info.first) + query.parse_again(); + + if (! query) throw_(std::runtime_error, _("Invalid display predicate: %1") << join_args(args)); - disp_sub_args.push_back(string_value(info.first.text())); + call_scope_t disp_sub_args(static_cast<scope_t&>(args)); + disp_sub_args.push_back(string_value(query.text())); parse_command(disp_sub_args); } + return NULL_VALUE; } diff --git a/src/precmd.h b/src/precmd.h index b36d33e1..e0f81cf8 100644 --- a/src/precmd.h +++ b/src/precmd.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _PRECMD_H #define _PRECMD_H diff --git a/src/predicate.h b/src/predicate.h index 739356b6..17a2d36a 100644 --- a/src/predicate.h +++ b/src/predicate.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _PREDICATE_H #define _PREDICATE_H @@ -52,47 +48,40 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ -class item_predicate +class predicate_t : public expr_t { public: - expr_t predicate; keep_details_t what_to_keep; - item_predicate() { - TRACE_CTOR(item_predicate, ""); + predicate_t(const keep_details_t& _what_to_keep = keep_details_t()) + : what_to_keep(_what_to_keep) { + TRACE_CTOR(predicate_t, ""); } - item_predicate(const item_predicate& other) - : predicate(other.predicate), what_to_keep(other.what_to_keep) { - TRACE_CTOR(item_predicate, "copy"); + predicate_t(const predicate_t& other) + : expr_t(other), what_to_keep(other.what_to_keep) { + TRACE_CTOR(predicate_t, "copy"); } - 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&"); + + predicate_t(const string& str, const keep_details_t& _what_to_keep, + const parse_flags_t& flags = PARSE_DEFAULT) + : expr_t(str, flags), what_to_keep(_what_to_keep) { + TRACE_CTOR(predicate_t, "string, keep_details_t, parse_flags_t"); } - 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&"); + predicate_t(std::istream& in, const keep_details_t& _what_to_keep, + const parse_flags_t& flags = PARSE_DEFAULT) + : expr_t(in, flags), what_to_keep(_what_to_keep) { + TRACE_CTOR(predicate_t, "std::istream&, keep_details_t, parse_flags_t"); } - ~item_predicate() throw() { - TRACE_DTOR(item_predicate); + ~predicate_t() throw() { + TRACE_DTOR(predicate_t); } - bool operator()(scope_t& item) { - try { - return ! predicate || predicate.calc(item).strip_annotations(what_to_keep); - } - catch (const std::exception& err) { - add_error_context(_("While determining truth of predicate expression:")); - add_error_context(expr_context(predicate)); - throw; - } + virtual value_t real_calc(scope_t& scope) { + return (*this ? + expr_t::real_calc(scope) + .strip_annotations(what_to_keep) + .to_boolean() : + true); } #if defined(HAVE_BOOST_SERIALIZATION) @@ -103,207 +92,12 @@ private: template<class Archive> void serialize(Archive& ar, const unsigned int /* version */) { - ar & predicate; + ar & boost::serialization::base_object<expr_t>(*this); ar & what_to_keep; } #endif // HAVE_BOOST_SERIALIZATION }; -class query_lexer_t -{ - friend class query_parser_t; - - value_t::sequence_t::const_iterator begin; - value_t::sequence_t::const_iterator end; - - string::const_iterator arg_i; - string::const_iterator arg_end; - - bool consume_whitespace; - -public: - struct token_t - { - enum kind_t { - UNKNOWN, - - LPAREN, - RPAREN, - - TOK_NOT, - TOK_AND, - TOK_OR, - TOK_EQ, - - TOK_DATE, - TOK_CODE, - TOK_PAYEE, - TOK_NOTE, - TOK_ACCOUNT, - TOK_META, - TOK_EXPR, - - TERM, - - END_REACHED - - } kind; - - optional<string> value; - - explicit token_t(kind_t _kind = UNKNOWN, - const optional<string>& _value = none) - : kind(_kind), value(_value) { - TRACE_CTOR(query_lexer_t::token_t, ""); - } - token_t(const token_t& tok) - : kind(tok.kind), value(tok.value) { - TRACE_CTOR(query_lexer_t::token_t, "copy"); - } - ~token_t() throw() { - TRACE_DTOR(query_lexer_t::token_t); - } - - token_t& operator=(const token_t& tok) { - if (this != &tok) { - kind = tok.kind; - value = tok.value; - } - return *this; - } - - operator bool() const { - return kind != END_REACHED; - } - - string to_string() const { - switch (kind) { - case UNKNOWN: return "UNKNOWN"; - case LPAREN: return "LPAREN"; - case RPAREN: return "RPAREN"; - case TOK_NOT: return "TOK_NOT"; - case TOK_AND: return "TOK_AND"; - case TOK_OR: return "TOK_OR"; - case TOK_EQ: return "TOK_EQ"; - case TOK_DATE: return "TOK_DATE"; - case TOK_CODE: return "TOK_CODE"; - case TOK_PAYEE: return "TOK_PAYEE"; - case TOK_NOTE: return "TOK_NOTE"; - case TOK_ACCOUNT: return "TOK_ACCOUNT"; - case TOK_META: return "TOK_META"; - case TOK_EXPR: return "TOK_EXPR"; - case TERM: return string("TERM(") + *value + ")"; - case END_REACHED: return "END_REACHED"; - } - } - - string symbol() const { - switch (kind) { - case LPAREN: return "("; - case RPAREN: return ")"; - case TOK_NOT: return "not"; - case TOK_AND: return "and"; - case TOK_OR: return "or"; - case TOK_EQ: return "="; - case TOK_DATE: return "date"; - case TOK_CODE: return "code"; - case TOK_PAYEE: return "payee"; - case TOK_NOTE: return "note"; - case TOK_ACCOUNT: return "account"; - case TOK_META: return "meta"; - case TOK_EXPR: return "expr"; - - case END_REACHED: return "<EOF>"; - - case TERM: - assert(0); - return "<TERM>"; - - case UNKNOWN: - default: - assert(0); - return "<UNKNOWN>"; - } - } - - void unexpected(); - void expected(char wanted, char c = '\0'); - }; - - token_t token_cache; - - query_lexer_t(value_t::sequence_t::const_iterator _begin, - value_t::sequence_t::const_iterator _end) - : begin(_begin), end(_end), consume_whitespace(false) - { - TRACE_CTOR(query_lexer_t, ""); - assert(begin != end); - arg_i = (*begin).as_string().begin(); - arg_end = (*begin).as_string().end(); - } - query_lexer_t(const query_lexer_t& lexer) - : begin(lexer.begin), end(lexer.end), - arg_i(lexer.arg_i), arg_end(lexer.arg_end), - consume_whitespace(lexer.consume_whitespace), - token_cache(lexer.token_cache) - { - TRACE_CTOR(query_lexer_t, "copy"); - } - ~query_lexer_t() throw() { - TRACE_DTOR(query_lexer_t); - } - - token_t next_token(); - void push_token(token_t tok) { - assert(token_cache.kind == token_t::UNKNOWN); - token_cache = tok; - } - token_t peek_token() { - if (token_cache.kind == token_t::UNKNOWN) - token_cache = next_token(); - return token_cache; - } -}; - -class query_parser_t -{ - query_lexer_t lexer; - - expr_t::ptr_op_t parse_query_term(query_lexer_t::token_t::kind_t tok_context); - expr_t::ptr_op_t parse_unary_expr(query_lexer_t::token_t::kind_t tok_context); - expr_t::ptr_op_t parse_and_expr(query_lexer_t::token_t::kind_t tok_context); - expr_t::ptr_op_t parse_or_expr(query_lexer_t::token_t::kind_t tok_context); - expr_t::ptr_op_t parse_query_expr(query_lexer_t::token_t::kind_t tok_context); - -public: - query_parser_t(value_t::sequence_t::const_iterator begin, - value_t::sequence_t::const_iterator end) - : lexer(begin, end) { - TRACE_CTOR(query_parser_t, ""); - } - query_parser_t(const query_parser_t& parser) - : lexer(parser.lexer) { - TRACE_CTOR(query_parser_t, "copy"); - } - ~query_parser_t() throw() { - TRACE_DTOR(query_parser_t); - } - - expr_t::ptr_op_t parse(); - - bool tokens_remaining() { - query_lexer_t::token_t tok = lexer.peek_token(); - assert(tok.kind != query_lexer_t::token_t::UNKNOWN); - return tok.kind != query_lexer_t::token_t::END_REACHED; - } -}; - -std::pair<expr_t, query_parser_t> -args_to_predicate(value_t::sequence_t::const_iterator begin, - value_t::sequence_t::const_iterator end); - -std::pair<expr_t, query_parser_t> args_to_predicate(query_parser_t parser); - } // namespace ledger #endif // _PREDICATE_H diff --git a/src/pstream.h b/src/pstream.h index 43153e5c..00caf4e0 100644 --- a/src/pstream.h +++ b/src/pstream.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup util - * - * @brief Brief - * - * Full */ #ifndef _PSTREAM_H #define _PSTREAM_H diff --git a/src/py_amount.cc b/src/py_amount.cc index 937ad484..83f5dd29 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -285,11 +285,16 @@ internal precision.")) .def("valid", &amount_t::valid) ; - enum_< amount_t::parse_flags_enum_t >("AmountParse") - .value("DEFAULT", amount_t::PARSE_DEFAULT) - .value("NO_MIGRATE", amount_t::PARSE_NO_MIGRATE) - .value("NO_REDUCE", amount_t::PARSE_NO_REDUCE) - .value("SOFT_FAIL", amount_t::PARSE_SOFT_FAIL) + enum_< parse_flags_enum_t >("ParseFlags") + .value("Default", PARSE_DEFAULT) + .value("Partial", PARSE_PARTIAL) + .value("Single", PARSE_SINGLE) + .value("NoMigrate", PARSE_NO_MIGRATE) + .value("NoReduce", PARSE_NO_REDUCE) + .value("NoAssign", PARSE_NO_ASSIGN) + .value("NoDates", PARSE_NO_DATES) + .value("OpContext", PARSE_OP_CONTEXT) + .value("SoftFail", PARSE_SOFT_FAIL) ; register_optional_to_python<amount_t>(); diff --git a/src/py_xact.cc b/src/py_xact.cc index d886bf90..6feb6080 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -133,7 +133,7 @@ void export_xact() ; class_< auto_xact_t, bases<xact_base_t> > ("AutomatedTransaction") - .def(init<item_predicate>()) + .def(init<predicate_t>()) .add_property("predicate", make_getter(&auto_xact_t::predicate), diff --git a/src/predicate.cc b/src/query.cc index 46d973c4..98a3de1d 100644 --- a/src/predicate.cc +++ b/src/query.cc @@ -31,12 +31,12 @@ #include <system.hh> -#include "predicate.h" +#include "query.h" #include "op.h" namespace ledger { -query_lexer_t::token_t query_lexer_t::next_token() +query_t::lexer_t::token_t query_t::lexer_t::next_token() { if (token_cache.kind != token_t::UNKNOWN) { token_t tok = token_cache; @@ -191,7 +191,7 @@ query_lexer_t::token_t query_lexer_t::next_token() return token_t(token_t::UNKNOWN); } -void query_lexer_t::token_t::unexpected() +void query_t::lexer_t::token_t::unexpected() { kind_t prev_kind = kind; @@ -207,7 +207,7 @@ void query_lexer_t::token_t::unexpected() } } -void query_lexer_t::token_t::expected(char wanted, char c) +void query_t::lexer_t::token_t::expected(char wanted, char c) { kind = UNKNOWN; @@ -225,32 +225,32 @@ void query_lexer_t::token_t::expected(char wanted, char c) } expr_t::ptr_op_t -query_parser_t::parse_query_term(query_lexer_t::token_t::kind_t tok_context) +query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_context) { expr_t::ptr_op_t node; - query_lexer_t::token_t tok = lexer.next_token(); + lexer_t::token_t tok = lexer.next_token(); switch (tok.kind) { - case query_lexer_t::token_t::END_REACHED: + case lexer_t::token_t::END_REACHED: break; - case query_lexer_t::token_t::TOK_DATE: - case query_lexer_t::token_t::TOK_CODE: - case query_lexer_t::token_t::TOK_PAYEE: - case query_lexer_t::token_t::TOK_NOTE: - case query_lexer_t::token_t::TOK_ACCOUNT: - case query_lexer_t::token_t::TOK_META: - case query_lexer_t::token_t::TOK_EXPR: + case lexer_t::token_t::TOK_DATE: + case lexer_t::token_t::TOK_CODE: + case lexer_t::token_t::TOK_PAYEE: + case lexer_t::token_t::TOK_NOTE: + case lexer_t::token_t::TOK_ACCOUNT: + case lexer_t::token_t::TOK_META: + case lexer_t::token_t::TOK_EXPR: node = parse_query_term(tok.kind); if (! node) throw_(parse_error, _("%1 operator not followed by argument") << tok.symbol()); break; - case query_lexer_t::token_t::TERM: + case lexer_t::token_t::TERM: assert(tok.value); switch (tok_context) { - case query_lexer_t::token_t::TOK_DATE: { + case lexer_t::token_t::TOK_DATE: { expr_t::ptr_op_t ident = new expr_t::op_t(expr_t::op_t::IDENT); ident->set_ident("date"); @@ -285,11 +285,11 @@ query_parser_t::parse_query_term(query_lexer_t::token_t::kind_t tok_context) break; } - case query_lexer_t::token_t::TOK_EXPR: + case lexer_t::token_t::TOK_EXPR: node = expr_t(*tok.value).get_op(); break; - case query_lexer_t::token_t::TOK_META: { + case lexer_t::token_t::TOK_META: { node = new expr_t::op_t(expr_t::op_t::O_CALL); expr_t::ptr_op_t ident = new expr_t::op_t(expr_t::op_t::IDENT); @@ -300,10 +300,10 @@ query_parser_t::parse_query_term(query_lexer_t::token_t::kind_t tok_context) arg1->set_value(mask_t(*tok.value)); tok = lexer.peek_token(); - if (tok.kind == query_lexer_t::token_t::TOK_EQ) { + if (tok.kind == lexer_t::token_t::TOK_EQ) { tok = lexer.next_token(); tok = lexer.next_token(); - if (tok.kind != query_lexer_t::token_t::TERM) + if (tok.kind != lexer_t::token_t::TERM) throw_(parse_error, _("Metadata equality operator not followed by term")); @@ -327,13 +327,13 @@ query_parser_t::parse_query_term(query_lexer_t::token_t::kind_t tok_context) expr_t::ptr_op_t ident = new expr_t::op_t(expr_t::op_t::IDENT); switch (tok_context) { - case query_lexer_t::token_t::TOK_ACCOUNT: + case lexer_t::token_t::TOK_ACCOUNT: ident->set_ident("account"); break; - case query_lexer_t::token_t::TOK_PAYEE: + case lexer_t::token_t::TOK_PAYEE: ident->set_ident("payee"); break; - case query_lexer_t::token_t::TOK_CODE: + case lexer_t::token_t::TOK_CODE: ident->set_ident("code"); break; - case query_lexer_t::token_t::TOK_NOTE: + case lexer_t::token_t::TOK_NOTE: ident->set_ident("note"); break; default: assert(0); break; @@ -348,10 +348,10 @@ query_parser_t::parse_query_term(query_lexer_t::token_t::kind_t tok_context) } break; - case query_lexer_t::token_t::LPAREN: + case lexer_t::token_t::LPAREN: node = parse_query_expr(tok_context); tok = lexer.next_token(); - if (tok.kind != query_lexer_t::token_t::RPAREN) + if (tok.kind != lexer_t::token_t::RPAREN) tok.expected(')'); break; @@ -364,13 +364,13 @@ query_parser_t::parse_query_term(query_lexer_t::token_t::kind_t tok_context) } expr_t::ptr_op_t -query_parser_t::parse_unary_expr(query_lexer_t::token_t::kind_t tok_context) +query_t::parser_t::parse_unary_expr(lexer_t::token_t::kind_t tok_context) { expr_t::ptr_op_t node; - query_lexer_t::token_t tok = lexer.next_token(); + lexer_t::token_t tok = lexer.next_token(); switch (tok.kind) { - case query_lexer_t::token_t::TOK_NOT: { + case lexer_t::token_t::TOK_NOT: { expr_t::ptr_op_t term(parse_query_term(tok_context)); if (! term) throw_(parse_error, @@ -391,12 +391,12 @@ query_parser_t::parse_unary_expr(query_lexer_t::token_t::kind_t tok_context) } expr_t::ptr_op_t -query_parser_t::parse_and_expr(query_lexer_t::token_t::kind_t tok_context) +query_t::parser_t::parse_and_expr(lexer_t::token_t::kind_t tok_context) { if (expr_t::ptr_op_t node = parse_unary_expr(tok_context)) { while (true) { - query_lexer_t::token_t tok = lexer.next_token(); - if (tok.kind == query_lexer_t::token_t::TOK_AND) { + lexer_t::token_t tok = lexer.next_token(); + if (tok.kind == lexer_t::token_t::TOK_AND) { expr_t::ptr_op_t prev(node); node = new expr_t::op_t(expr_t::op_t::O_AND); node->set_left(prev); @@ -415,12 +415,12 @@ query_parser_t::parse_and_expr(query_lexer_t::token_t::kind_t tok_context) } expr_t::ptr_op_t -query_parser_t::parse_or_expr(query_lexer_t::token_t::kind_t tok_context) +query_t::parser_t::parse_or_expr(lexer_t::token_t::kind_t tok_context) { if (expr_t::ptr_op_t node = parse_and_expr(tok_context)) { while (true) { - query_lexer_t::token_t tok = lexer.next_token(); - if (tok.kind == query_lexer_t::token_t::TOK_OR) { + lexer_t::token_t tok = lexer.next_token(); + if (tok.kind == lexer_t::token_t::TOK_OR) { expr_t::ptr_op_t prev(node); node = new expr_t::op_t(expr_t::op_t::O_OR); node->set_left(prev); @@ -439,7 +439,7 @@ query_parser_t::parse_or_expr(query_lexer_t::token_t::kind_t tok_context) } expr_t::ptr_op_t -query_parser_t::parse_query_expr(query_lexer_t::token_t::kind_t tok_context) +query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context) { if (expr_t::ptr_op_t node = parse_or_expr(tok_context)) { if (expr_t::ptr_op_t next = parse_query_expr(tok_context)) { @@ -453,24 +453,4 @@ query_parser_t::parse_query_expr(query_lexer_t::token_t::kind_t tok_context) return expr_t::ptr_op_t(); } -expr_t::ptr_op_t query_parser_t::parse() -{ - return parse_query_expr(query_lexer_t::token_t::TOK_ACCOUNT); -} - -std::pair<expr_t, query_parser_t> -args_to_predicate(value_t::sequence_t::const_iterator begin, - value_t::sequence_t::const_iterator end) -{ - query_parser_t parser(begin, end); - expr_t expr(parser.parse()); - return std::pair<expr_t, query_parser_t>(expr, parser); -} - -std::pair<expr_t, query_parser_t> args_to_predicate(query_parser_t parser) -{ - expr_t expr(parser.parse()); - return std::pair<expr_t, query_parser_t>(expr, parser); -} - } // namespace ledger diff --git a/src/query.h b/src/query.h new file mode 100644 index 00000000..e64588ad --- /dev/null +++ b/src/query.h @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup expr + */ + +/** + * @file predicate.h + * @author John Wiegley + * + * @ingroup expr + */ +#ifndef _QUERY_H +#define _QUERY_H + +#include "predicate.h" + +namespace ledger { + +class query_t : public predicate_t +{ +public: + class lexer_t + { + friend class query_t; + friend class parser_t; + + value_t::sequence_t::const_iterator begin; + value_t::sequence_t::const_iterator end; + + string::const_iterator arg_i; + string::const_iterator arg_end; + + bool consume_whitespace; + + public: + struct token_t + { + enum kind_t { + UNKNOWN, + + LPAREN, + RPAREN, + + TOK_NOT, + TOK_AND, + TOK_OR, + TOK_EQ, + + TOK_DATE, + TOK_CODE, + TOK_PAYEE, + TOK_NOTE, + TOK_ACCOUNT, + TOK_META, + TOK_EXPR, + + TERM, + + END_REACHED + + } kind; + + optional<string> value; + + explicit token_t(kind_t _kind = UNKNOWN, + const optional<string>& _value = none) + : kind(_kind), value(_value) { + TRACE_CTOR(lexer_t::token_t, ""); + } + token_t(const token_t& tok) + : kind(tok.kind), value(tok.value) { + TRACE_CTOR(lexer_t::token_t, "copy"); + } + ~token_t() throw() { + TRACE_DTOR(lexer_t::token_t); + } + + token_t& operator=(const token_t& tok) { + if (this != &tok) { + kind = tok.kind; + value = tok.value; + } + return *this; + } + + operator bool() const { + return kind != END_REACHED; + } + + string to_string() const { + switch (kind) { + case UNKNOWN: return "UNKNOWN"; + case LPAREN: return "LPAREN"; + case RPAREN: return "RPAREN"; + case TOK_NOT: return "TOK_NOT"; + case TOK_AND: return "TOK_AND"; + case TOK_OR: return "TOK_OR"; + case TOK_EQ: return "TOK_EQ"; + case TOK_DATE: return "TOK_DATE"; + case TOK_CODE: return "TOK_CODE"; + case TOK_PAYEE: return "TOK_PAYEE"; + case TOK_NOTE: return "TOK_NOTE"; + case TOK_ACCOUNT: return "TOK_ACCOUNT"; + case TOK_META: return "TOK_META"; + case TOK_EXPR: return "TOK_EXPR"; + case TERM: return string("TERM(") + *value + ")"; + case END_REACHED: return "END_REACHED"; + } + } + + string symbol() const { + switch (kind) { + case LPAREN: return "("; + case RPAREN: return ")"; + case TOK_NOT: return "not"; + case TOK_AND: return "and"; + case TOK_OR: return "or"; + case TOK_EQ: return "="; + case TOK_DATE: return "date"; + case TOK_CODE: return "code"; + case TOK_PAYEE: return "payee"; + case TOK_NOTE: return "note"; + case TOK_ACCOUNT: return "account"; + case TOK_META: return "meta"; + case TOK_EXPR: return "expr"; + + case END_REACHED: return "<EOF>"; + + case TERM: + assert(0); + return "<TERM>"; + + case UNKNOWN: + default: + assert(0); + return "<UNKNOWN>"; + } + } + + void unexpected(); + void expected(char wanted, char c = '\0'); + }; + + token_t token_cache; + + lexer_t(value_t::sequence_t::const_iterator _begin, + value_t::sequence_t::const_iterator _end) + : begin(_begin), end(_end), consume_whitespace(false) + { + TRACE_CTOR(lexer_t, ""); + assert(begin != end); + arg_i = (*begin).as_string().begin(); + arg_end = (*begin).as_string().end(); + } + lexer_t(const lexer_t& lexer) + : begin(lexer.begin), end(lexer.end), + arg_i(lexer.arg_i), arg_end(lexer.arg_end), + consume_whitespace(lexer.consume_whitespace), + token_cache(lexer.token_cache) + { + TRACE_CTOR(lexer_t, "copy"); + } + ~lexer_t() throw() { + TRACE_DTOR(lexer_t); + } + + token_t next_token(); + void push_token(token_t tok) { + assert(token_cache.kind == token_t::UNKNOWN); + token_cache = tok; + } + token_t peek_token() { + if (token_cache.kind == token_t::UNKNOWN) + token_cache = next_token(); + return token_cache; + } + }; + +protected: + class parser_t + { + friend class query_t; + + value_t args; + lexer_t lexer; + + expr_t::ptr_op_t parse_query_term(lexer_t::token_t::kind_t tok_context); + expr_t::ptr_op_t parse_unary_expr(lexer_t::token_t::kind_t tok_context); + expr_t::ptr_op_t parse_and_expr(lexer_t::token_t::kind_t tok_context); + expr_t::ptr_op_t parse_or_expr(lexer_t::token_t::kind_t tok_context); + expr_t::ptr_op_t parse_query_expr(lexer_t::token_t::kind_t tok_context); + + public: + parser_t(const value_t& _args) + : args(_args), lexer(args.begin(), args.end()) { + TRACE_CTOR(parser_t, ""); + } + parser_t(const parser_t& parser) + : args(parser.args), lexer(parser.lexer) { + TRACE_CTOR(parser_t, "copy"); + } + ~parser_t() throw() { + TRACE_DTOR(parser_t); + } + + expr_t::ptr_op_t parse() { + return parse_query_expr(lexer_t::token_t::TOK_ACCOUNT); + } + + bool tokens_remaining() { + lexer_t::token_t tok = lexer.peek_token(); + assert(tok.kind != lexer_t::token_t::UNKNOWN); + return tok.kind != lexer_t::token_t::END_REACHED; + } + }; + + optional<parser_t> parser; + +public: + query_t() { + TRACE_CTOR(query_t, ""); + } + query_t(const query_t& other) + : predicate_t(other) { + TRACE_CTOR(query_t, "copy"); + } + + query_t(const value_t& args, + const keep_details_t& _what_to_keep = keep_details_t()) + : predicate_t(_what_to_keep) { + TRACE_CTOR(query_t, "string, keep_details_t"); + if (! args.empty()) + parse_args(args); + } + ~query_t() throw() { + TRACE_DTOR(query_t); + } + + void parse_args(const value_t& args) { + if (! parser) + parser = parser_t(args); + ptr = parser->parse(); // expr_t::ptr + } + + void parse_again() { + assert(parser); + ptr = parser->parse(); // expr_t::ptr + } + + bool tokens_remaining() { + return parser && parser->tokens_remaining(); + } + + virtual string text() { + return print_to_str(); + } +}; + +} // namespace ledger + +#endif // _QUERY_H diff --git a/src/quotes.h b/src/quotes.h index 5db69d1f..d00c5bfd 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup extra - * - * @brief Brief - * - * Long. */ #ifndef _QUOTES_H #define _QUOTES_H diff --git a/src/report.cc b/src/report.cc index fbe8d37c..24f0c054 100644 --- a/src/report.cc +++ b/src/report.cc @@ -35,13 +35,15 @@ #include "session.h" #include "unistring.h" #include "format.h" +#include "query.h" #include "output.h" #include "iterators.h" #include "filters.h" #include "precmd.h" #include "stats.h" #include "generate.h" -#include "derive.h" +#include "draft.h" +#include "xml.h" #include "emacs.h" namespace ledger { @@ -96,7 +98,7 @@ void report_t::accounts_report(acct_handler_ptr handler) if (HANDLED(display_)) pass_down_accounts(handler, *iter.get(), - item_predicate(HANDLER(display_).str(), what_to_keep()), + predicate_t(HANDLER(display_).str(), what_to_keep()), *this); else pass_down_accounts(handler, *iter.get()); @@ -431,33 +433,23 @@ namespace { value_t operator()(call_scope_t& args) { if (args.size() > 0) { - value_t::sequence_t::const_iterator begin = - args.value().as_sequence().begin(); - value_t::sequence_t::const_iterator end = - args.value().as_sequence().end(); - - std::pair<expr_t, query_parser_t> info = args_to_predicate(begin, end); - if (! info.first) + query_t query(args.value(), report.what_to_keep()); + if (! query) throw_(std::runtime_error, - _("Invalid query predicate: %1") << join_args(args)); + _("Invalid query predicate: %1") << query.text()); - string limit = info.first.text(); - if (! limit.empty()) - report.HANDLER(limit_).on(whence, limit); + report.HANDLER(limit_).on(whence, query.text()); DEBUG("report.predicate", "Predicate = " << report.HANDLER(limit_).str()); - if (info.second.tokens_remaining()) { - info = args_to_predicate(info.second); - if (! info.first) + if (query.tokens_remaining()) { + query.parse_again(); + if (! query) throw_(std::runtime_error, - _("Invalid display predicate: %1") << join_args(args)); - - string display = info.first.text(); + _("Invalid display predicate: %1") << query.text()); - if (! display.empty()) - report.HANDLER(display_).on(whence, display); + report.HANDLER(display_).on(whence, query.text()); DEBUG("report.predicate", "Display predicate = " << report.HANDLER(display_).str()); @@ -990,6 +982,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'x': if (is_eq(p, "xact")) return WRAP_FUNCTOR(xact_command); + else if (is_eq(p, "xml")) + return WRAP_FUNCTOR(reporter<>(new format_xml(*this), *this, "#xml")); break; } break; diff --git a/src/report.h b/src/report.h index 08582075..515dbd26 100644 --- a/src/report.h +++ b/src/report.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _REPORT_H #define _REPORT_H @@ -104,11 +100,6 @@ class xact_t; // says that the formatter should be "flushed" after the entities are // iterated. This does not happen for the commodities iteration, however. -/** - * @brief Brief - * - * Long. - */ class report_t : public scope_t { report_t(); diff --git a/src/scope.h b/src/scope.h index c2c9df20..44ca3229 100644 --- a/src/scope.h +++ b/src/scope.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _SCOPE_H #define _SCOPE_H @@ -50,11 +46,6 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ struct symbol_t { enum kind_t { @@ -63,7 +54,8 @@ struct symbol_t OPTION, PRECOMMAND, COMMAND, - DIRECTIVE + DIRECTIVE, + FORMAT }; kind_t kind; @@ -105,11 +97,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class scope_t { public: @@ -138,11 +125,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class child_scope_t : public noncopyable, public scope_t { public: @@ -186,11 +168,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class symbol_scope_t : public child_scope_t { typedef std::map<symbol_t, expr_t::ptr_op_t> symbol_map; @@ -228,11 +205,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class call_scope_t : public child_scope_t { value_t args; @@ -301,11 +273,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class bind_scope_t : public child_scope_t { bind_scope_t(); @@ -349,11 +316,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ template <typename T> T * search_scope(scope_t * ptr) { diff --git a/src/session.h b/src/session.h index 579f87a8..bde37f46 100644 --- a/src/session.h +++ b/src/session.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _SESSION_H #define _SESSION_H @@ -57,11 +53,6 @@ namespace ledger { class commodity_pool_t; class xact_t; -/** - * @brief Brief - * - * Long. - */ class session_t : public symbol_scope_t { friend void set_session_context(session_t * session); diff --git a/src/stats.cc b/src/stats.cc index e2db9d8b..b89a5949 100644 --- a/src/stats.cc +++ b/src/stats.cc @@ -31,7 +31,7 @@ #include <system.hh> -#include "derive.h" +#include "draft.h" #include "xact.h" #include "post.h" #include "account.h" diff --git a/src/stats.h b/src/stats.h index db8e9a0d..d2d10de6 100644 --- a/src/stats.h +++ b/src/stats.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _STATS_H #define _STATS_H diff --git a/src/temps.h b/src/temps.h index 34494ba8..4243079c 100644 --- a/src/temps.h +++ b/src/temps.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup report - * - * @brief Brief - * - * Long. */ #ifndef _TEMPS_H #define _TEMPS_H diff --git a/src/textual.cc b/src/textual.cc index 8d4294a6..37c38e55 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -144,13 +144,13 @@ namespace { const string& name); }; - void parse_amount_expr(scope_t& scope, - std::istream& in, - amount_t& amount, - post_t * post, - uint_least8_t flags = 0) + void parse_amount_expr(scope_t& scope, + std::istream& in, + amount_t& amount, + post_t * post, + const parse_flags_t& flags = PARSE_DEFAULT) { - expr_t expr(in, flags | static_cast<uint_least8_t>(expr_t::PARSE_PARTIAL)); + expr_t expr(in, flags.plus_flags(PARSE_PARTIAL)); DEBUG("textual.parse", "Parsed an amount expression"); @@ -506,8 +506,8 @@ void instance_t::automated_xact_directive(char * line) } std::auto_ptr<auto_xact_t> ae - (new auto_xact_t(item_predicate(skip_ws(line + 1), - keep_details_t(true, true, true)))); + (new auto_xact_t(predicate_t(skip_ws(line + 1), + keep_details_t(true, true, true)))); reveal_context = false; @@ -852,12 +852,10 @@ post_t * instance_t::parse_post(char * line, ptristream stream(next, len - beg); if (*next != '(') // indicates a value expression - post->amount.parse(stream, amount_t::PARSE_NO_REDUCE); + post->amount.parse(stream, PARSE_NO_REDUCE); else parse_amount_expr(scope, stream, post->amount, post.get(), - static_cast<uint_least8_t>(expr_t::PARSE_NO_REDUCE) | - static_cast<uint_least8_t>(expr_t::PARSE_SINGLE) | - static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN)); + PARSE_NO_REDUCE | PARSE_SINGLE | PARSE_NO_ASSIGN); if (! post->amount.is_null() && honor_strict && strict && post->amount.has_commodity() && @@ -900,12 +898,11 @@ post_t * instance_t::parse_post(char * line, ptristream cstream(p, len - beg); if (*p != '(') // indicates a value expression - post->cost->parse(cstream, amount_t::PARSE_NO_MIGRATE); + post->cost->parse(cstream, PARSE_NO_MIGRATE); else parse_amount_expr(scope, cstream, *post->cost, post.get(), - static_cast<uint_least8_t>(expr_t::PARSE_NO_MIGRATE) | - static_cast<uint_least8_t>(expr_t::PARSE_SINGLE) | - static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN)); + PARSE_NO_MIGRATE | PARSE_SINGLE | + PARSE_NO_ASSIGN); if (post->cost->sign() < 0) throw parse_error(_("A posting's cost may not be negative")); @@ -953,11 +950,10 @@ post_t * instance_t::parse_post(char * line, ptristream stream(p, len - beg); if (*p != '(') // indicates a value expression - post->assigned_amount->parse(stream, amount_t::PARSE_NO_MIGRATE); + post->assigned_amount->parse(stream, PARSE_NO_MIGRATE); else parse_amount_expr(scope, stream, *post->assigned_amount, post.get(), - static_cast<uint_least8_t>(expr_t::PARSE_SINGLE) | - static_cast<uint_least8_t>(expr_t::PARSE_NO_MIGRATE)); + PARSE_SINGLE | PARSE_NO_MIGRATE); if (post->assigned_amount->is_null()) { if (post->amount.is_null()) diff --git a/src/timelog.h b/src/timelog.h index 85ea56c6..7d79af3e 100644 --- a/src/timelog.h +++ b/src/timelog.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup data - * - * @brief Brief - * - * Long. */ #ifndef _TIMELOG_H #define _TIMELOG_H @@ -54,11 +50,6 @@ namespace ledger { class account_t; class journal_t; -/** - * @brief Brief - * - * Long. - */ class time_xact_t { public: @@ -85,11 +76,6 @@ public: } }; -/** - * @brief Brief - * - * Long. - */ class time_log_t { std::list<time_xact_t> time_xacts; diff --git a/src/times.h b/src/times.h index 9387320e..3cd359d2 100644 --- a/src/times.h +++ b/src/times.h @@ -116,6 +116,28 @@ std::string format_date(const date_t& when, void set_date_format(const char * format); void set_input_date_format(const char * format); +inline void to_xml(std::ostream& out, const datetime_t& when, + bool wrap = true) +{ + if (wrap) { + push_xml x(out, "datetime"); + out << format_datetime(when, FMT_WRITTEN); + } else { + out << format_datetime(when, FMT_WRITTEN); + } +} + +inline void to_xml(std::ostream& out, const date_t& when, + bool wrap = true) +{ + if (wrap) { + push_xml x(out, "date"); + out << format_date(when, FMT_WRITTEN); + } else { + out << format_date(when, FMT_WRITTEN); + } +} + class date_interval_t : public equality_comparable<date_interval_t> { public: diff --git a/src/token.cc b/src/token.cc index fde6a0e3..3df072a7 100644 --- a/src/token.cc +++ b/src/token.cc @@ -138,7 +138,7 @@ void expr_t::token_t::parse_ident(std::istream& in) value.set_string(buf); } -void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags) +void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags) { if (in.eof()) { kind = TOK_EOF; @@ -232,7 +232,7 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags) case '{': { in.get(c); amount_t temp; - temp.parse(in, amount_t::PARSE_NO_MIGRATE); + temp.parse(in, PARSE_NO_MIGRATE); in.get(c); if (c != '}') expected('}', c); @@ -298,7 +298,7 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags) case '/': { in.get(c); - if (pflags & PARSE_OP_CONTEXT) { // operator context + if (pflags.has_flags(PARSE_OP_CONTEXT)) { // operator context kind = SLASH; } else { // terminal context // Read in the regexp @@ -399,17 +399,16 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags) // When in relaxed parsing mode, we want to migrate commodity flags // so that any precision specified by the user updates the current // maximum displayed precision. - amount_t::parse_flags_t parse_flags; - parser_t::parse_flags_t pflags_copy(pflags); + parse_flags_t parse_flags; - if (pflags_copy.has_flags(PARSE_NO_MIGRATE)) - parse_flags.add_flags(amount_t::PARSE_NO_MIGRATE); - if (pflags_copy.has_flags(PARSE_NO_REDUCE)) - parse_flags.add_flags(amount_t::PARSE_NO_REDUCE); + if (pflags.has_flags(PARSE_NO_MIGRATE)) + parse_flags.add_flags(PARSE_NO_MIGRATE); + if (pflags.has_flags(PARSE_NO_REDUCE)) + parse_flags.add_flags(PARSE_NO_REDUCE); try { amount_t temp; - if (! temp.parse(in, parse_flags.plus_flags(amount_t::PARSE_SOFT_FAIL))) { + if (! temp.parse(in, parse_flags.plus_flags(PARSE_SOFT_FAIL))) { // If the amount had no commodity, it must be an unambiguous // variable reference diff --git a/src/token.h b/src/token.h index 50d2bf72..670f16e3 100644 --- a/src/token.h +++ b/src/token.h @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup expr - * - * @brief Brief - * - * Long. */ #ifndef _TOKEN_H #define _TOKEN_H @@ -50,11 +46,6 @@ namespace ledger { -/** - * @brief Brief - * - * Long. - */ struct expr_t::token_t : public noncopyable { enum kind_t { @@ -133,7 +124,7 @@ struct expr_t::token_t : public noncopyable int parse_reserved_word(std::istream& in); void parse_ident(std::istream& in); - void next(std::istream& in, const uint_least8_t flags); + void next(std::istream& in, const parse_flags_t& flags); void rewind(std::istream& in); void unexpected(); void expected(char wanted, char c = '\0'); diff --git a/src/unistring.h b/src/unistring.h index bc55b016..7c433d9d 100644 --- a/src/unistring.h +++ b/src/unistring.h @@ -38,15 +38,10 @@ * @author John Wiegley * * @ingroup utils - * - * @brief Brief - * - * Long. */ #ifndef _UNISTRING_H #define _UNISTRING_H - namespace ledger { /** diff --git a/src/utils.h b/src/utils.h index a8784a66..bfdee0b2 100644 --- a/src/utils.h +++ b/src/utils.h @@ -165,8 +165,6 @@ void report_memory(std::ostream& out, bool report_all = false); #if defined(STRING_VERIFY_ON) /** - * @brief Brief - * * This string type is a wrapper around std::string that allows us to * trace constructor and destructor calls. */ @@ -648,6 +646,51 @@ inline string to_hex(uint_least32_t * message_digest, const int len = 1) return buf.str(); } +class push_xml +{ + std::ostream& out; + string tag; + bool leave_open; + +public: + push_xml(std::ostream& _out, const string& _tag, bool has_attrs = false, + bool _leave_open = false) + : out(_out), tag(_tag), leave_open(_leave_open) { + out << '<' << tag; + if (! has_attrs) + out << '>'; + } + ~push_xml() { + if (! leave_open) + out << "</" << tag << '>'; + } + + void close_attrs() { + out << '>'; + } + + static string guard(const string& str) { + std::ostringstream buf; + foreach (const char& ch, str) { + switch (ch) { + case '<': + buf << "<"; + break; + case '>': + buf << ">"; + break; + case '&': + buf << "&"; + break; + default: + buf << ch; + break; + } + } + return buf.str(); + } +}; + extern const string version; } // namespace ledger diff --git a/src/value.cc b/src/value.cc index e2c9dc8b..b4060a1c 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1746,4 +1746,57 @@ bool sort_value_is_less_than(const std::list<sort_value_t>& left_values, return false; } +void to_xml(std::ostream& out, const value_t& value) +{ + switch (value.type()) { + case value_t::VOID: + out << "<void />"; + break; + case value_t::BOOLEAN: { + push_xml y(out, "boolean"); + out << (value.as_boolean() ? "true" : "false"); + break; + } + case value_t::INTEGER: { + push_xml y(out, "integer"); + out << value.as_long(); + break; + } + + case value_t::AMOUNT: + to_xml(out, value.as_amount()); + break; + case value_t::BALANCE: + to_xml(out, value.as_balance()); + break; + + case value_t::DATETIME: + to_xml(out, value.as_datetime()); + break; + case value_t::DATE: + to_xml(out, value.as_date()); + break; + case value_t::STRING: { + push_xml y(out, "string"); + out << y.guard(value.as_string()); + break; + } + case value_t::MASK: + to_xml(out, value.as_mask()); + break; + + case value_t::SEQUENCE: { + push_xml y(out, "sequence"); + foreach (const value_t& member, value.as_sequence()) + to_xml(out, member); + break; + } + + case value_t::SCOPE: + default: + assert(false); + break; + } +} + } // namespace ledger diff --git a/src/value.h b/src/value.h index bd681b07..94002bef 100644 --- a/src/value.h +++ b/src/value.h @@ -870,6 +870,10 @@ public: return 1; } + bool empty() const { + return size() == 0; + } + /** * Informational methods. */ @@ -978,6 +982,8 @@ struct sort_value_t bool sort_value_is_less_than(const std::list<sort_value_t>& left_values, const std::list<sort_value_t>& right_values); +void to_xml(std::ostream& out, const value_t& value); + } // namespace ledger #endif // _VALUE_H diff --git a/src/xact.cc b/src/xact.cc index 68b607f4..f4331a29 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -560,4 +560,63 @@ void extend_xact_base(journal_t * journal, xact->extend_xact(base, post_handler); } +void to_xml(std::ostream& out, const xact_t& xact) +{ + push_xml x(out, "transaction", true, true); + + if (xact.state() == item_t::CLEARED) + out << " state=\"cleared\""; + else if (xact.state() == item_t::PENDING) + out << " state=\"pending\""; + + if (xact.has_flags(ITEM_GENERATED)) + out << " generated=\"true\""; + + x.close_attrs(); + + if (xact._date) { + push_xml y(out, "date"); + to_xml(out, *xact._date, false); + } + if (xact._date_eff) { + push_xml y(out, "effective-date"); + to_xml(out, *xact._date_eff, false); + } + + if (xact.code) { + push_xml y(out, "code"); + out << y.guard(*xact.code); + } + + { + push_xml y(out, "payee"); + out << y.guard(xact.payee); + } + + if (xact.note) { + push_xml y(out, "note"); + out << y.guard(*xact.note); + } + + if (xact.metadata) { + push_xml y(out, "metadata"); + foreach (const item_t::string_map::value_type& pair, *xact.metadata) { + if (pair.second) { + push_xml z(out, "variable"); + { + push_xml w(out, "key"); + out << y.guard(pair.first); + } + { + push_xml w(out, "value"); + out << y.guard(*pair.second); + } + } else { + push_xml z(out, "tag"); + out << y.guard(pair.first); + } + } + } +} + } // namespace ledger @@ -38,10 +38,6 @@ * @author John Wiegley * * @ingroup data - * - * @brief Brief - * - * Long. */ #ifndef _XACT_H #define _XACT_H @@ -56,11 +52,6 @@ class journal_t; typedef std::list<post_t *> posts_list; -/** - * @brief Brief - * - * Long. - */ class xact_base_t : public item_t { public: @@ -109,11 +100,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class xact_t : public xact_base_t { public: @@ -154,25 +140,15 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ struct xact_finalizer_t { virtual ~xact_finalizer_t() {} virtual bool operator()(xact_t& xact, bool post) = 0; }; -/** - * @brief Brief - * - * Long. - */ class auto_xact_t : public xact_base_t { public: - item_predicate predicate; + predicate_t predicate; auto_xact_t() { TRACE_CTOR(auto_xact_t, ""); @@ -181,10 +157,10 @@ public: : xact_base_t(), predicate(other.predicate) { TRACE_CTOR(auto_xact_t, "copy"); } - auto_xact_t(const item_predicate& _predicate) + auto_xact_t(const predicate_t& _predicate) : predicate(_predicate) { - TRACE_CTOR(auto_xact_t, "const item_predicate<post_t>&"); + TRACE_CTOR(auto_xact_t, "const predicate_t&"); } virtual ~auto_xact_t() { @@ -207,11 +183,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ struct auto_xact_finalizer_t : public xact_finalizer_t { journal_t * journal; @@ -245,11 +216,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class period_xact_t : public xact_base_t { public: @@ -287,11 +253,6 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -/** - * @brief Brief - * - * Long. - */ class func_finalizer_t : public xact_finalizer_t { func_finalizer_t(); @@ -328,6 +289,8 @@ typedef std::list<xact_t *> xacts_list; typedef std::list<auto_xact_t *> auto_xacts_list; typedef std::list<period_xact_t *> period_xacts_list; +void to_xml(std::ostream& out, const xact_t& xact); + } // namespace ledger #endif // _XACT_H diff --git a/src/xml.cc b/src/xml.cc new file mode 100644 index 00000000..9cff980a --- /dev/null +++ b/src/xml.cc @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <system.hh> + +#include "xml.h" +#include "xact.h" +#include "post.h" +#include "account.h" +#include "session.h" +#include "report.h" + +namespace ledger { + +namespace { + void xml_account(std::ostream& out, const account_t * acct) { + if ((acct->has_xdata() && + acct->xdata().has_flags(ACCOUNT_EXT_VISITED)) || + acct->children_with_flags(ACCOUNT_EXT_VISITED)) { + out << "<account id=\""; + out.width(sizeof(unsigned long) * 2); + out.fill('0'); + out << std::hex << reinterpret_cast<unsigned long>(acct); + out << "\">\n"; + + out << "<name>" << acct->name << "</name>\n"; + value_t total = acct->amount(); + if (! total.is_null()) { + out << "<amount>\n"; + to_xml(out, total); + out << "</amount>\n"; + } + total = acct->total(); + if (! total.is_null()) { + out << "<total>\n"; + to_xml(out, total); + out << "</total>\n"; + } + out << "</account>\n"; + } + + foreach (const accounts_map::value_type& pair, acct->accounts) + xml_account(out, pair.second); + } + + void xml_transaction(std::ostream& out, const xact_t * xact) { + to_xml(out, *xact); + + foreach (const post_t * post, xact->posts) + if (post->has_xdata() && + post->xdata().has_flags(POST_EXT_VISITED)) + to_xml(out, *post); + + out << "</transaction>\n"; + } +} + +void format_xml::flush() +{ + std::ostream& out(report.output_stream); + + out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; + out << "<ledger version=\"" << VERSION << "\">\n"; + + out << "<commodities>\n"; + foreach (const commodities_pair& pair, commodities) { + to_xml(out, *pair.second, true); + out << '\n'; + } + out << "</commodities>\n"; + + out << "<accounts>\n"; + xml_account(out, report.session.journal->master); + out << "</accounts>\n"; + + out << "<transactions>\n"; + foreach (const xact_t * xact, transactions) + xml_transaction(out, xact); + out << "</transactions>\n"; + + out << "</ledger>\n"; + out.flush(); +} + +void format_xml::operator()(post_t& post) +{ + assert(post.xdata().has_flags(POST_EXT_VISITED)); + + commodities.insert(commodities_pair(post.amount.commodity().symbol(), + &post.amount.commodity())); + + if (transactions_set.find(post.xact) == transactions_set.end()) + transactions.push_back(post.xact); +} + +} // namespace ledger diff --git a/src/derive.h b/src/xml.h index cf764b10..30a7b1b1 100644 --- a/src/derive.h +++ b/src/xml.h @@ -30,11 +30,11 @@ */ /** - * @addtogroup derive + * @addtogroup report */ /** - * @file derive.h + * @file xml.h * @author John Wiegley * * @ingroup report @@ -43,24 +43,48 @@ * * Long. */ -#ifndef _DERIVE_H -#define _DERIVE_H +#ifndef _XML_H +#define _XML_H -#include "value.h" +#include "chain.h" namespace ledger { -class call_scope_t; - -value_t xact_command(call_scope_t& args); -value_t template_command(call_scope_t& args); - class xact_t; +class account_t; +class commodity_t; +class post_t; class report_t; -xact_t * derive_new_xact(report_t& report, - value_t::sequence_t::const_iterator i, - value_t::sequence_t::const_iterator end); + +/** + * @brief Brief + * + * Long. + */ +class format_xml : public item_handler<post_t> +{ +protected: + report_t& report; + + typedef std::map<string, commodity_t *> commodities_map; + typedef std::pair<string, commodity_t *> commodities_pair; + + commodities_map commodities; + std::set<xact_t *> transactions_set; + std::deque<xact_t *> transactions; + +public: + format_xml(report_t& _report) : report(_report) { + TRACE_CTOR(format_xml, "report&"); + } + virtual ~format_xml() { + TRACE_DTOR(format_xml); + } + + virtual void flush(); + virtual void operator()(post_t& post); +}; } // namespace ledger -#endif // _DERIVE_H +#endif // _XML_H |