Merge branch 'next'

This commit is contained in:
John Wiegley 2009-11-19 03:37:16 -05:00
commit cc9110a43a
20 changed files with 1750 additions and 613 deletions

View file

@ -55,9 +55,10 @@ typedef std::map<const string, account_t *> accounts_map;
class account_t : public supports_flags<>, public scope_t class account_t : public supports_flags<>, public scope_t
{ {
#define ACCOUNT_NORMAL 0x00 // no flags at all, a basic account #define ACCOUNT_NORMAL 0x00 // no flags at all, a basic account
#define ACCOUNT_KNOWN 0x01 #define ACCOUNT_KNOWN 0x01
#define ACCOUNT_TEMP 0x02 // account is a temporary object #define ACCOUNT_TEMP 0x02 // account is a temporary object
#define ACCOUNT_GENERATED 0x04 // account never actually existed
public: public:
account_t * parent; account_t * parent;

View file

@ -43,7 +43,7 @@
#include "xact.h" #include "xact.h"
#define LEDGER_MAGIC 0x4c454447 #define LEDGER_MAGIC 0x4c454447
#define ARCHIVE_VERSION 0x03000005 #define ARCHIVE_VERSION 0x03000006
//BOOST_IS_ABSTRACT(ledger::scope_t) //BOOST_IS_ABSTRACT(ledger::scope_t)
BOOST_CLASS_EXPORT(ledger::scope_t) BOOST_CLASS_EXPORT(ledger::scope_t)

View file

@ -41,7 +41,7 @@ namespace ledger {
post_handler_ptr chain_post_handlers(report_t& report, post_handler_ptr chain_post_handlers(report_t& report,
post_handler_ptr base_handler, post_handler_ptr base_handler,
bool only_preliminaries) bool for_accounts_report)
{ {
post_handler_ptr handler(base_handler); post_handler_ptr handler(base_handler);
predicate_t display_predicate; predicate_t display_predicate;
@ -51,7 +51,7 @@ post_handler_ptr chain_post_handlers(report_t& report,
expr_t& expr(report.HANDLER(amount_).expr); expr_t& expr(report.HANDLER(amount_).expr);
expr.set_context(&report); expr.set_context(&report);
if (! only_preliminaries) { if (! for_accounts_report) {
// Make sure only forecast postings which match are allowed through // Make sure only forecast postings which match are allowed through
if (report.HANDLED(forecast_while_)) { if (report.HANDLED(forecast_while_)) {
handler.reset(new filter_posts handler.reset(new filter_posts
@ -77,25 +77,23 @@ post_handler_ptr chain_post_handlers(report_t& report,
report.what_to_keep()); report.what_to_keep());
handler.reset(new filter_posts(handler, display_predicate, report)); handler.reset(new filter_posts(handler, display_predicate, report));
} }
// changed_value_posts adds virtual posts to the list to account for
// changes in market value of commodities, which otherwise would affect
// the running total unpredictably.
if (report.HANDLED(revalued))
handler.reset(new changed_value_posts
(handler,
report.HANDLER(display_amount_).expr,
report.HANDLED(revalued_total_) ?
report.HANDLER(revalued_total_).expr :
report.HANDLER(display_total_).expr,
report.HANDLER(display_total_).expr,
report, report.HANDLED(revalued_only)));
} }
// changed_value_posts adds virtual posts to the list to account for changes
// in market value of commodities, which otherwise would affect the running
// total unpredictably.
if (report.HANDLED(revalued) && (! for_accounts_report ||
report.HANDLED(unrealized)))
handler.reset(new changed_value_posts(handler, report,
for_accounts_report,
report.HANDLED(unrealized)));
// calc_posts computes the running total. When this appears will determine, // calc_posts computes the running total. When this appears will determine,
// for example, whether filtered posts are included or excluded from the // for example, whether filtered posts are included or excluded from the
// running total. // running total.
handler.reset(new calc_posts(handler, expr, only_preliminaries)); handler.reset(new calc_posts(handler, expr, (! for_accounts_report ||
(report.HANDLED(revalued) &&
report.HANDLED(unrealized)))));
// filter_posts will only pass through posts matching the // filter_posts will only pass through posts matching the
// `secondary_predicate'. // `secondary_predicate'.
@ -105,7 +103,7 @@ post_handler_ptr chain_post_handlers(report_t& report,
handler.reset(new filter_posts(handler, only_predicate, report)); handler.reset(new filter_posts(handler, only_predicate, report));
} }
if (! only_preliminaries) { if (! for_accounts_report) {
// sort_posts will sort all the posts it sees, based on the `sort_order' // sort_posts will sort all the posts it sees, based on the `sort_order'
// value expression. // value expression.
if (report.HANDLED(sort_)) { if (report.HANDLED(sort_)) {
@ -139,10 +137,6 @@ post_handler_ptr chain_post_handlers(report_t& report,
else if (report.HANDLED(subtotal)) else if (report.HANDLED(subtotal))
handler.reset(new subtotal_posts(handler, expr)); handler.reset(new subtotal_posts(handler, expr));
} }
else if (! report.HANDLED(period_) &&
! report.HANDLED(unsorted)) {
handler.reset(new sort_posts(handler, "date"));
}
if (report.HANDLED(dow)) if (report.HANDLED(dow))
handler.reset(new dow_posts(handler, expr)); handler.reset(new dow_posts(handler, expr));

View file

@ -84,7 +84,7 @@ class report_t;
post_handler_ptr post_handler_ptr
chain_post_handlers(report_t& report, chain_post_handlers(report_t& report,
post_handler_ptr base_handler, post_handler_ptr base_handler,
bool only_preliminaries = false); bool for_accounts_report = false);
} // namespace ledger } // namespace ledger

View file

@ -637,7 +637,8 @@ bool compare_amount_commodities::operator()(const amount_t * left,
return false; return false;
if (aleftcomm.details.date && arightcomm.details.date) { if (aleftcomm.details.date && arightcomm.details.date) {
date_duration_t diff = *aleftcomm.details.date - *arightcomm.details.date; gregorian::date_duration diff =
*aleftcomm.details.date - *arightcomm.details.date;
return diff.is_negative(); return diff.is_negative();
} }

View file

@ -217,7 +217,7 @@ void calc_posts::operator()(post_t& post)
if (last_post) { if (last_post) {
assert(last_post->has_xdata()); assert(last_post->has_xdata());
if (! account_wise) if (calc_running_total)
xdata.total = last_post->xdata().total; xdata.total = last_post->xdata().total;
xdata.count = last_post->xdata().count + 1; xdata.count = last_post->xdata().count + 1;
} else { } else {
@ -230,7 +230,7 @@ void calc_posts::operator()(post_t& post)
account_t * acct = post.reported_account(); account_t * acct = post.reported_account();
acct->xdata().add_flags(ACCOUNT_EXT_VISITED); acct->xdata().add_flags(ACCOUNT_EXT_VISITED);
if (! account_wise) if (calc_running_total)
add_or_set_value(xdata.total, xdata.visited_value); add_or_set_value(xdata.total, xdata.visited_value);
item_handler<post_t>::operator()(post); item_handler<post_t>::operator()(post);
@ -239,19 +239,21 @@ void calc_posts::operator()(post_t& post)
} }
namespace { namespace {
typedef function<void (post_t *)> post_functor_t; typedef function<void (post_t&)> post_functor_t;
void handle_value(const value_t& value, void handle_value(const value_t& value,
account_t * account, account_t * account,
xact_t * xact, xact_t * xact,
temporaries_t& temps, temporaries_t& temps,
item_handler<post_t>& handler, post_handler_ptr handler,
const date_t& date = date_t(), const date_t& date = date_t(),
const value_t& total = value_t(), const value_t& total = value_t(),
const bool direct_amount = false, const bool direct_amount = false,
const bool mark_visited = false,
const optional<post_functor_t>& functor = none) const optional<post_functor_t>& functor = none)
{ {
post_t& post = temps.create_post(*xact, account); post_t& post = temps.create_post(*xact, account);
post.add_flags(ITEM_GENERATED);
// If the account for this post is all virtual, then report the post as // If the account for this post is all virtual, then report the post as
// such. This allows subtotal reports to show "(Account)" for accounts // such. This allows subtotal reports to show "(Account)" for accounts
@ -302,11 +304,16 @@ namespace {
xdata.add_flags(POST_EXT_DIRECT_AMT); xdata.add_flags(POST_EXT_DIRECT_AMT);
if (functor) if (functor)
(*functor)(&post); (*functor)(post);
DEBUG("filter.changed_value.rounding", "post.amount = " << post.amount); DEBUG("filter.changed_value.rounding", "post.amount = " << post.amount);
handler(post); (*handler)(post);
if (mark_visited) {
post.xdata().add_flags(POST_EXT_VISITED);
post.account->xdata().add_flags(ACCOUNT_EXT_VISITED);
}
} }
} }
@ -344,7 +351,7 @@ void collapse_posts::report_subtotal()
earliest_date : last_xact->_date); earliest_date : last_xact->_date);
DEBUG("filter.collapse", "Pseudo-xact date = " << *xact._date); DEBUG("filter.collapse", "Pseudo-xact date = " << *xact._date);
handle_value(subtotal, &totals_account, &xact, temps, *handler); handle_value(subtotal, &totals_account, &xact, temps, handler);
} }
component_posts.clear(); component_posts.clear();
@ -404,30 +411,58 @@ void related_posts::flush()
item_handler<post_t>::flush(); item_handler<post_t>::flush();
} }
changed_value_posts::changed_value_posts(post_handler_ptr handler,
report_t& _report,
bool _for_accounts_report,
bool _show_unrealized)
: item_handler<post_t>(handler), report(_report),
for_accounts_report(_for_accounts_report),
show_unrealized(_show_unrealized), last_post(NULL),
revalued_account(temps.create_account(_("<Revalued>"))),
rounding_account(temps.create_account(_("<Rounding>")))
{
TRACE_CTOR(changed_value_posts, "post_handler_ptr, report_t&, bool");
display_amount_expr = report.HANDLER(display_amount_).expr;
total_expr = (report.HANDLED(revalued_total_) ?
report.HANDLER(revalued_total_).expr :
report.HANDLER(display_total_).expr);
display_total_expr = report.HANDLER(display_total_).expr;
changed_values_only = report.HANDLED(revalued_only);
gains_equity_account =
report.session.journal->master->find_account(_("Equity:Unrealized Gains"));
gains_equity_account->add_flags(ACCOUNT_GENERATED);
losses_equity_account =
report.session.journal->master->find_account(_("Equity:Unrealized Losses"));
losses_equity_account->add_flags(ACCOUNT_GENERATED);
}
void changed_value_posts::flush() void changed_value_posts::flush()
{ {
if (last_post && last_post->date() <= report.terminus.date()) { if (last_post && last_post->date() <= report.terminus.date()) {
output_revaluation(last_post, report.terminus.date()); output_revaluation(*last_post, report.terminus.date());
last_post = NULL; last_post = NULL;
} }
item_handler<post_t>::flush(); item_handler<post_t>::flush();
} }
void changed_value_posts::output_revaluation(post_t * post, const date_t& date) void changed_value_posts::output_revaluation(post_t& post, const date_t& date)
{ {
if (is_valid(date)) if (is_valid(date))
post->xdata().date = date; post.xdata().date = date;
value_t repriced_total; value_t repriced_total;
try { try {
bind_scope_t bound_scope(report, *post); bind_scope_t bound_scope(report, post);
repriced_total = total_expr.calc(bound_scope); repriced_total = total_expr.calc(bound_scope);
} }
catch (...) { catch (...) {
post->xdata().date = date_t(); post.xdata().date = date_t();
throw; throw;
} }
post->xdata().date = date_t(); post.xdata().date = date_t();
DEBUG("filter.changed_value", DEBUG("filter.changed_value",
"output_revaluation(last_balance) = " << last_total); "output_revaluation(last_balance) = " << last_total);
@ -441,19 +476,44 @@ void changed_value_posts::output_revaluation(post_t * post, const date_t& date)
xact_t& xact = temps.create_xact(); xact_t& xact = temps.create_xact();
xact.payee = _("Commodities revalued"); xact.payee = _("Commodities revalued");
xact._date = is_valid(date) ? date : post->date(); xact._date = is_valid(date) ? date : post.date();
handle_value(diff, &revalued_account, &xact, temps, *handler, if (! for_accounts_report) {
*xact._date, repriced_total, false, handle_value
optional<post_functor_t> (/* value= */ diff,
(bind(&changed_value_posts::output_rounding, this, _1))); /* account= */ &revalued_account,
/* xact= */ &xact,
/* temps= */ temps,
/* handler= */ handler,
/* date= */ *xact._date,
/* total= */ repriced_total,
/* direct_amount= */ false,
/* mark_visited= */ false,
/* functor= */ (optional<post_functor_t>
(bind(&changed_value_posts::output_rounding,
this, _1))));
}
else if (show_unrealized) {
handle_value
(/* value= */ - diff,
/* account= */ (diff < 0L ?
losses_equity_account :
gains_equity_account),
/* xact= */ &xact,
/* temps= */ temps,
/* handler= */ handler,
/* date= */ *xact._date,
/* total= */ value_t(),
/* direct_amount= */ false,
/* mark_visited= */ true);
}
} }
} }
} }
void changed_value_posts::output_rounding(post_t * post) void changed_value_posts::output_rounding(post_t& post)
{ {
bind_scope_t bound_scope(report, *post); bind_scope_t bound_scope(report, post);
value_t new_display_total(display_total_expr.calc(bound_scope)); value_t new_display_total(display_total_expr.calc(bound_scope));
DEBUG("filter.changed_value.rounding", DEBUG("filter.changed_value.rounding",
@ -478,9 +538,9 @@ void changed_value_posts::output_rounding(post_t * post)
xact_t& xact = temps.create_xact(); xact_t& xact = temps.create_xact();
xact.payee = _("Commodity rounding"); xact.payee = _("Commodity rounding");
xact._date = post->date(); xact._date = post.date();
handle_value(diff, &rounding_account, &xact, temps, *handler, handle_value(diff, &rounding_account, &xact, temps, handler,
*xact._date, precise_display_total, true); *xact._date, precise_display_total, true);
} }
} }
@ -491,19 +551,19 @@ void changed_value_posts::output_rounding(post_t * post)
void changed_value_posts::operator()(post_t& post) void changed_value_posts::operator()(post_t& post)
{ {
if (last_post) if (last_post)
output_revaluation(last_post, post.date()); output_revaluation(*last_post, post.date());
if (changed_values_only) if (changed_values_only)
post.xdata().add_flags(POST_EXT_DISPLAYED); post.xdata().add_flags(POST_EXT_DISPLAYED);
output_rounding(&post); if (! for_accounts_report)
output_rounding(post);
item_handler<post_t>::operator()(post); item_handler<post_t>::operator()(post);
bind_scope_t bound_scope(report, post); bind_scope_t bound_scope(report, post);
last_total = total_expr.calc(bound_scope); last_total = total_expr.calc(bound_scope);
last_post = &post;
last_post = &post;
} }
void subtotal_posts::report_subtotal(const char * spec_fmt, void subtotal_posts::report_subtotal(const char * spec_fmt,
@ -515,12 +575,14 @@ void subtotal_posts::report_subtotal(const char * spec_fmt,
optional<date_t> range_start = interval ? interval->start : none; optional<date_t> range_start = interval ? interval->start : none;
optional<date_t> range_finish = interval ? interval->inclusive_end() : none; optional<date_t> range_finish = interval ? interval->inclusive_end() : none;
foreach (post_t * post, component_posts) { if (! range_start || ! range_finish) {
date_t date = post->date(); foreach (post_t * post, component_posts) {
if (! range_start || date < *range_start) date_t date = post->date();
range_start = date; if (! range_start || date < *range_start)
if (! range_finish || date > *range_finish) range_start = date;
range_finish = date; if (! range_finish || date > *range_finish)
range_finish = date;
}
} }
component_posts.clear(); component_posts.clear();
@ -542,7 +604,7 @@ void subtotal_posts::report_subtotal(const char * spec_fmt,
foreach (values_map::value_type& pair, values) foreach (values_map::value_type& pair, values)
handle_value(pair.second.value, pair.second.account, &xact, temps, handle_value(pair.second.value, pair.second.account, &xact, temps,
*handler); handler);
values.clear(); values.clear();
} }
@ -652,10 +714,10 @@ void posts_as_equity::report_subtotal()
foreach (balance_t::amounts_map::value_type amount_pair, foreach (balance_t::amounts_map::value_type amount_pair,
pair.second.value.as_balance().amounts) pair.second.value.as_balance().amounts)
handle_value(amount_pair.second, pair.second.account, &xact, temps, handle_value(amount_pair.second, pair.second.account, &xact, temps,
*handler); handler);
} else { } else {
handle_value(pair.second.value, pair.second.account, &xact, temps, handle_value(pair.second.value, pair.second.account, &xact, temps,
*handler); handler);
} }
total += pair.second.value; total += pair.second.value;
} }
@ -784,7 +846,7 @@ void budget_posts::report_budget_items(const date_t& date)
assert(begin); assert(begin);
if (*begin <= date && if (*begin <= date &&
(! pair.first.end || *begin < *pair.first.end)) { (! pair.first.finish || *begin < *pair.first.finish)) {
post_t& post = *pair.second; post_t& post = *pair.second;
DEBUG("budget.generate", "Reporting budget for " DEBUG("budget.generate", "Reporting budget for "
@ -905,8 +967,8 @@ void forecast_posts::flush()
} }
date_t& begin = *(*least).first.start; date_t& begin = *(*least).first.start;
if ((*least).first.end) if ((*least).first.finish)
assert(begin < *(*least).first.end); assert(begin < *(*least).first.finish);
// If the next date in the series for this periodic posting is more than 5 // If the next date in the series for this periodic posting is more than 5
// years beyond the last valid post we generated, drop it from further // years beyond the last valid post we generated, drop it from further

View file

@ -241,8 +241,7 @@ public:
const predicate_t& predicate, const predicate_t& predicate,
scope_t& _context) scope_t& _context)
: item_handler<post_t>(handler), pred(predicate), context(_context) { : item_handler<post_t>(handler), pred(predicate), context(_context) {
TRACE_CTOR(filter_posts, TRACE_CTOR(filter_posts, "post_handler_ptr, predicate_t, scope_t&");
"post_handler_ptr, const predicate_t&, scope_t&");
} }
virtual ~filter_posts() { virtual ~filter_posts() {
TRACE_DTOR(filter_posts); TRACE_DTOR(filter_posts);
@ -280,16 +279,16 @@ class calc_posts : public item_handler<post_t>
{ {
post_t * last_post; post_t * last_post;
expr_t& amount_expr; expr_t& amount_expr;
bool account_wise; bool calc_running_total;
calc_posts(); calc_posts();
public: public:
calc_posts(post_handler_ptr handler, calc_posts(post_handler_ptr handler,
expr_t& _amount_expr, expr_t& _amount_expr,
bool _account_wise = false) bool _calc_running_total = false)
: item_handler<post_t>(handler), last_post(NULL), : item_handler<post_t>(handler), last_post(NULL),
amount_expr(_amount_expr), account_wise(_account_wise) { amount_expr(_amount_expr), calc_running_total(_calc_running_total) {
TRACE_CTOR(calc_posts, "post_handler_ptr, expr_t&, bool"); TRACE_CTOR(calc_posts, "post_handler_ptr, expr_t&, bool");
} }
virtual ~calc_posts() { virtual ~calc_posts() {
@ -379,39 +378,33 @@ class changed_value_posts : public item_handler<post_t>
expr_t display_total_expr; expr_t display_total_expr;
report_t& report; report_t& report;
bool changed_values_only; bool changed_values_only;
bool for_accounts_report;
bool show_unrealized;
post_t * last_post; post_t * last_post;
value_t last_total; value_t last_total;
value_t last_display_total; value_t last_display_total;
temporaries_t temps; temporaries_t temps;
account_t& revalued_account; account_t& revalued_account;
account_t& rounding_account; account_t& rounding_account;
account_t * gains_equity_account;
account_t * losses_equity_account;
changed_value_posts(); changed_value_posts();
public: public:
changed_value_posts(post_handler_ptr handler, changed_value_posts(post_handler_ptr handler,
const expr_t& _display_amount_expr, report_t& _report,
const expr_t& _total_expr, bool _for_accounts_report,
const expr_t& _display_total_expr, bool _show_unrealized);
report_t& _report,
bool _changed_values_only)
: item_handler<post_t>(handler),
display_amount_expr(_display_amount_expr), total_expr(_total_expr),
display_total_expr(_display_total_expr), report(_report),
changed_values_only(_changed_values_only), last_post(NULL),
revalued_account(temps.create_account(_("<Revalued>"))),
rounding_account(temps.create_account(_("<Rounding>"))) {
TRACE_CTOR(changed_value_posts,
"post_handler_ptr, const expr_t&, const expr_t&, report_t&, bool");
}
virtual ~changed_value_posts() { virtual ~changed_value_posts() {
TRACE_DTOR(changed_value_posts); TRACE_DTOR(changed_value_posts);
} }
virtual void flush(); virtual void flush();
void output_revaluation(post_t * post, const date_t& current); void output_revaluation(post_t& post, const date_t& current);
void output_rounding(post_t * post); void output_rounding(post_t& post);
virtual void operator()(post_t& post); virtual void operator()(post_t& post);
}; };

View file

@ -165,57 +165,12 @@ value_t period_command(call_scope_t& args)
report_t& report(find_scope<report_t>(args)); report_t& report(find_scope<report_t>(args));
std::ostream& out(report.output_stream); std::ostream& out(report.output_stream);
show_period_tokens(out, arg);
out << std::endl;
date_interval_t interval(arg); date_interval_t interval(arg);
interval.dump(out, report.session.current_year);
out << _("global details => ") << std::endl << std::endl;
if (interval.start)
out << _(" start: ") << format_date(*interval.start) << std::endl;
else
out << _(" start: TODAY: ") << format_date(CURRENT_DATE()) << std::endl;
if (interval.end)
out << _(" end: ") << format_date(*interval.end) << std::endl;
if (interval.skip_duration)
out << _(" skip: ") << *interval.skip_duration << std::endl;
if (interval.factor)
out << _(" factor: ") << interval.factor << std::endl;
if (interval.duration)
out << _("duration: ") << *interval.duration << std::endl;
if (interval.find_period(interval.start ?
*interval.start : CURRENT_DATE())) {
out << std::endl
<< _("after finding first period => ") << std::endl
<< std::endl;
if (interval.start)
out << _(" start: ") << format_date(*interval.start) << std::endl;
if (interval.end)
out << _(" end: ") << format_date(*interval.end) << std::endl;
if (interval.skip_duration)
out << _(" skip: ") << *interval.skip_duration << std::endl;
if (interval.factor)
out << _(" factor: ") << interval.factor << std::endl;
if (interval.duration)
out << _("duration: ") << *interval.duration << std::endl;
out << std::endl;
for (int i = 0; i < 20 && interval; i++, ++interval) {
out << std::right;
out.width(2);
out << i << "): " << format_date(*interval.start);
if (interval.end_of_duration)
out << " -- " << format_date(*interval.inclusive_end());
out << std::endl;
if (! interval.skip_duration)
break;
}
}
return NULL_VALUE; return NULL_VALUE;
} }
@ -229,14 +184,12 @@ value_t args_command(call_scope_t& args)
out << std::endl << std::endl; out << std::endl << std::endl;
query_t query(args.value(), report.what_to_keep()); query_t query(args.value(), report.what_to_keep());
if (! query) if (query) {
throw_(std::runtime_error, call_scope_t sub_args(static_cast<scope_t&>(args));
_("Invalid query predicate: %1") << join_args(args)); sub_args.push_back(string_value(query.text()));
call_scope_t sub_args(static_cast<scope_t&>(args)); parse_command(sub_args);
sub_args.push_back(string_value(query.text())); }
parse_command(sub_args);
if (query.tokens_remaining()) { if (query.tokens_remaining()) {
out << std::endl << _("====== Display predicate ======") out << std::endl << _("====== Display predicate ======")
@ -244,14 +197,12 @@ value_t args_command(call_scope_t& args)
query.parse_again(); query.parse_again();
if (! query) if (query) {
throw_(std::runtime_error, call_scope_t disp_sub_args(static_cast<scope_t&>(args));
_("Invalid display predicate: %1") << join_args(args)); disp_sub_args.push_back(string_value(query.text()));
call_scope_t disp_sub_args(static_cast<scope_t&>(args)); parse_command(disp_sub_args);
disp_sub_args.push_back(string_value(query.text())); }
parse_command(disp_sub_args);
} }
return NULL_VALUE; return NULL_VALUE;

View file

@ -263,12 +263,12 @@ query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_contex
node->set_right(arg1); node->set_right(arg1);
} }
if (interval.end) { if (interval.finish) {
expr_t::ptr_op_t lt = new expr_t::op_t(expr_t::op_t::O_LT); expr_t::ptr_op_t lt = new expr_t::op_t(expr_t::op_t::O_LT);
lt->set_left(ident); lt->set_left(ident);
expr_t::ptr_op_t arg1 = new expr_t::op_t(expr_t::op_t::VALUE); expr_t::ptr_op_t arg1 = new expr_t::op_t(expr_t::op_t::VALUE);
arg1->set_value(*interval.end); arg1->set_value(*interval.finish);
lt->set_right(arg1); lt->set_right(arg1);
if (node) { if (node) {

View file

@ -96,14 +96,14 @@ public:
explicit token_t(kind_t _kind = UNKNOWN, explicit token_t(kind_t _kind = UNKNOWN,
const optional<string>& _value = none) const optional<string>& _value = none)
: kind(_kind), value(_value) { : kind(_kind), value(_value) {
TRACE_CTOR(lexer_t::token_t, ""); TRACE_CTOR(query_t::lexer_t::token_t, "");
} }
token_t(const token_t& tok) token_t(const token_t& tok)
: kind(tok.kind), value(tok.value) { : kind(tok.kind), value(tok.value) {
TRACE_CTOR(lexer_t::token_t, "copy"); TRACE_CTOR(query_t::lexer_t::token_t, "copy");
} }
~token_t() throw() { ~token_t() throw() {
TRACE_DTOR(lexer_t::token_t); TRACE_DTOR(query_t::lexer_t::token_t);
} }
token_t& operator=(const token_t& tok) { token_t& operator=(const token_t& tok) {
@ -120,40 +120,42 @@ public:
string to_string() const { string to_string() const {
switch (kind) { switch (kind) {
case UNKNOWN: return "UNKNOWN"; case UNKNOWN: return "UNKNOWN";
case LPAREN: return "LPAREN"; case LPAREN: return "LPAREN";
case RPAREN: return "RPAREN"; case RPAREN: return "RPAREN";
case TOK_NOT: return "TOK_NOT"; case TOK_NOT: return "TOK_NOT";
case TOK_AND: return "TOK_AND"; case TOK_AND: return "TOK_AND";
case TOK_OR: return "TOK_OR"; case TOK_OR: return "TOK_OR";
case TOK_EQ: return "TOK_EQ"; case TOK_EQ: return "TOK_EQ";
case TOK_DATE: return "TOK_DATE"; case TOK_DATE: return "TOK_DATE";
case TOK_CODE: return "TOK_CODE"; case TOK_CODE: return "TOK_CODE";
case TOK_PAYEE: return "TOK_PAYEE"; case TOK_PAYEE: return "TOK_PAYEE";
case TOK_NOTE: return "TOK_NOTE"; case TOK_NOTE: return "TOK_NOTE";
case TOK_ACCOUNT: return "TOK_ACCOUNT"; case TOK_ACCOUNT: return "TOK_ACCOUNT";
case TOK_META: return "TOK_META"; case TOK_META: return "TOK_META";
case TOK_EXPR: return "TOK_EXPR"; case TOK_EXPR: return "TOK_EXPR";
case TERM: return string("TERM(") + *value + ")"; case TERM: return string("TERM(") + *value + ")";
case END_REACHED: return "END_REACHED"; case END_REACHED: return "END_REACHED";
} }
assert(false);
return empty_string;
} }
string symbol() const { string symbol() const {
switch (kind) { switch (kind) {
case LPAREN: return "("; case LPAREN: return "(";
case RPAREN: return ")"; case RPAREN: return ")";
case TOK_NOT: return "not"; case TOK_NOT: return "not";
case TOK_AND: return "and"; case TOK_AND: return "and";
case TOK_OR: return "or"; case TOK_OR: return "or";
case TOK_EQ: return "="; case TOK_EQ: return "=";
case TOK_DATE: return "date"; case TOK_DATE: return "date";
case TOK_CODE: return "code"; case TOK_CODE: return "code";
case TOK_PAYEE: return "payee"; case TOK_PAYEE: return "payee";
case TOK_NOTE: return "note"; case TOK_NOTE: return "note";
case TOK_ACCOUNT: return "account"; case TOK_ACCOUNT: return "account";
case TOK_META: return "meta"; case TOK_META: return "meta";
case TOK_EXPR: return "expr"; case TOK_EXPR: return "expr";
case END_REACHED: return "<EOF>"; case END_REACHED: return "<EOF>";
@ -180,7 +182,7 @@ public:
consume_whitespace(false), consume_whitespace(false),
consume_next_arg(false) consume_next_arg(false)
{ {
TRACE_CTOR(lexer_t, ""); TRACE_CTOR(query_t::lexer_t, "");
assert(begin != end); assert(begin != end);
arg_i = (*begin).as_string().begin(); arg_i = (*begin).as_string().begin();
arg_end = (*begin).as_string().end(); arg_end = (*begin).as_string().end();
@ -192,10 +194,10 @@ public:
consume_next_arg(lexer.consume_next_arg), consume_next_arg(lexer.consume_next_arg),
token_cache(lexer.token_cache) token_cache(lexer.token_cache)
{ {
TRACE_CTOR(lexer_t, "copy"); TRACE_CTOR(query_t::lexer_t, "copy");
} }
~lexer_t() throw() { ~lexer_t() throw() {
TRACE_DTOR(lexer_t); TRACE_DTOR(query_t::lexer_t);
} }
token_t next_token(); token_t next_token();
@ -227,14 +229,14 @@ protected:
public: public:
parser_t(const value_t& _args) parser_t(const value_t& _args)
: args(_args), lexer(args.begin(), args.end()) { : args(_args), lexer(args.begin(), args.end()) {
TRACE_CTOR(parser_t, ""); TRACE_CTOR(query_t::parser_t, "");
} }
parser_t(const parser_t& parser) parser_t(const parser_t& parser)
: args(parser.args), lexer(parser.lexer) { : args(parser.args), lexer(parser.lexer) {
TRACE_CTOR(parser_t, "copy"); TRACE_CTOR(query_t::parser_t, "copy");
} }
~parser_t() throw() { ~parser_t() throw() {
TRACE_DTOR(parser_t); TRACE_DTOR(query_t::parser_t);
} }
expr_t::ptr_op_t parse() { expr_t::ptr_op_t parse() {

View file

@ -120,14 +120,15 @@ void report_t::normalize_options(const string& verb)
date_interval_t interval(HANDLER(period_).str()); date_interval_t interval(HANDLER(period_).str());
if (! HANDLED(begin_) && interval.start) { optional<date_t> begin = interval.begin(session.current_year);
string predicate = optional<date_t> end = interval.end(session.current_year);
"date>=[" + to_iso_extended_string(*interval.start) + "]";
if (! HANDLED(begin_) && begin) {
string predicate = "date>=[" + to_iso_extended_string(*begin) + "]";
HANDLER(limit_).on(string("?normalize"), predicate); HANDLER(limit_).on(string("?normalize"), predicate);
} }
if (! HANDLED(end_) && interval.end) { if (! HANDLED(end_) && end) {
string predicate = string predicate = "date<[" + to_iso_extended_string(*end) + "]";
"date<[" + to_iso_extended_string(*interval.end) + "]";
HANDLER(limit_).on(string("?normalize"), predicate); HANDLER(limit_).on(string("?normalize"), predicate);
} }
@ -221,25 +222,21 @@ void report_t::normalize_options(const string& verb)
void report_t::parse_query_args(const value_t& args, const string& whence) void report_t::parse_query_args(const value_t& args, const string& whence)
{ {
query_t query(args, what_to_keep()); query_t query(args, what_to_keep());
if (! query) if (query) {
throw_(std::runtime_error, HANDLER(limit_).on(whence, query.text());
_("Invalid query predicate: %1") << query.text());
HANDLER(limit_).on(whence, query.text()); DEBUG("report.predicate",
"Predicate = " << HANDLER(limit_).str());
DEBUG("report.predicate", }
"Predicate = " << HANDLER(limit_).str());
if (query.tokens_remaining()) { if (query.tokens_remaining()) {
query.parse_again(); query.parse_again();
if (! query) if (query) {
throw_(std::runtime_error, HANDLER(display_).on(whence, query.text());
_("Invalid display predicate: %1") << query.text());
HANDLER(display_).on(whence, query.text()); DEBUG("report.predicate",
"Display predicate = " << HANDLER(display_).str());
DEBUG("report.predicate", }
"Display predicate = " << HANDLER(display_).str());
} }
} }
@ -281,6 +278,12 @@ void report_t::accounts_report(acct_handler_ptr handler)
chain_post_handlers(*this, post_handler_ptr(new ignore_posts), true); chain_post_handlers(*this, post_handler_ptr(new ignore_posts), true);
pass_down_posts(chain, walker); pass_down_posts(chain, walker);
HANDLER(amount_).expr.mark_uncompiled();
HANDLER(total_).expr.mark_uncompiled();
HANDLER(display_amount_).expr.mark_uncompiled();
HANDLER(display_total_).expr.mark_uncompiled();
HANDLER(revalued_total_).expr.mark_uncompiled();
scoped_ptr<accounts_iterator> iter; scoped_ptr<accounts_iterator> iter;
if (! HANDLED(sort_)) { if (! HANDLED(sort_)) {
iter.reset(new basic_accounts_iterator(*session.journal->master)); iter.reset(new basic_accounts_iterator(*session.journal->master));
@ -884,6 +887,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
case 'u': case 'u':
OPT(unbudgeted); OPT(unbudgeted);
else OPT(uncleared); else OPT(uncleared);
else OPT(unrealized);
else OPT(unround); else OPT(unround);
else OPT(unsorted); else OPT(unsorted);
break; break;

View file

@ -50,6 +50,7 @@
#include "option.h" #include "option.h"
#include "commodity.h" #include "commodity.h"
#include "annotate.h" #include "annotate.h"
#include "session.h"
#include "format.h" #include "format.h"
namespace ledger { namespace ledger {
@ -300,6 +301,7 @@ public:
HANDLER(truncate_).report(out); HANDLER(truncate_).report(out);
HANDLER(unbudgeted).report(out); HANDLER(unbudgeted).report(out);
HANDLER(uncleared).report(out); HANDLER(uncleared).report(out);
HANDLER(unrealized).report(out);
HANDLER(unround).report(out); HANDLER(unround).report(out);
HANDLER(unsorted).report(out); HANDLER(unsorted).report(out);
HANDLER(weekly).report(out); HANDLER(weekly).report(out);
@ -352,7 +354,7 @@ public:
set_expr(args[0].to_string(), args[1].to_string()); set_expr(args[0].to_string(), args[1].to_string());
}); });
OPTION(report_t, amount_data); OPTION(report_t, amount_data); // -j
OPTION(report_t, anon); OPTION(report_t, anon);
OPTION_(report_t, average, DO() { // -A OPTION_(report_t, average, DO() { // -A
@ -377,14 +379,14 @@ public:
}); });
OPTION_(report_t, begin_, DO_(args) { // -b OPTION_(report_t, begin_, DO_(args) { // -b
date_interval_t interval(args[1].to_string()); date_interval_t interval(args[1].to_string());
if (! interval.start) optional<date_t> begin = interval.begin(parent->session.current_year);
if (! begin)
throw_(std::invalid_argument, throw_(std::invalid_argument,
_("Could not determine beginning of period '%1'") _("Could not determine beginning of period '%1'")
<< args[1].to_string()); << args[1].to_string());
string predicate = string predicate = "date>=[" + to_iso_extended_string(*begin) + "]";
"date>=[" + to_iso_extended_string(*interval.start) + "]";
parent->HANDLER(limit_).on(string("--begin"), predicate); parent->HANDLER(limit_).on(string("--begin"), predicate);
}); });
@ -524,17 +526,19 @@ public:
OPTION(report_t, empty); // -E OPTION(report_t, empty); // -E
OPTION_(report_t, end_, DO_(args) { // -e OPTION_(report_t, end_, DO_(args) { // -e
date_interval_t interval(args[1].to_string()); date_interval_t interval(args[1].to_string());
if (! interval.start) // Use begin() here so that if the user says --end=2008, we end on
// 2008/01/01 instead of 2009/01/01 (which is what end() would return).
optional<date_t> end = interval.begin(parent->session.current_year);
if (! end)
throw_(std::invalid_argument, throw_(std::invalid_argument,
_("Could not determine end of period '%1'") _("Could not determine end of period '%1'")
<< args[1].to_string()); << args[1].to_string());
string predicate = string predicate = "date<[" + to_iso_extended_string(*end) + "]";
"date<[" + to_iso_extended_string(*interval.start) + "]";
parent->HANDLER(limit_).on(string("--end"), predicate); parent->HANDLER(limit_).on(string("--end"), predicate);
parent->terminus = datetime_t(*interval.start); parent->terminus = datetime_t(*end);
}); });
OPTION(report_t, equity); OPTION(report_t, equity);
@ -622,11 +626,13 @@ public:
OPTION_(report_t, now_, DO_(args) { OPTION_(report_t, now_, DO_(args) {
date_interval_t interval(args[1].to_string()); date_interval_t interval(args[1].to_string());
if (! interval.start) optional<date_t> begin = interval.begin(parent->session.current_year);
if (! begin)
throw_(std::invalid_argument, throw_(std::invalid_argument,
_("Could not determine beginning of period '%1'") _("Could not determine beginning of period '%1'")
<< args[1].to_string()); << args[1].to_string());
ledger::epoch = datetime_t(*interval.start); ledger::epoch = parent->terminus = datetime_t(*begin);
parent->session.current_year = ledger::epoch->date().year();
}); });
OPTION__ OPTION__
@ -844,7 +850,7 @@ public:
set_expr(args[0].to_string(), args[1].to_string()); set_expr(args[0].to_string(), args[1].to_string());
}); });
OPTION(report_t, total_data); OPTION(report_t, total_data); // -J
OPTION_(report_t, truncate_, DO_(args) { OPTION_(report_t, truncate_, DO_(args) {
string style(args[1].to_string()); string style(args[1].to_string());
@ -868,6 +874,8 @@ public:
parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending"); parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending");
}); });
OPTION(report_t, unrealized);
OPTION_(report_t, unround, DO() { OPTION_(report_t, unround, DO() {
parent->HANDLER(display_amount_) parent->HANDLER(display_amount_)
.set_expr(string("--unround"), "unrounded(amount_expr)"); .set_expr(string("--unround"), "unrounded(amount_expr)");

File diff suppressed because it is too large Load diff

View file

@ -59,7 +59,6 @@ inline bool is_valid(const datetime_t& moment) {
} }
typedef boost::gregorian::date date_t; typedef boost::gregorian::date date_t;
typedef boost::gregorian::date_duration date_duration_t;
typedef boost::gregorian::date_iterator date_iterator_t; typedef boost::gregorian::date_iterator date_iterator_t;
inline bool is_valid(const date_t& moment) { inline bool is_valid(const date_t& moment) {
@ -85,19 +84,19 @@ string_to_day_of_week(const std::string& str);
optional<date_time::months_of_year> optional<date_time::months_of_year>
string_to_month_of_year(const std::string& str); string_to_month_of_year(const std::string& str);
datetime_t parse_datetime(const char * str, typedef optional<date_t::year_type> optional_year;
optional<date_t::year_type> current_year = none);
datetime_t parse_datetime(const char * str, optional_year current_year = none);
inline datetime_t parse_datetime(const std::string& str, inline datetime_t parse_datetime(const std::string& str,
optional<date_t::year_type> current_year = none) { optional_year current_year = none) {
return parse_datetime(str.c_str(), current_year); return parse_datetime(str.c_str(), current_year);
} }
date_t parse_date(const char * str, date_t parse_date(const char * str, optional_year current_year = none);
optional<date_t::year_type> current_year = none);
inline date_t parse_date(const std::string& str, inline date_t parse_date(const std::string& str,
optional<date_t::year_type> current_year = none) { optional_year current_year = none) {
return parse_date(str.c_str(), current_year); return parse_date(str.c_str(), current_year);
} }
@ -138,105 +137,423 @@ inline void to_xml(std::ostream& out, const date_t& when,
} }
} }
struct date_traits_t
{
bool has_year;
bool has_month;
bool has_day;
date_traits_t(bool _has_year = false,
bool _has_month = false,
bool _has_day = false)
: has_year(_has_year), has_month(_has_month), has_day(_has_day) {
TRACE_CTOR(date_traits_t, "bool, bool, bool");
}
date_traits_t(const date_traits_t& traits)
: has_year(traits.has_year),
has_month(traits.has_month),
has_day(traits.has_day) {
TRACE_CTOR(date_traits_t, "copy");
}
~date_traits_t() throw() {
TRACE_DTOR(date_traits_t);
}
date_traits_t& operator=(const date_traits_t& traits) {
has_year = traits.has_year;
has_month = traits.has_month;
has_day = traits.has_day;
return *this;
}
bool operator==(const date_traits_t& traits) const {
return (has_year == traits.has_year &&
has_month == traits.has_month &&
has_day == traits.has_day);
}
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
ar & has_year;
ar & has_month;
ar & has_day;
}
#endif // HAVE_BOOST_SERIALIZATION
};
struct date_duration_t
{
enum skip_quantum_t {
DAYS, WEEKS, MONTHS, QUARTERS, YEARS
} quantum;
int length;
date_duration_t() : quantum(DAYS), length(0) {
TRACE_CTOR(date_duration_t, "");
}
date_duration_t(skip_quantum_t _quantum, int _length)
: quantum(_quantum), length(_length) {
TRACE_CTOR(date_duration_t, "skip_quantum_t, int");
}
date_duration_t(const date_duration_t& dur)
: quantum(dur.quantum), length(dur.length) {
TRACE_CTOR(date_duration_t, "copy");
}
~date_duration_t() throw() {
TRACE_DTOR(date_duration_t);
}
date_t add(const date_t& date) const {
switch (quantum) {
case DAYS:
return date + gregorian::days(length);
case WEEKS:
return date + gregorian::weeks(length);
case MONTHS:
return date + gregorian::months(length);
case QUARTERS:
return date + gregorian::months(length * 3);
case YEARS:
return date + gregorian::years(length);
default:
assert(false); return date_t();
}
}
date_t subtract(const date_t& date) const {
switch (quantum) {
case DAYS:
return date - gregorian::days(length);
case WEEKS:
return date - gregorian::weeks(length);
case MONTHS:
return date - gregorian::months(length);
case QUARTERS:
return date - gregorian::months(length * 3);
case YEARS:
return date - gregorian::years(length);
default:
assert(false); return date_t();
}
}
string to_string() const {
std::ostringstream out;
out << length << ' ';
switch (quantum) {
case DAYS: out << "day"; break;
case WEEKS: out << "week"; break;
case MONTHS: out << "month"; break;
case QUARTERS: out << "quarter"; break;
case YEARS: out << "year"; break;
default:
assert(false);
break;
}
if (length > 1)
out << 's';
return out.str();
}
static date_t find_nearest(const date_t& date, skip_quantum_t skip);
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
ar & quantum;
ar & length;
}
#endif // HAVE_BOOST_SERIALIZATION
};
class date_specifier_t
{
friend class date_parser_t;
#if 0
typedef date_t::year_type year_type;
#else
typedef unsigned short year_type;
#endif
typedef date_t::month_type month_type;
typedef date_t::day_type day_type;
typedef date_t::day_of_week_type day_of_week_type;
optional<year_type> year;
optional<month_type> month;
optional<day_type> day;
optional<day_of_week_type> wday;
public:
date_specifier_t(const optional<year_type>& _year = none,
const optional<month_type>& _month = none,
const optional<day_type>& _day = none,
const optional<day_of_week_type>& _wday = none)
: year(_year), month(_month), day(_day), wday(_wday) {
TRACE_CTOR(date_specifier_t,
"year_type, month_type, day_type, day_of_week_type");
}
date_specifier_t(const date_t& date,
const optional<date_traits_t>& traits = none) {
TRACE_CTOR(date_specifier_t, "date_t, date_traits_t");
if (! traits || traits->has_year)
year = date.year();
if (! traits || traits->has_month)
month = date.month();
if (! traits || traits->has_day)
day = date.day();
}
date_specifier_t(const date_specifier_t& other)
: year(other.year), month(other.month),
day(other.day), wday(other.wday) {
TRACE_CTOR(date_specifier_t, "copy");
}
~date_specifier_t() throw() {
TRACE_DTOR(date_specifier_t);
}
date_t begin(const optional_year& current_year = none) const;
date_t end(const optional_year& current_year = none) const;
bool is_within(const date_t& date,
const optional_year& current_year = none) const {
return date >= begin(current_year) && date < end(current_year);
}
optional<date_duration_t> implied_duration() const {
if (day || wday)
return date_duration_t(date_duration_t::DAYS, 1);
else if (month)
return date_duration_t(date_duration_t::MONTHS, 1);
else if (year)
return date_duration_t(date_duration_t::YEARS, 1);
else
return none;
}
string to_string() const {
std::ostringstream out;
if (year)
out << " year " << *year;
if (month)
out << " month " << *month;
if (day)
out << " day " << *day;
if (wday)
out << " wday " << *wday;
return out.str();
}
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
ar & year;
ar & month;
ar & day;
ar & wday;
}
#endif // HAVE_BOOST_SERIALIZATION
};
class date_range_t
{
friend class date_parser_t;
optional<date_specifier_t> range_begin;
optional<date_specifier_t> range_end;
bool end_inclusive;
public:
date_range_t(const optional<date_specifier_t>& _range_begin = none,
const optional<date_specifier_t>& _range_end = none)
: range_begin(_range_begin), range_end(_range_end),
end_inclusive(false) {
TRACE_CTOR(date_range_t, "date_specifier_t, date_specifier_t");
}
date_range_t(const date_range_t& other)
: range_begin(other.range_begin), range_end(other.range_end),
end_inclusive(other.end_inclusive) {
TRACE_CTOR(date_range_t, "date_range_t");
}
~date_range_t() throw() {
TRACE_DTOR(date_range_t);
}
optional<date_t> begin(const optional_year& current_year = none) const {
if (range_begin)
return range_begin->begin(current_year);
else
return none;
}
optional<date_t> end(const optional_year& current_year = none) const {
if (range_end) {
if (end_inclusive)
return range_end->end(current_year);
else
return range_end->begin(current_year);
} else {
return none;
}
}
bool is_within(const date_t& date,
const optional_year& current_year = none) const {
optional<date_t> b = begin(current_year);
optional<date_t> e = end(current_year);
bool after_begin = b ? date >= *b : true;
bool before_end = e ? date < *e : true;
return after_begin && before_end;
}
string to_string() const {
std::ostringstream out;
if (range_begin)
out << "from" << range_begin->to_string();
if (range_end)
out << " to" << range_end->to_string();
return out.str();
}
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
ar & range_begin;
ar & range_end;
ar & end_inclusive;
}
#endif // HAVE_BOOST_SERIALIZATION
};
class date_specifier_or_range_t
{
typedef variant<int, date_specifier_t, date_range_t> value_type;
value_type specifier_or_range;
public:
date_specifier_or_range_t() {
TRACE_CTOR(date_specifier_or_range_t, "");
}
date_specifier_or_range_t(const date_specifier_or_range_t& other)
: specifier_or_range(other.specifier_or_range) {
TRACE_CTOR(date_specifier_or_range_t, "copy");
}
date_specifier_or_range_t(const date_specifier_t& specifier)
: specifier_or_range(specifier) {
TRACE_CTOR(date_specifier_or_range_t, "date_specifier_t");
}
date_specifier_or_range_t(const date_range_t& range)
: specifier_or_range(range) {
TRACE_CTOR(date_specifier_or_range_t, "date_range_t");
}
~date_specifier_or_range_t() throw() {
TRACE_DTOR(date_specifier_or_range_t);
}
optional<date_t> begin(const optional_year& current_year = none) const {
if (specifier_or_range.type() == typeid(date_specifier_t))
return boost::get<date_specifier_t>(specifier_or_range).begin(current_year);
else if (specifier_or_range.type() == typeid(date_range_t))
return boost::get<date_range_t>(specifier_or_range).begin(current_year);
else
return none;
}
optional<date_t> end(const optional_year& current_year = none) const {
if (specifier_or_range.type() == typeid(date_specifier_t))
return boost::get<date_specifier_t>(specifier_or_range).end(current_year);
else if (specifier_or_range.type() == typeid(date_range_t))
return boost::get<date_range_t>(specifier_or_range).end(current_year);
else
return none;
}
string to_string() const {
std::ostringstream out;
if (specifier_or_range.type() == typeid(date_specifier_t))
out << "in" << boost::get<date_specifier_t>(specifier_or_range).to_string();
else if (specifier_or_range.type() == typeid(date_range_t))
out << boost::get<date_range_t>(specifier_or_range).to_string();
return out.str();
}
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
ar & specifier_or_range;
}
#endif // HAVE_BOOST_SERIALIZATION
};
class date_interval_t : public equality_comparable<date_interval_t> class date_interval_t : public equality_comparable<date_interval_t>
{ {
public: public:
struct duration_t static date_t add_duration(const date_t& date,
{ const date_duration_t& duration);
enum skip_quantum_t { static date_t subtract_duration(const date_t& date,
DAYS, WEEKS, MONTHS, YEARS const date_duration_t& duration);
} quantum;
int length;
duration_t() : quantum(DAYS), length(0) { optional<date_specifier_or_range_t> range;
TRACE_CTOR(date_interval_t::duration_t, "");
}
duration_t(skip_quantum_t _quantum, int _length)
: quantum(_quantum), length(_length) {
TRACE_CTOR(date_interval_t::duration_t, "skip_quantum_t, int");
}
duration_t(const duration_t& dur)
: quantum(dur.quantum), length(dur.length) {
TRACE_CTOR(date_interval_t::duration_t, "copy");
}
~duration_t() throw() {
TRACE_DTOR(date_interval_t::duration_t);
}
date_t add(const date_t& date) const { optional<date_t> start; // the real start, after adjustment
switch (quantum) { optional<date_t> finish; // the real end, likewise
case DAYS: bool aligned;
return date + gregorian::days(length); optional<date_t> next;
case WEEKS: optional<date_duration_t> duration;
return date + gregorian::weeks(length); optional<date_t> end_of_duration;
case MONTHS:
return date + gregorian::months(length);
case YEARS:
return date + gregorian::years(length);
default:
assert(false); return date_t();
}
}
date_t subtract(const date_t& date) const { explicit date_interval_t() : aligned(false) {
switch (quantum) {
case DAYS:
return date - gregorian::days(length);
case WEEKS:
return date - gregorian::weeks(length);
case MONTHS:
return date - gregorian::months(length);
case YEARS:
return date - gregorian::years(length);
default:
assert(false); return date_t();
}
}
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
ar & quantum;
ar & length;
}
#endif // HAVE_BOOST_SERIALIZATION
};
static date_t add_duration(const date_t& date,
const duration_t& duration);
static date_t subtract_duration(const date_t& date,
const duration_t& duration);
optional<date_t> start;
bool aligned;
optional<duration_t> skip_duration;
std::size_t factor;
optional<date_t> next;
optional<duration_t> duration;
optional<date_t> end_of_duration;
optional<date_t> end;
explicit date_interval_t() : aligned(false), factor(1) {
TRACE_CTOR(date_interval_t, ""); TRACE_CTOR(date_interval_t, "");
} }
date_interval_t(const string& str) : aligned(false), factor(1) { date_interval_t(const string& str) : aligned(false) {
TRACE_CTOR(date_interval_t, "const string&"); TRACE_CTOR(date_interval_t, "const string&");
parse(str); parse(str);
} }
date_interval_t(const date_interval_t& other) date_interval_t(const date_interval_t& other)
: start(other.start), : range(other.range),
start(other.start),
finish(other.finish),
aligned(other.aligned), aligned(other.aligned),
skip_duration(other.skip_duration),
factor(other.factor),
next(other.next), next(other.next),
duration(other.duration), duration(other.duration),
end_of_duration(other.end_of_duration), end_of_duration(other.end_of_duration) {
end(other.end) {
TRACE_CTOR(date_interval_t, "copy"); TRACE_CTOR(date_interval_t, "copy");
} }
~date_interval_t() throw() { ~date_interval_t() throw() {
@ -252,17 +569,19 @@ public:
return is_valid(); return is_valid();
} }
void parse(std::istream& in); optional<date_t> begin(const optional_year& current_year = none) const {
return start ? start : (range ? range->begin(current_year) : none);
void parse(const string& str) { }
std::istringstream in(str); optional<date_t> end(const optional_year& current_year = none) const {
parse(in); return finish ? finish : (range ? range->end(current_year) : none);
} }
void resolve_end(); void parse(const string& str);
void stabilize(const optional<date_t>& date = none);
bool is_valid() const { void resolve_end();
void stabilize(const optional<date_t>& date = none);
bool is_valid() const {
return start; return start;
} }
@ -280,6 +599,8 @@ public:
date_interval_t& operator++(); date_interval_t& operator++();
void dump(std::ostream& out, optional_year current_year = none);
#if defined(HAVE_BOOST_SERIALIZATION) #if defined(HAVE_BOOST_SERIALIZATION)
private: private:
/** Serialization. */ /** Serialization. */
@ -288,14 +609,13 @@ private:
template<class Archive> template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) { void serialize(Archive& ar, const unsigned int /* version */) {
ar & range;
ar & start; ar & start;
ar & finish;
ar & aligned; ar & aligned;
ar & skip_duration;
ar & factor;
ar & next; ar & next;
ar & duration; ar & duration;
ar & end_of_duration; ar & end_of_duration;
ar & end;
} }
#endif // HAVE_BOOST_SERIALIZATION #endif // HAVE_BOOST_SERIALIZATION
}; };
@ -303,8 +623,9 @@ private:
void times_initialize(); void times_initialize();
void times_shutdown(); void times_shutdown();
std::ostream& operator<<(std::ostream& out, void show_period_tokens(std::ostream& out, const string& arg);
const date_interval_t::duration_t& duration);
std::ostream& operator<<(std::ostream& out, const date_duration_t& duration);
} // namespace ledger } // namespace ledger

