summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/account.h9
-rw-r--r--src/accum.h14
-rw-r--r--src/amount.cc25
-rw-r--r--src/amount.h28
-rw-r--r--src/annotate.cc2
-rw-r--r--src/annotate.h33
-rw-r--r--src/archive.cc2
-rw-r--r--src/archive.h9
-rw-r--r--src/balance.cc8
-rw-r--r--src/balance.h2
-rw-r--r--src/chain.cc32
-rw-r--r--src/chain.h9
-rw-r--r--src/commodity.cc47
-rw-r--r--src/commodity.h23
-rw-r--r--src/compare.h9
-rw-r--r--src/derive.cc561
-rw-r--r--src/draft.cc525
-rw-r--r--src/draft.h113
-rw-r--r--src/emacs.h9
-rw-r--r--src/error.h4
-rw-r--r--src/expr.cc136
-rw-r--r--src/expr.h153
-rw-r--r--src/exprbase.h254
-rw-r--r--src/filters.cc8
-rw-r--r--src/filters.h153
-rw-r--r--src/flags.h19
-rw-r--r--src/format.cc83
-rw-r--r--src/format.h52
-rw-r--r--src/generate.h4
-rw-r--r--src/global.cc18
-rw-r--r--src/global.h14
-rw-r--r--src/hooks.h10
-rw-r--r--src/interactive.cc9
-rw-r--r--src/interactive.h9
-rw-r--r--src/item.h9
-rw-r--r--src/iterators.h44
-rw-r--r--src/journal.h9
-rw-r--r--src/mask.h11
-rw-r--r--src/op.h26
-rw-r--r--src/option.cc2
-rw-r--r--src/option.h4
-rw-r--r--src/output.cc45
-rw-r--r--src/output.h24
-rw-r--r--src/parser.cc5
-rw-r--r--src/parser.h24
-rw-r--r--src/pool.h10
-rw-r--r--src/post.cc95
-rw-r--r--src/post.h11
-rw-r--r--src/precmd.cc24
-rw-r--r--src/precmd.h4
-rw-r--r--src/predicate.h256
-rw-r--r--src/pstream.h4
-rw-r--r--src/py_amount.cc15
-rw-r--r--src/py_xact.cc2
-rw-r--r--src/query.cc (renamed from src/predicate.cc)92
-rw-r--r--src/query.h291
-rw-r--r--src/quotes.h4
-rw-r--r--src/report.cc36
-rw-r--r--src/report.h9
-rw-r--r--src/scope.h42
-rw-r--r--src/session.h9
-rw-r--r--src/stats.cc2
-rw-r--r--src/stats.h4
-rw-r--r--src/temps.h4
-rw-r--r--src/textual.cc34
-rw-r--r--src/timelog.h14
-rw-r--r--src/times.h22
-rw-r--r--src/token.cc19
-rw-r--r--src/token.h11
-rw-r--r--src/unistring.h5
-rw-r--r--src/utils.h47
-rw-r--r--src/value.cc53
-rw-r--r--src/value.h6
-rw-r--r--src/xact.cc59
-rw-r--r--src/xact.h47
-rw-r--r--src/xml.cc124
-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
diff --git a/src/expr.h b/src/expr.h
index fc6a76b1..97e8c948 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -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;
diff --git a/src/item.h b/src/item.h
index ea510a5e..84385eb7 100644
--- a/src/item.h
+++ b/src/item.h
@@ -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:
diff --git a/src/mask.h b/src/mask.h
index 62df9b63..18f1893d 100644
--- a/src/mask.h
+++ b/src/mask.h
@@ -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
diff --git a/src/op.h b/src/op.h
index 2c15186b..48d167b7 100644
--- a/src/op.h
+++ b/src/op.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
diff --git a/src/pool.h b/src/pool.h
index 76257676..7328df9d 100644
--- a/src/pool.h
+++ b/src/pool.h
@@ -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
diff --git a/src/post.h b/src/post.h
index 42217d60..addf0629 100644
--- a/src/post.h
+++ b/src/post.h
@@ -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 << "&lt;";
+ break;
+ case '>':
+ buf << "&gt;";
+ break;
+ case '&':
+ buf << "&amp;";
+ 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
diff --git a/src/xact.h b/src/xact.h
index ecdf12ad..9a52fafe 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -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