View file

@ -206,11 +206,12 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
length++; length++;
date_interval_t timespan(buf); date_interval_t timespan(buf);
if (! timespan) optional<date_t> begin = timespan.begin();
if (! begin)
throw_(parse_error, throw_(parse_error,
_("Date specifier does not refer to a starting date")); _("Date specifier does not refer to a starting date"));
kind = VALUE; kind = VALUE;
value = *timespan.start; value = *begin;
break; break;
} }

View file

@ -194,6 +194,11 @@ public:
string(const string& str); string(const string& str);
string(const std::string& str); string(const std::string& str);
string(size_type len, char x); string(size_type len, char x);
template<class _InputIterator>
string(_InputIterator __beg, _InputIterator __end)
: std::string(__beg, __end) {
TRACE_CTOR(string, "InputIterator, InputIterator");
}
string(const char * str); string(const char * str);
string(const char * str, const char * end); string(const char * str, const char * end);
string(const string& str, size_type x); string(const string& str, size_type x);

View file

@ -338,10 +338,13 @@ value_t& value_t::operator+=(const value_t& val)
case DATETIME: case DATETIME:
switch (val.type()) { switch (val.type()) {
case INTEGER: case INTEGER:
as_datetime_lval() += date_duration(val.as_long()); as_datetime_lval() +=
time_duration_t(0, 0, static_cast<time_duration_t::sec_type>(val.as_long()));
return *this; return *this;
case AMOUNT: case AMOUNT:
as_datetime_lval() += date_duration(val.as_amount().to_long()); as_datetime_lval() +=
time_duration_t(0, 0, static_cast<time_duration_t::sec_type>
(val.as_amount().to_long()));
return *this; return *this;
default: default:
break; break;
@ -351,10 +354,10 @@ value_t& value_t::operator+=(const value_t& val)
case DATE: case DATE:
switch (val.type()) { switch (val.type()) {
case INTEGER: case INTEGER:
as_date_lval() += date_duration_t(val.as_long()); as_date_lval() += gregorian::date_duration(val.as_long());
return *this; return *this;
case AMOUNT: case AMOUNT:
as_date_lval() += date_duration_t(val.as_amount().to_long()); as_date_lval() += gregorian::date_duration(val.as_amount().to_long());
return *this; return *this;
default: default:
break; break;
@ -466,10 +469,13 @@ value_t& value_t::operator-=(const value_t& val)
case DATETIME: case DATETIME:
switch (val.type()) { switch (val.type()) {
case INTEGER: case INTEGER:
as_datetime_lval() -= date_duration(val.as_long()); as_datetime_lval() -=
time_duration_t(0, 0, static_cast<time_duration_t::sec_type>(val.as_long()));
return *this; return *this;
case AMOUNT: case AMOUNT:
as_datetime_lval() -= date_duration(val.as_amount().to_long()); as_datetime_lval() -=
time_duration_t(0, 0, static_cast<time_duration_t::sec_type>
(val.as_amount().to_long()));
return *this; return *this;
default: default:
break; break;
@ -479,10 +485,10 @@ value_t& value_t::operator-=(const value_t& val)
case DATE: case DATE:
switch (val.type()) { switch (val.type()) {
case INTEGER: case INTEGER:
as_date_lval() -= date_duration_t(val.as_long()); as_date_lval() -= gregorian::date_duration(val.as_long());
return *this; return *this;
case AMOUNT: case AMOUNT:
as_date_lval() -= date_duration_t(val.as_amount().to_long()); as_date_lval() -= gregorian::date_duration(val.as_amount().to_long());
return *this; return *this;
default: default:
break; break;
@ -1187,6 +1193,13 @@ void value_t::in_place_negate()
case BALANCE: case BALANCE:
as_balance_lval().in_place_negate(); as_balance_lval().in_place_negate();
return; return;
case SEQUENCE: {
value_t temp;
foreach (const value_t& value, as_sequence())
temp.push_back(- value);
*this = temp;
return;
}
default: default:
break; break;
} }
@ -1216,6 +1229,13 @@ void value_t::in_place_not()
case STRING: case STRING:
set_boolean(as_string().empty()); set_boolean(as_string().empty());
return; return;
case SEQUENCE: {
value_t temp;
foreach (const value_t& value, as_sequence())
temp.push_back(! value);
*this = temp;
return;
}
default: default:
break; break;
} }

View file

@ -0,0 +1,20 @@
bal -V --unrealized
<<<
2008/10/01 Sample
Assets:Brokerage 10 AAPL
Assets:Checking $-200.00
P 2008/10/20 12:00:00 AAPL $30.00
; 2008/10/20 <Generated Transaction>
; Assets:Brokerage $100
; Equity:Unrealized Gains
>>>1
$100.00 Assets
$300.00 Brokerage
$-200.00 Checking
$-100.00 Equity:Unrealized Gains
--------------------
0
>>>2
=== 0

View file

@ -1,50 +1,95 @@
period --now=2010/11/01 12/01 period --now=2010/11/01 12/01
<<< <<<
>>>1 >>>1
global details => --- Period expression tokens ---
TOK_DATE: month Dec day 1
END_REACHED: <EOF>
start: 09-Dec-01 --- Before stabilization ---
end: 09-Dec-02 range: in month Dec day 1
factor: 1
--- After stabilization ---
range: in month Dec day 1
start: 10-Dec-01
finish: 10-Dec-02
--- Sample dates in range (max. 20) ---
1: 10-Dec-01
>>>2 >>>2
=== 0 === 0
period --now=2010/11/01 10/01 period --now=2010/11/01 10/01
<<< <<<
>>>1 >>>1
global details => --- Period expression tokens ---
TOK_DATE: month Oct day 1
END_REACHED: <EOF>
--- Before stabilization ---
range: in month Oct day 1
--- After stabilization ---
range: in month Oct day 1
start: 10-Oct-01 start: 10-Oct-01
end: 10-Oct-02 finish: 10-Oct-02
factor: 1
--- Sample dates in range (max. 20) ---
1: 10-Oct-01
>>>2 >>>2
=== 0 === 0
period --now=2010/11/01 2009/10 period --now=2010/11/01 2009/10
<<< <<<
>>>1 >>>1
global details => --- Period expression tokens ---
TOK_DATE: year 2009 month Oct
END_REACHED: <EOF>
--- Before stabilization ---
range: in year 2009 month Oct
--- After stabilization ---
range: in year 2009 month Oct
start: 09-Oct-01 start: 09-Oct-01
end: 09-Nov-01 finish: 09-Nov-01
factor: 1
--- Sample dates in range (max. 20) ---
1: 09-Oct-01
>>>2 >>>2
=== 0 === 0
period --now=2010/11/01 2009/10/01 period --now=2010/11/01 2009/10/01
<<< <<<
>>>1 >>>1
global details => --- Period expression tokens ---
TOK_DATE: year 2009 month Oct day 1
END_REACHED: <EOF>
--- Before stabilization ---
range: in year 2009 month Oct day 1
--- After stabilization ---
range: in year 2009 month Oct day 1
start: 09-Oct-01 start: 09-Oct-01
end: 09-Oct-02 finish: 09-Oct-02
factor: 1
--- Sample dates in range (max. 20) ---
1: 09-Oct-01
>>>2 >>>2
=== 0 === 0
period --now=2010/11/01 2009 period --now=2010/11/01 2009
<<< <<<
>>>1 >>>1
global details => --- Period expression tokens ---
TOK_A_YEAR: 2009
END_REACHED: <EOF>
--- Before stabilization ---
range: in year 2009
--- After stabilization ---
range: in year 2009
start: 09-Jan-01 start: 09-Jan-01
end: 10-Jan-01 finish: 10-Jan-01
factor: 1
--- Sample dates in range (max. 20) ---
1: 09-Jan-01
>>>2 >>>2
=== 0 === 0

View file

@ -1,10 +1,20 @@
period june 2008 period june 2008
<<< <<<
>>>1 >>>1
global details => --- Period expression tokens ---
TOK_A_MONTH: Jun
TOK_A_YEAR: 2008
END_REACHED: <EOF>
--- Before stabilization ---
range: in year 2008 month Jun
--- After stabilization ---
range: in year 2008 month Jun
start: 08-Jun-01 start: 08-Jun-01
end: 08-Jul-01 finish: 08-Jul-01
factor: 1
--- Sample dates in range (max. 20) ---
1: 08-Jun-01
>>>2 >>>2
=== 0 === 0