From baad7272c7d263dd34076760a6a4d98676ec270e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:28:54 -0400 Subject: [PATCH 01/26] Minor whitespace fix --- src/textual.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/textual.cc b/src/textual.cc index 9a4fee8e3..757a054d2 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -158,9 +158,9 @@ namespace { xact_base_t& xact, const bool defer_expr = false); - xact_t * parse_xact(char * line, - std::streamsize len, - account_t * account); + xact_t * parse_xact(char * line, + std::streamsize len, + account_t * account); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); From c558613ae4dc40ae1cc438b4ba05292b8fc3b9c1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:29:08 -0400 Subject: [PATCH 02/26] Fixed a spelling error --- src/textual.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/textual.cc b/src/textual.cc index 757a054d2..a2801fd5e 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -546,11 +546,9 @@ void instance_t::automated_xact_directive(char * line) ae.release(); } - - } catch (const std::exception& err) { if (reveal_context) { - add_error_context(_("While parsing periodic transaction:")); + add_error_context(_("While parsing automated transaction:")); add_error_context(source_context(pathname, pos, curr_pos, "> ")); } throw; From 1ced1f3d83927afb552d62a59ec54c6687d66c2a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:29:22 -0400 Subject: [PATCH 03/26] Reformatted a comment --- src/textual.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/textual.cc b/src/textual.cc index a2801fd5e..2e8b011f9 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -611,9 +611,9 @@ void instance_t::xact_directive(char * line, std::streamsize len) manager.release(); // it's owned by the journal now context.count++; } - // It's perfectly valid for the journal to reject the xact, which it will - // do if the xact has no substantive effect (for example, a checking - // xact, all of whose postings have null amounts). + // It's perfectly valid for the journal to reject the xact, which it + // will do if the xact has no substantive effect (for example, a + // checking xact, all of whose postings have null amounts). } else { throw parse_error(_("Failed to parse transaction")); } From 9edeb4ba11458374b3a0756710c1e3adc97c28e6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:30:03 -0400 Subject: [PATCH 04/26] Moved value.cc into libexpr --- tools/Makefile.am | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index c38f529ce..5b032f72b 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -28,7 +28,6 @@ libledger_util_la_CPPFLAGS = $(lib_cppflags) libledger_util_la_LDFLAGS = -release $(VERSION) libledger_math_la_SOURCES = \ - src/value.cc \ src/balance.cc \ src/quotes.cc \ src/pool.cc \ @@ -49,7 +48,8 @@ libledger_expr_la_SOURCES = \ src/expr.cc \ src/op.cc \ src/parser.cc \ - src/token.cc + src/token.cc \ + src/value.cc libledger_expr_la_CPPFLAGS = $(lib_cppflags) libledger_expr_la_LDFLAGS = -release $(VERSION) @@ -107,8 +107,8 @@ pkginclude_HEADERS = \ src/pool.h \ src/quotes.h \ src/balance.h \ - src/value.h \ \ + src/value.h \ src/token.h \ src/parser.h \ src/op.h \ From 9effdb3aa2bfcad9dc391cbe5a15b9f563505b00 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:30:33 -0400 Subject: [PATCH 05/26] Option --input-date-format adds to candidate list --- src/times.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/times.cc b/src/times.cc index 26e1dc215..a9768f4fa 100644 --- a/src/times.cc +++ b/src/times.cc @@ -191,7 +191,7 @@ namespace { shared_ptr printed_datetime_io; shared_ptr printed_date_io; - std::vector > readers; + std::deque > readers; date_t parse_date_mask_routine(const char * date_str, date_io_t& io, optional_year year, @@ -1533,7 +1533,7 @@ void set_date_format(const char * format) void set_input_date_format(const char * format) { - input_date_io.reset(new date_io_t(format, true)); + readers.push_front(shared_ptr(new date_io_t(format, true))); } void times_initialize() From 15bf3ed39ed24bc55abc624757a1a943ebabb085 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:31:55 -0400 Subject: [PATCH 06/26] account(NAME) function can lookup account objects For example, just the word "account" returns the name of the current posting's account, but account("Expenses:Food") returns the actual account object, so that it's total may be accessed. --- src/account.cc | 21 ++++++++++++++++++--- src/post.cc | 17 ++++++++++++++++- src/session.cc | 27 +++++++++++++++++++++++++-- src/session.h | 2 ++ 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/account.cc b/src/account.cc index 4170a4d29..710cabe12 100644 --- a/src/account.cc +++ b/src/account.cc @@ -182,8 +182,23 @@ namespace { env.get(0) : false)); } - value_t get_account(account_t& account) { // this gets the name - return string_value(account.fullname()); + value_t get_account(call_scope_t& scope) { // this gets the name + interactive_t args(scope, "&v"); + account_t& account(find_scope(scope)); + if (args.has(0)) { + account_t * acct = account.parent; + for (; acct && acct->parent; acct = acct->parent) ; + if (scope[0].is_string()) + return value_t(static_cast + (acct->find_account(args.get(0), false))); + else if (scope[0].is_mask()) + return value_t(static_cast + (acct->find_account_re(args.get(0).str()))); + else + return NULL_VALUE; + } else { + return string_value(account.fullname()); + } } value_t get_account_base(account_t& account) { @@ -301,7 +316,7 @@ expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, if (name[1] == '\0' || name == "amount") return WRAP_FUNCTOR(get_wrapper<&get_amount>); else if (name == "account") - return WRAP_FUNCTOR(get_wrapper<&get_account>); + return WRAP_FUNCTOR(&get_account); else if (name == "account_base") return WRAP_FUNCTOR(get_wrapper<&get_account_base>); else if (name == "addr") diff --git a/src/post.cc b/src/post.cc index fa8bab4ff..dd489cbaa 100644 --- a/src/post.cc +++ b/src/post.cc @@ -318,7 +318,22 @@ namespace { value_t get_account(call_scope_t& scope) { - return account_name(scope); + interactive_t args(scope, "&v"); + account_t& account(*find_scope(scope).account); + if (args.has(0)) { + account_t * acct = account.parent; + for (; acct && acct->parent; acct = acct->parent) ; + if (scope[0].is_string()) + return value_t(static_cast + (acct->find_account(args.get(0), false))); + else if (scope[0].is_mask()) + return value_t(static_cast + (acct->find_account_re(args.get(0).str()))); + else + return NULL_VALUE; + } else { + return account_name(scope); + } } value_t get_account_id(post_t& post) { diff --git a/src/session.cc b/src/session.cc index 8adfef380..34cda5efe 100644 --- a/src/session.cc +++ b/src/session.cc @@ -182,6 +182,19 @@ void session_t::close_journal_files() amount_t::initialize(); } +value_t session_t::fn_account(call_scope_t& scope) +{ + interactive_t args(scope, "v"); + if (scope[0].is_string()) + return value_t(static_cast + (journal->find_account(args.get(0), false))); + else if (scope[0].is_mask()) + return value_t(static_cast + (journal->find_account_re(args.get(0).str()))); + else + return NULL_VALUE; +} + option_t * session_t::lookup_option(const char * p) { switch (*p) { @@ -224,15 +237,25 @@ option_t * session_t::lookup_option(const char * p) expr_t::ptr_op_t session_t::lookup(const symbol_t::kind_t kind, const string& name) { + const char * p = name.c_str(); + switch (kind) { case symbol_t::FUNCTION: + switch (*p) { + case 'a': + if (is_eq(p, "account")) + return MAKE_FUNCTOR(session_t::fn_account); + break; + default: + break; + } // Check if they are trying to access an option's setting or value. - if (option_t * handler = lookup_option(name.c_str())) + if (option_t * handler = lookup_option(p)) return MAKE_OPT_FUNCTOR(session_t, handler); break; case symbol_t::OPTION: - if (option_t * handler = lookup_option(name.c_str())) + if (option_t * handler = lookup_option(p)) return MAKE_OPT_HANDLER(session_t, handler); break; diff --git a/src/session.h b/src/session.h index 4968f1b89..5130e3fba 100644 --- a/src/session.h +++ b/src/session.h @@ -75,6 +75,8 @@ class session_t : public symbol_scope_t void read_journal_files(); void close_journal_files(); + value_t fn_account(call_scope_t& scope); + void report_options(std::ostream& out) { HANDLER(cache_).report(out); From 5ec85538d9690634a2b1ee7c422e109700eb0627 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:33:41 -0400 Subject: [PATCH 07/26] account_t::add_post resets family_details.gathered --- src/account.cc | 14 ++++++++++++++ src/account.h | 4 +--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/account.cc b/src/account.cc index 710cabe12..33b1ebab6 100644 --- a/src/account.cc +++ b/src/account.cc @@ -122,6 +122,20 @@ account_t * account_t::find_account_re(const string& regexp) return find_account_re_(this, mask_t(regexp)); } +void account_t::add_post(post_t * post) +{ + posts.push_back(post); + + // Adding a new post changes the possible totals that may have been + // computed before. + if (xdata_) { + xdata_->self_details.gathered = false; + xdata_->self_details.calculated = false; + xdata_->family_details.gathered = false; + xdata_->family_details.calculated = false; + } +} + bool account_t::remove_post(post_t * post) { assert(! posts.empty()); diff --git a/src/account.h b/src/account.h index d22d2bb32..26ebe2610 100644 --- a/src/account.h +++ b/src/account.h @@ -119,9 +119,7 @@ class account_t : public supports_flags<>, public scope_t (accounts.end(), bind(&accounts_map::value_type::second, _1)); } - void add_post(post_t * post) { - posts.push_back(post); - } + void add_post(post_t * post); bool remove_post(post_t * post); posts_list::iterator posts_begin() { From 8d2fce1ae8a1b8c3cce4e22d807421915a0001ce Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:39:28 -0400 Subject: [PATCH 08/26] Automated xacts may now contain "deferred tags" For example, consider the following automated transaction: = /Food/ ; Next Date:: date + 10 (Expenses:Tax) 1.00 ; Next Date:: date + 20 This will add a metadata field named 'Next Date' to the _matching posting_, with a value that is 10 days later than that posting. It will also generate a new posting for that transaction, whose amount is the same as the matching posting. Further, it will add a 'Next Date' metadata tag to the _generated posting_ whose value is 20 days later than the date of the matching posting. --- src/journal.cc | 10 ++++++---- src/journal.h | 6 ++++-- src/textual.cc | 6 +++--- src/xact.cc | 26 ++++++++++++++++++++++++-- src/xact.h | 28 +++++++++++++++++++++++++++- 5 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/journal.cc b/src/journal.cc index fd6d3eac7..ed1e26be6 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -105,7 +105,8 @@ account_t * journal_t::find_account_re(const string& regexp) return master->find_account_re(regexp); } -bool journal_t::add_xact(xact_t * xact) +bool journal_t::add_xact(xact_t * xact, + optional current_year) { xact->journal = this; @@ -114,16 +115,17 @@ bool journal_t::add_xact(xact_t * xact) return false; } - extend_xact(xact); + extend_xact(xact, current_year); xacts.push_back(xact); return true; } -void journal_t::extend_xact(xact_base_t * xact) +void journal_t::extend_xact(xact_base_t * xact, + optional current_year) { foreach (auto_xact_t * auto_xact, auto_xacts) - auto_xact->extend_xact(*xact); + auto_xact->extend_xact(*xact, current_year); } bool journal_t::remove_xact(xact_t * xact) diff --git a/src/journal.h b/src/journal.h index ca6b6e4f4..183d074d9 100644 --- a/src/journal.h +++ b/src/journal.h @@ -140,8 +140,10 @@ class journal_t : public noncopyable account_t * find_account(const string& name, bool auto_create = true); account_t * find_account_re(const string& regexp); - bool add_xact(xact_t * xact); - void extend_xact(xact_base_t * xact); + bool add_xact(xact_t * xact, + optional current_year = none); + void extend_xact(xact_base_t * xact, + optional current_year = none); bool remove_xact(xact_t * xact); xacts_list::iterator xacts_begin() { diff --git a/src/textual.cc b/src/textual.cc index 2e8b011f9..4d133302d 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -577,7 +577,7 @@ void instance_t::period_xact_directive(char * line) pe->journal = &context.journal; if (pe->finalize()) { - context.journal.extend_xact(pe.get()); + context.journal.extend_xact(pe.get(), current_year); context.journal.period_xacts.push_back(pe.get()); pe->pos->end_pos = curr_pos; @@ -1254,7 +1254,7 @@ post_t * instance_t::parse_post(char * line, foreach (const state_t& state, context.state_stack) if (state.type() == typeid(string)) post->parse_tags(boost::get(state).c_str(), context.scope, - true); + true, current_year); } TRACE_STOP(post_details, 1); @@ -1426,7 +1426,7 @@ xact_t * instance_t::parse_xact(char * line, foreach (const state_t& state, context.state_stack) if (state.type() == typeid(string)) xact->parse_tags(boost::get(state).c_str(), context.scope, - false); + false, current_year); } TRACE_STOP(xact_details, 1); diff --git a/src/xact.cc b/src/xact.cc index 3b66598c9..77d87ae95 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -634,7 +634,8 @@ namespace { } // unnamed namespace -void auto_xact_t::extend_xact(xact_base_t& xact) +void auto_xact_t::extend_xact(xact_base_t& xact, + optional current_year) { posts_list initial_posts(xact.posts.begin(), xact.posts.end()); @@ -679,6 +680,18 @@ void auto_xact_t::extend_xact(xact_base_t& xact) matches_predicate = predicate(*initial_post); } if (matches_predicate) { + bind_scope_t bound_scope(*scope_t::default_scope, *initial_post); + + if (deferred_notes) { + foreach (deferred_tag_data_t& data, *deferred_notes) { + if (data.apply_to_post == NULL) + initial_post->parse_tags(data.tag_data.c_str(), + bound_scope, + data.overwrite_existing, + current_year); + } + } + foreach (post_t * post, posts) { amount_t post_amount; if (post->amount.is_null()) { @@ -686,7 +699,6 @@ void auto_xact_t::extend_xact(xact_base_t& xact) throw_(amount_error, _("Automated transaction's posting has no amount")); - bind_scope_t bound_scope(*scope_t::default_scope, *initial_post); value_t result(post->amount_expr->calc(bound_scope)); if (result.is_long()) { post_amount = result.to_amount(); @@ -752,6 +764,16 @@ void auto_xact_t::extend_xact(xact_base_t& xact) if (new_post->must_balance()) needs_further_verification = true; + + if (deferred_notes) { + foreach (deferred_tag_data_t& data, *deferred_notes) { + if (data.apply_to_post == post) + new_post->parse_tags(data.tag_data.c_str(), + bound_scope, + data.overwrite_existing, + current_year); + } + } } } } diff --git a/src/xact.h b/src/xact.h index 2c3c7d053..407eed57d 100644 --- a/src/xact.h +++ b/src/xact.h @@ -150,6 +150,21 @@ class auto_xact_t : public xact_base_t std::map memoized_results; + struct deferred_tag_data_t { + string tag_data; + bool overwrite_existing; + post_t * apply_to_post; + + deferred_tag_data_t(string _tag_data, + bool _overwrite_existing) + : tag_data(_tag_data), overwrite_existing(_overwrite_existing), + apply_to_post(NULL) {} + }; + + typedef std::list deferred_notes_list; + + optional deferred_notes; + auto_xact_t() : try_quick_match(true) { TRACE_CTOR(auto_xact_t, ""); } @@ -168,7 +183,17 @@ class auto_xact_t : public xact_base_t TRACE_DTOR(auto_xact_t); } - virtual void extend_xact(xact_base_t& xact); + virtual void parse_tags(const char * p, + scope_t&, + bool overwrite_existing = true, + optional = none) { + if (! deferred_notes) + deferred_notes = deferred_notes_list(); + deferred_notes->push_back(deferred_tag_data_t(p, overwrite_existing)); + } + + virtual void extend_xact(xact_base_t& xact, + optional current_year); #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -180,6 +205,7 @@ class auto_xact_t : public xact_base_t void serialize(Archive& ar, const unsigned int /* version */) { ar & boost::serialization::base_object(*this); ar & predicate; + ar & deferred_notes; } #endif // HAVE_BOOST_SERIALIZATION }; From 6f56a7443fea165c73d00029437530d567556994 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:39:36 -0400 Subject: [PATCH 09/26] Removed an unused function --- src/xact.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/xact.cc b/src/xact.cc index 77d87ae95..eeb487d97 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -789,13 +789,6 @@ void auto_xact_t::extend_xact(xact_base_t& xact, } } -void extend_xact_base(journal_t * journal, - xact_base_t& base) -{ - foreach (auto_xact_t * xact, journal->auto_xacts) - xact->extend_xact(base); -} - void to_xml(std::ostream& out, const xact_t& xact) { push_xml x(out, "transaction", true, true); From 6ef755c1330c6e11476ea2e63b8388ad29efb4ef Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 21:43:03 -0400 Subject: [PATCH 10/26] Added support for assert, check and expr directives These can occur in many places: ; Within an automated transaction, the assert is evaluated every time ; a posting is matched, with the expression context set to the ; matching posting. = /Food/ assert account("Expenses:Food").total >= $100 2010-06-12 Sample Expenses:Food $100 Assets:Checking ; At file scope, the expression is evaluated with "global" scope. assert account("Expenses:Food").total == $100 ; At the top of a transction, the assertion's scope is the ; transaction. After a posting, the scope is that posting. Note ; however that account totals are only adjusted after successful ; parsing of a transaction, which means that all the assertions below ; are true, even though it appears as though the middle posting should ; affect the total immediately (which is not the case). 2010-06-12 Sample 2 assert account("Expenses:Food").total == $100 Expenses:Food $50 assert account("Expenses:Food").total == $100 Assets:Checking assert account("Expenses:Food").total == $100 --- src/textual.cc | 152 +++++++++++++++++++++++++++++++++++++++++++------ src/xact.cc | 15 +++++ src/xact.h | 12 ++++ 3 files changed, 161 insertions(+), 18 deletions(-) diff --git a/src/textual.cc b/src/textual.cc index 4d133302d..113bafe80 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -146,6 +146,9 @@ namespace { void account_mapping_directive(char * line); void tag_directive(char * line); void define_directive(char * line); + void assert_directive(char * line); + void check_directive(char * line); + void expr_directive(char * line); bool general_directive(char * line); post_t * parse_post(char * line, @@ -523,20 +526,74 @@ void instance_t::automated_xact_directive(char * line) bool reveal_context = true; try { + std::auto_ptr ae + (new auto_xact_t(query_t(string(skip_ws(line + 1)), + keep_details_t(true, true, true), false))); + ae->pos = position_t(); + ae->pos->pathname = pathname; + ae->pos->beg_pos = line_beg_pos; + ae->pos->beg_line = linenum; + ae->pos->sequence = context.sequence++; + + post_t * last_post = NULL; + + while (peek_whitespace_line()) { + std::streamsize len = read_line(line); + + char * p = skip_ws(line); + if (! *p) + break; - std::auto_ptr ae - (new auto_xact_t(query_t(string(skip_ws(line + 1)), - keep_details_t(true, true, true), false))); - ae->pos = position_t(); - ae->pos->pathname = pathname; - ae->pos->beg_pos = line_beg_pos; - ae->pos->beg_line = linenum; - ae->pos->sequence = context.sequence++; + const std::size_t remlen = std::strlen(p); - reveal_context = false; + if (*p == ';') { + item_t * item; + if (last_post) + item = last_post; + else + item = ae.get(); - if (parse_posts(context.top_account(), *ae.get(), true)) { - reveal_context = true; + // This is a trailing note, and possibly a metadata info tag + item->append_note(p + 1, context.scope, true, current_year); + item->pos->end_pos = curr_pos; + item->pos->end_line++; + + // If there was no last_post yet, then deferred notes get applied to + // the matched posting. Other notes get applied to the auto-generated + // posting. + ae->deferred_notes->back().apply_to_post = last_post; + } + else if ((remlen > 7 && *p == 'a' && + std::strncmp(p, "assert", 6) == 0 && std::isspace(p[6])) || + (remlen > 6 && *p == 'c' && + std::strncmp(p, "check", 5) == 0 && std::isspace(p[5])) || + (remlen > 5 && *p == 'e' && + std::strncmp(p, "expr", 4) == 0 && std::isspace(p[4]))) { + const char c = *p; + p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]); + if (! ae->check_exprs) + ae->check_exprs = auto_xact_t::check_expr_list(); + ae->check_exprs->push_back + (auto_xact_t::check_expr_pair(expr_t(p), + c == 'a' ? + auto_xact_t::EXPR_ASSERTION : + (c == 'c' ? + auto_xact_t::EXPR_CHECK : + auto_xact_t::EXPR_GENERAL))); + } + else { + reveal_context = false; + + if (post_t * post = + parse_post(p, len - (p - line), context.top_account(), + NULL, true)) { + reveal_context = true; + ae->add_post(post); + last_post = post; + } + reveal_context = true; + } + } context.journal.auto_xacts.push_back(ae.get()); @@ -585,6 +642,7 @@ void instance_t::period_xact_directive(char * line) pe.release(); } else { + reveal_context = true; pe->journal = NULL; throw parse_error(_("Period transaction failed to balance")); } @@ -823,6 +881,26 @@ void instance_t::define_directive(char * line) def.compile(context.scope); // causes definitions to be established } +void instance_t::assert_directive(char * line) +{ + expr_t expr(line); + if (! expr.calc(context.scope).to_boolean()) + throw_(parse_error, _("Assertion failed: %1" << line)); +} + +void instance_t::check_directive(char * line) +{ + expr_t expr(line); + if (! expr.calc(context.scope).to_boolean()) + warning_(_("Check failed: %1" << line)); +} + +void instance_t::expr_directive(char * line) +{ + expr_t expr(line); + expr.calc(context.scope); +} + bool instance_t::general_directive(char * line) { char buf[8192]; @@ -845,6 +923,10 @@ bool instance_t::general_directive(char * line) alias_directive(arg); return true; } + else if (std::strcmp(p, "assert") == 0) { + assert_directive(arg); + return true; + } break; case 'b': @@ -859,6 +941,10 @@ bool instance_t::general_directive(char * line) account_mapping_directive(arg); return true; } + else if (std::strcmp(p, "check") == 0) { + check_directive(arg); + return true; + } break; case 'd': @@ -873,6 +959,10 @@ bool instance_t::general_directive(char * line) end_directive(arg); return true; } + else if (std::strcmp(p, "expr") == 0) { + expr_directive(arg); + return true; + } break; case 'f': @@ -1383,25 +1473,51 @@ xact_t * instance_t::parse_xact(char * line, if (! *p) break; - if (*p == ';') { - item_t * item; - if (last_post) - item = last_post; - else - item = xact.get(); + const std::size_t remlen = std::strlen(p); + + item_t * item; + if (last_post) + item = last_post; + else + item = xact.get(); + if (*p == ';') { // This is a trailing note, and possibly a metadata info tag item->append_note(p + 1, context.scope, true, current_year); item->pos->end_pos = curr_pos; item->pos->end_line++; - } else { + } + else if ((remlen > 7 && *p == 'a' && + std::strncmp(p, "assert", 6) == 0 && std::isspace(p[6])) || + (remlen > 6 && *p == 'c' && + std::strncmp(p, "check", 5) == 0 && std::isspace(p[5])) || + (remlen > 5 && *p == 'e' && + std::strncmp(p, "expr", 4) == 0 && std::isspace(p[4]))) { + const char c = *p; + p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]); + expr_t expr(p); + bind_scope_t bound_scope(context.scope, *item); + if (c == 'e') { + expr.calc(bound_scope); + } + else if (! expr.calc(bound_scope).to_boolean()) { + if (c == 'a') { + throw_(parse_error, _("Transaction assertion failed: %1" << p)); + } else { + warning_(_("Transaction check failed: %1" << p)); + } + } + } + else { reveal_context = false; if (post_t * post = parse_post(p, len - (p - line), account, xact.get())) { + reveal_context = true; xact->add_post(post); last_post = post; } + reveal_context = true; } } diff --git a/src/xact.cc b/src/xact.cc index eeb487d97..0bf1fc2c0 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -691,6 +691,21 @@ void auto_xact_t::extend_xact(xact_base_t& xact, current_year); } } + if (check_exprs) { + foreach (check_expr_pair& pair, *check_exprs) { + if (pair.second == auto_xact_t::EXPR_GENERAL) { + pair.first.calc(bound_scope); + } + else if (! pair.first.calc(bound_scope).to_boolean()) { + if (pair.second == auto_xact_t::EXPR_ASSERTION) { + throw_(parse_error, + _("Transaction assertion failed: %1" << pair.first)); + } else { + warning_(_("Transaction check failed: %1" << pair.first)); + } + } + } + } foreach (post_t * post, posts) { amount_t post_amount; diff --git a/src/xact.h b/src/xact.h index 407eed57d..a3c639b94 100644 --- a/src/xact.h +++ b/src/xact.h @@ -150,6 +150,17 @@ class auto_xact_t : public xact_base_t std::map memoized_results; + enum xact_expr_kind_t { + EXPR_GENERAL, + EXPR_ASSERTION, + EXPR_CHECK + }; + + typedef std::pair check_expr_pair; + typedef std::list check_expr_list; + + optional check_exprs; + struct deferred_tag_data_t { string tag_data; bool overwrite_existing; @@ -205,6 +216,7 @@ class auto_xact_t : public xact_base_t void serialize(Archive& ar, const unsigned int /* version */) { ar & boost::serialization::base_object(*this); ar & predicate; + ar & check_exprs; ar & deferred_notes; } #endif // HAVE_BOOST_SERIALIZATION From 536e3e73228b6168437704ede89499406b87391d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 22:16:38 -0400 Subject: [PATCH 11/26] Added a new scope_value() inline helper function --- src/account.cc | 8 +++----- src/post.cc | 4 ++-- src/report.h | 2 +- src/session.cc | 6 ++---- src/value.h | 4 ++++ 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/account.cc b/src/account.cc index 33b1ebab6..606562b85 100644 --- a/src/account.cc +++ b/src/account.cc @@ -203,11 +203,9 @@ namespace { account_t * acct = account.parent; for (; acct && acct->parent; acct = acct->parent) ; if (scope[0].is_string()) - return value_t(static_cast - (acct->find_account(args.get(0), false))); + return scope_value(acct->find_account(args.get(0), false)); else if (scope[0].is_mask()) - return value_t(static_cast - (acct->find_account_re(args.get(0).str()))); + return scope_value(acct->find_account_re(args.get(0).str())); else return NULL_VALUE; } else { @@ -285,7 +283,7 @@ namespace { } value_t get_parent(account_t& account) { - return value_t(static_cast(account.parent)); + return scope_value(account.parent); } value_t fn_any(call_scope_t& scope) diff --git a/src/post.cc b/src/post.cc index dd489cbaa..da892062d 100644 --- a/src/post.cc +++ b/src/post.cc @@ -126,7 +126,7 @@ optional post_t::effective_date() const namespace { value_t get_this(post_t& post) { - return value_t(static_cast(&post)); + return scope_value(&post); } value_t get_is_calculated(post_t& post) { @@ -146,7 +146,7 @@ namespace { } value_t get_xact(post_t& post) { - return value_t(static_cast(post.xact)); + return scope_value(post.xact); } value_t get_xact_id(post_t& post) { diff --git a/src/report.h b/src/report.h index 082c204b8..00cbec286 100644 --- a/src/report.h +++ b/src/report.h @@ -182,7 +182,7 @@ class report_t : public scope_t } value_t fn_options(call_scope_t&) { - return value_t(static_cast(this)); + return scope_value(this); } string report_format(option_t& option) { diff --git a/src/session.cc b/src/session.cc index 34cda5efe..b5441766d 100644 --- a/src/session.cc +++ b/src/session.cc @@ -186,11 +186,9 @@ value_t session_t::fn_account(call_scope_t& scope) { interactive_t args(scope, "v"); if (scope[0].is_string()) - return value_t(static_cast - (journal->find_account(args.get(0), false))); + return scope_value(journal->find_account(args.get(0), false)); else if (scope[0].is_mask()) - return value_t(static_cast - (journal->find_account_re(args.get(0).str()))); + return scope_value(journal->find_account_re(args.get(0).str())); else return NULL_VALUE; } diff --git a/src/value.h b/src/value.h index 4dfd591c8..5971535c1 100644 --- a/src/value.h +++ b/src/value.h @@ -993,6 +993,10 @@ inline string value_context(const value_t& val) { return buf.str(); } +inline value_t scope_value(scope_t * val) { + return value_t(val); +} + template inline value_t& add_or_set_value(value_t& lhs, const T& rhs) { if (lhs.is_null()) From 1bc5b894dfd9412a60c4db16e9a4e49ddddad5fd Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 22:18:16 -0400 Subject: [PATCH 12/26] Expression evaluations now have a "type context" Thus, an expression can know if the context in which it's being evaluated requires a string, and if so, determine it's output accordingly. For example: account ; returns the full name of the posting's account account.total ; here the context is SCOPE, so account is an obj --- src/account.cc | 6 +++++- src/op.cc | 15 +++++++++------ src/post.cc | 40 ++++++++++++---------------------------- src/scope.h | 31 ++++++++++++++++++++++++++++--- 4 files changed, 54 insertions(+), 38 deletions(-) diff --git a/src/account.cc b/src/account.cc index 606562b85..f26b79130 100644 --- a/src/account.cc +++ b/src/account.cc @@ -208,7 +208,11 @@ namespace { return scope_value(acct->find_account_re(args.get(0).str())); else return NULL_VALUE; - } else { + } + else if (scope.type_context() == value_t::SCOPE) { + return scope_value(&account); + } + else { return string_value(account.fullname()); } } diff --git a/src/op.cc b/src/op.cc index 9497e068b..8f73493df 100644 --- a/src/op.cc +++ b/src/op.cc @@ -39,7 +39,8 @@ namespace ledger { namespace { - value_t split_cons_expr(expr_t::ptr_op_t op, scope_t& scope, + value_t split_cons_expr(expr_t::ptr_op_t op, + scope_t& scope, std::vector& exprs) { value_t seq; @@ -150,7 +151,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) // Evaluating an identifier is the same as calling its definition // directly, so we create an empty call_scope_t to reflect the scope for // this implicit call. - call_scope_t call_args(scope); + call_scope_t call_args(scope, scope.type_context()); result = left()->compile(call_args, depth + 1) ->calc(call_args, locus, depth + 1); break; @@ -160,7 +161,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) // Evaluating a FUNCTION is the same as calling it directly; this happens // when certain functions-that-look-like-variables (such as "amount") are // resolved. - call_scope_t call_args(scope); + call_scope_t call_args(scope, scope.type_context()); result = as_function()(call_args); #if defined(DEBUG_ON) skip_debug = true; @@ -200,8 +201,9 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; } - case O_LOOKUP: - if (value_t obj = left()->calc(scope, locus, depth + 1)) { + case O_LOOKUP: { + context_scope_t context_scope(scope, value_t::SCOPE); + if (value_t obj = left()->calc(context_scope, locus, depth + 1)) { if (obj.is_scope()) { if (obj.as_scope() == NULL) { throw_(calc_error, _("Left operand of . operator is NULL")); @@ -222,10 +224,11 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) throw_(calc_error, _("Failed to lookup member '%1'") << right()->as_ident()); break; + } case O_CALL: case O_EXPAND: { - call_scope_t call_args(scope); + call_scope_t call_args(scope, scope.type_context()); // When evaluating a macro call, these expressions have to live beyond the // call to calc() below. optional > args_expr; diff --git a/src/post.cc b/src/post.cc index da892062d..d4a16122e 100644 --- a/src/post.cc +++ b/src/post.cc @@ -254,20 +254,20 @@ namespace { return 1L; } - value_t account_name(call_scope_t& scope) + value_t get_account(call_scope_t& scope) { in_context_t env(scope, "&v"); - - string name; + account_t& account(*env->reported_account()); + string name; if (env.has(0)) { if (env.value_at(0).is_long()) { if (env.get(0) > 2) - name = format_t::truncate(env->reported_account()->fullname(), + name = format_t::truncate(account.fullname(), env.get(0) - 2, 2 /* account_abbrev_length */); else - name = env->reported_account()->fullname(); + name = account.fullname(); } else { account_t * account = NULL; account_t * master = env->account; @@ -294,8 +294,12 @@ namespace { else return value_t(static_cast(account)); } - } else { - name = env->reported_account()->fullname(); + } + else if (scope.type_context() == value_t::SCOPE) { + return scope_value(&account); + } + else { + name = account.fullname(); } return string_value(name); } @@ -304,7 +308,7 @@ namespace { { in_context_t env(scope, "&v"); - value_t acct = account_name(scope); + value_t acct = get_account(scope); if (acct.is_string()) { if (env->has_flags(POST_VIRTUAL)) { if (env->must_balance()) @@ -316,26 +320,6 @@ namespace { return acct; } - value_t get_account(call_scope_t& scope) - { - interactive_t args(scope, "&v"); - account_t& account(*find_scope(scope).account); - if (args.has(0)) { - account_t * acct = account.parent; - for (; acct && acct->parent; acct = acct->parent) ; - if (scope[0].is_string()) - return value_t(static_cast - (acct->find_account(args.get(0), false))); - else if (scope[0].is_mask()) - return value_t(static_cast - (acct->find_account_re(args.get(0).str()))); - else - return NULL_VALUE; - } else { - return account_name(scope); - } - } - value_t get_account_id(post_t& post) { return static_cast(post.account_id()); } diff --git a/src/scope.h b/src/scope.h index e3dd3e3f9..1b10d7a68 100644 --- a/src/scope.h +++ b/src/scope.h @@ -114,6 +114,10 @@ class scope_t virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name) = 0; + virtual value_t::type_t type_context() const { + return value_t::VOID; + } + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ @@ -205,13 +209,34 @@ class symbol_scope_t : public child_scope_t #endif // HAVE_BOOST_SERIALIZATION }; -class call_scope_t : public child_scope_t +class context_scope_t : public child_scope_t +{ + value_t::type_t value_type_context; + +public: + explicit context_scope_t(scope_t& _parent, + value_t::type_t _type_context = value_t::VOID) + : child_scope_t(_parent), value_type_context(_type_context) { + TRACE_CTOR(context_scope_t, "scope_t&, value_t::type_t"); + } + virtual ~context_scope_t() { + TRACE_DTOR(context_scope_t); + } + + virtual value_t::type_t type_context() const { + return value_type_context; + } +}; + +class call_scope_t : public context_scope_t { value_t args; public: - explicit call_scope_t(scope_t& _parent) : child_scope_t(_parent) { - TRACE_CTOR(call_scope_t, "scope_t&"); + explicit call_scope_t(scope_t& _parent, + value_t::type_t _type_context = value_t::VOID) + : context_scope_t(_parent, _type_context) { + TRACE_CTOR(call_scope_t, "scope_t&, value_t::type_t"); } virtual ~call_scope_t() { TRACE_DTOR(call_scope_t); From dcdd9c4a5630db5eaf9fe20ad391c60dc7209960 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 12 Jun 2010 22:25:10 -0400 Subject: [PATCH 13/26] If a type context exists, confirm the result matches --- src/interactive.cc | 9 ++++----- src/op.cc | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/interactive.cc b/src/interactive.cc index 54c7fd6cf..b95057dd4 100644 --- a/src/interactive.cc +++ b/src/interactive.cc @@ -169,15 +169,14 @@ void interactive_t::verify_arguments() const DEBUG("interactive.verify", "Remaining args are optional"); if (wrong_arg) { - throw_(std::logic_error, - _("Expected %1 for argument %2, but received %3") - << label << offset << vlabel); + throw_(calc_error, _("Expected %1 for argument %2, but received %3") + << label << offset << vlabel); } else if (*p && ! optional && ! next_arg) { - throw_(std::logic_error, _("Too few arguments to function")); + throw_(calc_error, _("Too few arguments to function")); } else if (! *p && next_arg) { - throw_(std::logic_error, _("Too many arguments to function")); + throw_(calc_error, _("Too many arguments to function")); } } diff --git a/src/op.cc b/src/op.cc index 8f73493df..0feb157f5 100644 --- a/src/op.cc +++ b/src/op.cc @@ -68,6 +68,17 @@ namespace { } return seq; } + + void check_type_context(scope_t& scope, value_t& result) + { + if (scope.type_context() != value_t::VOID && + result.type() != scope.type_context()) { + throw_(calc_error, + _("Expected return of %1, but received %2") + << result.label(scope.type_context()) + << result.label()); + } + } } expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) @@ -154,6 +165,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) call_scope_t call_args(scope, scope.type_context()); result = left()->compile(call_args, depth + 1) ->calc(call_args, locus, depth + 1); + check_type_context(scope, result); break; } @@ -163,6 +175,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) // resolved. call_scope_t call_args(scope, scope.type_context()); result = as_function()(call_args); + check_type_context(scope, result); #if defined(DEBUG_ON) skip_debug = true; #endif @@ -256,6 +269,8 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) result = func->as_function()(call_args); else result = func->calc(call_args, locus, depth + 1); + + check_type_context(scope, result); break; } From 0ed85124d9d3cbf607303e31802c96799ea03042 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 03:50:41 -0400 Subject: [PATCH 14/26] Further tweaks to tools/push --- tools/push | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/push b/tools/push index 0c1a019ac..286b32065 100755 --- a/tools/push +++ b/tools/push @@ -11,7 +11,8 @@ git rebase master git push git checkout master ./acprep --enable-cache --universal -j16 --warn opt upload -./acprep --enable-cache --universal -j16 --warn opt make speedtest | \ - tee build/last-speed.txt +cp -p ~/Products/ledger/opt/ledger ~/bin +./acprep --enable-cache --universal -j16 --warn opt make speedtest 2>&1 \ + | tee build/last-speed.txt mv *.dmg* build git checkout next From 0bb9c110624edab2cbde1709d69e38761a7b9fd0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 03:52:20 -0400 Subject: [PATCH 15/26] Changed display order of format exceptions --- src/format.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/format.cc b/src/format.cc index bde398822..ae40e1c35 100644 --- a/src/format.cc +++ b/src/format.cc @@ -362,8 +362,13 @@ string format_t::real_calc(scope_t& scope) out << value.to_string(); } catch (const calc_error&) { + string current_context = error_context(); + add_error_context(_("While calculating format expression:")); add_error_context(expr.context_to_str()); + + if (! current_context.empty()) + add_error_context(current_context); throw; } break; From 0c02b720ef209a26709acfed547da84056b3aea2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 03:53:08 -0400 Subject: [PATCH 16/26] Moved join_args function to precmd.cc --- src/precmd.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/precmd.cc b/src/precmd.cc index f78d766e4..4c9166086 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -43,6 +43,22 @@ namespace ledger { namespace { + string join_args(call_scope_t& args) + { + std::ostringstream buf; + bool first = true; + + for (std::size_t i = 0; i < args.size(); i++) { + if (first) + first = false; + else + buf << ' '; + buf << args[i]; + } + + return buf.str(); + } + post_t * get_sample_xact(report_t& report) { { From f3fa011d39336f9a13ad1f09016e30f88e09ba56 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 03:54:10 -0400 Subject: [PATCH 17/26] Improved support for value_t::in_place* functions --- src/value.cc | 62 ++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/src/value.cc b/src/value.cc index 63e483335..f03b40178 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1304,13 +1304,10 @@ void value_t::in_place_negate() case BALANCE: as_balance_lval().in_place_negate(); return; - case SEQUENCE: { - value_t temp; - foreach (const value_t& value, as_sequence()) - temp.push_back(- value); - *this = temp; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_negate(); return; - } default: break; } @@ -1341,13 +1338,10 @@ void value_t::in_place_not() case STRING: set_boolean(as_string().empty()); return; - case SEQUENCE: { - value_t temp; - foreach (const value_t& value, as_sequence()) - temp.push_back(! value); - *this = temp; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_not(); return; - } default: break; } @@ -1491,6 +1485,10 @@ void value_t::in_place_reduce() case BALANCE: as_balance_lval().in_place_reduce(); return; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_reduce(); + return; default: return; } @@ -1507,6 +1505,10 @@ void value_t::in_place_unreduce() case BALANCE: as_balance_lval().in_place_unreduce(); return; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_unreduce(); + return; default: return; } @@ -1547,13 +1549,10 @@ void value_t::in_place_round() case BALANCE: as_balance_lval().in_place_round(); return; - case SEQUENCE: { - value_t temp; - foreach (const value_t& value, as_sequence()) - temp.push_back(value.rounded()); - *this = temp; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_round(); return; - } default: break; } @@ -1573,13 +1572,10 @@ void value_t::in_place_truncate() case BALANCE: as_balance_lval().in_place_truncate(); return; - case SEQUENCE: { - value_t temp; - foreach (const value_t& value, as_sequence()) - temp.push_back(value.truncated()); - *this = temp; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_truncate(); return; - } default: break; } @@ -1599,13 +1595,10 @@ void value_t::in_place_floor() case BALANCE: as_balance_lval().in_place_floor(); return; - case SEQUENCE: { - value_t temp; - foreach (const value_t& value, as_sequence()) - temp.push_back(value.floored()); - *this = temp; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_floor(); return; - } default: break; } @@ -1625,13 +1618,10 @@ void value_t::in_place_unround() case BALANCE: as_balance_lval().in_place_unround(); return; - case SEQUENCE: { - value_t temp; - foreach (const value_t& value, as_sequence()) - temp.push_back(value.unrounded()); - *this = temp; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_unround(); return; - } default: break; } From 44a5103e1ce9f968df02ac6e1f86fcf9415a1983 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 03:54:36 -0400 Subject: [PATCH 18/26] Moved value_t::label() to value.cc --- src/value.cc | 39 +++++++++++++++++++++++++++++++++++++++ src/value.h | 34 +--------------------------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/value.cc b/src/value.cc index f03b40178..fd49e85c0 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1700,6 +1700,45 @@ value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const return NULL_VALUE; } +string value_t::label(optional the_type) const +{ + switch (the_type ? *the_type : type()) { + case VOID: + return _("an uninitialized value"); + case BOOLEAN: + return _("a boolean"); + case DATETIME: + return _("a date/time"); + case DATE: + return _("a date"); + case INTEGER: + return _("an integer"); + case AMOUNT: + return _("an amount"); + case BALANCE: + return _("a balance"); + case STRING: + return _("a string"); + case MASK: + return _("a regexp"); + case SEQUENCE: + return _("a sequence"); + case SCOPE: + return _("a scope"); + case ANY: + if (as_any().type() == typeid(expr_t::ptr_op_t)) + return _("an expr"); + else + return _("an object"); + break; + default: + assert(false); + break; + } + assert(false); + return _(""); +} + void value_t::print(std::ostream& out, const int first_width, const int latter_width, diff --git a/src/value.h b/src/value.h index 5971535c1..7bd09c9a6 100644 --- a/src/value.h +++ b/src/value.h @@ -904,39 +904,7 @@ class value_t /** * Informational methods. */ - string label(optional the_type = none) const { - switch (the_type ? *the_type : type()) { - case VOID: - return _("an uninitialized value"); - case BOOLEAN: - return _("a boolean"); - case DATETIME: - return _("a date/time"); - case DATE: - return _("a date"); - case INTEGER: - return _("an integer"); - case AMOUNT: - return _("an amount"); - case BALANCE: - return _("a balance"); - case STRING: - return _("a string"); - case MASK: - return _("a regexp"); - case SEQUENCE: - return _("a sequence"); - case SCOPE: - return _("a scope"); - case EXPR: - return _("a expr"); - default: - assert(false); - break; - } - assert(false); - return _(""); - } + string label(optional the_type = none) const; /** * Printing methods. From 4a44218da15c5d4dfb29fc32dfe5818a5ac27b7a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 03:55:02 -0400 Subject: [PATCH 19/26] For value_t, use boost::any to refer to objects --- src/system.hh.in | 1 + src/value.cc | 45 ++++++++++++--------------- src/value.h | 81 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/system.hh.in b/src/system.hh.in index 34efa4244..d9e664e31 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -137,6 +137,7 @@ typedef std::ostream::pos_type ostream_pos_type; #endif #include +#include #include #include #include diff --git a/src/value.cc b/src/value.cc index fd49e85c0..998378329 100644 --- a/src/value.cc +++ b/src/value.cc @@ -116,8 +116,8 @@ value_t::operator bool() const return false; case SCOPE: return as_scope() != NULL; - case EXPR: - return as_expr(); + case ANY: + return ! as_any().empty(); default: break; } @@ -145,12 +145,6 @@ void value_t::set_type(type_t new_type) } } -void value_t::set_expr(const expr_t& val) -{ - set_type(EXPR); - storage->data = new expr_t(val); -} - bool value_t::to_boolean() const { if (is_boolean()) { @@ -1372,8 +1366,8 @@ bool value_t::is_realzero() const case SCOPE: return as_scope() == NULL; - case EXPR: - return ! as_expr(); + case ANY: + return as_any().empty(); default: add_error_context(_("While applying is_realzero to %1:") << *this); @@ -1404,8 +1398,8 @@ bool value_t::is_zero() const case SCOPE: return as_scope() == NULL; - case EXPR: - return ! as_expr(); + case ANY: + return as_any().empty(); default: add_error_context(_("While applying is_zero to %1:") << *this); @@ -1677,7 +1671,7 @@ value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const case STRING: case MASK: case SCOPE: - case EXPR: + case ANY: return *this; case SEQUENCE: { @@ -1825,13 +1819,14 @@ void value_t::print(std::ostream& out, case SCOPE: out << "<#SCOPE>"; break; - case EXPR: - out << "<#EXPR "; - if (as_expr()) - as_expr().print(out); - else - out << "null"; - out << ">"; + case ANY: + if (as_any().type() == typeid(expr_t::ptr_op_t)) { + out << "<#EXPR "; + as_any()->print(out); + out << ">"; + } else { + out << "<#OBJECT>"; + } break; default: @@ -1902,11 +1897,11 @@ void value_t::dump(std::ostream& out, const bool relaxed) const case SCOPE: out << as_scope(); break; - case EXPR: - if (as_expr()) - as_expr().dump(out); + case ANY: + if (as_any().type() == typeid(expr_t::ptr_op_t)) + as_any()->dump(out); else - out << "null"; + out << boost::unsafe_any_cast(&as_any()); break; case SEQUENCE: { @@ -2020,7 +2015,7 @@ void to_xml(std::ostream& out, const value_t& value) } case value_t::SCOPE: - case value_t::EXPR: + case value_t::ANY: default: assert(false); break; diff --git a/src/value.h b/src/value.h index 7bd09c9a6..361282c41 100644 --- a/src/value.h +++ b/src/value.h @@ -57,7 +57,6 @@ namespace ledger { DECLARE_EXCEPTION(value_error, std::runtime_error); class scope_t; -class expr_t; /** * @class value_t @@ -110,7 +109,7 @@ class value_t MASK, // a regular expression mask SEQUENCE, // a vector of value_t objects SCOPE, // a pointer to a scope - EXPR // a pointer to a value expression + ANY // a pointer to an arbitrary object }; private: @@ -128,17 +127,17 @@ class value_t * The `type' member holds the value_t::type_t value representing * the type of the object stored. */ - variant data; type_t type; @@ -354,10 +353,13 @@ class value_t TRACE_CTOR(value_t, "scope_t *"); set_scope(item); } - explicit value_t(const expr_t& item) { - TRACE_CTOR(value_t, "const expr_t&"); - set_expr(item); +#if 0 + template + explicit value_t(T& item) { + TRACE_CTOR(value_t, "T&"); + set_any(item); } +#endif /** * Destructor. This does not do anything, because the intrusive_ptr @@ -530,7 +532,7 @@ class value_t * is_string() * is_mask() * is_sequence() - * is_pointer() + * is_any() * * There are corresponding as_*() methods that represent a value as a * reference to its underlying type. For example, as_long() returns a @@ -729,20 +731,45 @@ class value_t } /** - * Dealing with expr pointers. + * Dealing with any type at all is bit involved because we actually + * deal with typed object. For example, if you call as_any it returns + * a boost::any object, but if you use as_any, then it returns + * a type_t by value. */ - bool is_expr() const { - return is_type(EXPR); + bool is_any() const { + return is_type(ANY); + } + template + bool is_any() const { + return (is_type(ANY) && + boost::get(storage->data).type() == typeid(T)); } - expr_t& as_expr_lval() const { - VERIFY(is_expr()); - return *boost::get(storage->data); + boost::any& as_any_lval() { + VERIFY(is_any()); + _dup(); + return boost::get(storage->data); + } + template + T& as_any_lval() { + return any_cast(as_any_lval()); } - const expr_t& as_expr() const { - VERIFY(is_expr()); - return *boost::get(storage->data); + const boost::any& as_any() const { + VERIFY(is_any()); + return boost::get(storage->data); + } + template + const T& as_any() const { + return any_cast(as_any()); + } + void set_any(const boost::any& val) { + set_type(ANY); + storage->data = val; + } + template + void set_any(T& val) { + set_type(ANY); + storage->data = boost::any(val); } - void set_expr(const expr_t& val); /** * Data conversion methods. These methods convert a value object to From ad3b30a9641b8e09c38ea76e7011b614152d8faf Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 05:01:54 -0400 Subject: [PATCH 20/26] In value_t::_dup, test if storage is uninitialized --- src/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value.h b/src/value.h index 361282c41..03d2550c3 100644 --- a/src/value.h +++ b/src/value.h @@ -256,7 +256,7 @@ class value_t */ void _dup() { VERIFY(storage); - if (storage->refc > 1) + if (storage && storage->refc > 1) storage = new storage_t(*storage.get()); } From ea1642b3f969463a49e5a671478c92e4ef129665 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 05:02:14 -0400 Subject: [PATCH 21/26] Completely reworked argument passing in expressions --- src/account.cc | 50 +++---- src/convert.cc | 8 +- src/expr.h | 20 ++- src/global.h | 1 - src/interactive.cc | 199 --------------------------- src/interactive.h | 164 ---------------------- src/item.cc | 13 +- src/op.cc | 52 +++---- src/op.h | 3 +- src/option.h | 4 +- src/parser.cc | 4 +- src/post.cc | 98 ++++++------- src/pyinterp.cc | 2 +- src/pyinterp.h | 3 +- src/report.cc | 218 ++++++++++++----------------- src/report.h | 53 ++++---- src/scope.cc | 19 +++ src/scope.h | 333 ++++++++++++++++++++++++++++++++++++--------- src/session.cc | 7 +- src/session.h | 7 +- src/xact.cc | 25 ++-- tools/Makefile.am | 2 - 22 files changed, 533 insertions(+), 752 deletions(-) delete mode 100644 src/interactive.cc delete mode 100644 src/interactive.h diff --git a/src/account.cc b/src/account.cc index f26b79130..809b6e46b 100644 --- a/src/account.cc +++ b/src/account.cc @@ -34,7 +34,6 @@ #include "account.h" #include "post.h" #include "xact.h" -#include "interactive.h" namespace ledger { @@ -189,27 +188,26 @@ std::ostream& operator<<(std::ostream& out, const account_t& account) } namespace { - value_t get_partial_name(call_scope_t& scope) + value_t get_partial_name(call_scope_t& args) { - in_context_t env(scope, "&b"); - return string_value(env->partial_name(env.has(0) ? - env.get(0) : false)); + return string_value(args.context() + .partial_name(args.has(0) && + args.get(0))); } - value_t get_account(call_scope_t& scope) { // this gets the name - interactive_t args(scope, "&v"); - account_t& account(find_scope(scope)); - if (args.has(0)) { + value_t get_account(call_scope_t& args) { // this gets the name + account_t& account(args.context()); + if (args.has(0)) { account_t * acct = account.parent; for (; acct && acct->parent; acct = acct->parent) ; - if (scope[0].is_string()) + if (args[0].is_string()) return scope_value(acct->find_account(args.get(0), false)); - else if (scope[0].is_mask()) + else if (args[0].is_mask()) return scope_value(acct->find_account_re(args.get(0).str())); else return NULL_VALUE; } - else if (scope.type_context() == value_t::SCOPE) { + else if (args.type_context() == value_t::SCOPE) { return scope_value(&account); } else { @@ -282,39 +280,35 @@ namespace { } template - value_t get_wrapper(call_scope_t& scope) { - return (*Func)(find_scope(scope)); + value_t get_wrapper(call_scope_t& args) { + return (*Func)(args.context()); } value_t get_parent(account_t& account) { return scope_value(account.parent); } - value_t fn_any(call_scope_t& scope) + value_t fn_any(call_scope_t& args) { - interactive_t args(scope, "X&X"); - - account_t& account(find_scope(scope)); - expr_t& expr(args.get(0)); + account_t& account(args.context()); + expr_t::ptr_op_t expr(args.get(0)); foreach (post_t * p, account.posts) { - bind_scope_t bound_scope(scope, *p); - if (expr.calc(bound_scope).to_boolean()) + bind_scope_t bound_scope(args, *p); + if (expr->calc(bound_scope).to_boolean()) return true; } return false; } - value_t fn_all(call_scope_t& scope) + value_t fn_all(call_scope_t& args) { - interactive_t args(scope, "X&X"); - - account_t& account(find_scope(scope)); - expr_t& expr(args.get(0)); + account_t& account(args.context()); + expr_t::ptr_op_t expr(args.get(0)); foreach (post_t * p, account.posts) { - bind_scope_t bound_scope(scope, *p); - if (! expr.calc(bound_scope).to_boolean()) + bind_scope_t bound_scope(args, *p); + if (! expr->calc(bound_scope).to_boolean()) return false; } return true; diff --git a/src/convert.cc b/src/convert.cc index f33a6b2e9..d7ee52b75 100644 --- a/src/convert.cc +++ b/src/convert.cc @@ -34,7 +34,6 @@ #include "convert.h" #include "csv.h" #include "scope.h" -#include "interactive.h" #include "iterators.h" #include "report.h" #include "xact.h" @@ -43,11 +42,10 @@ namespace ledger { -value_t convert_command(call_scope_t& scope) +value_t convert_command(call_scope_t& args) { - interactive_t args(scope, "s"); - report_t& report(find_scope(scope)); - journal_t& journal(*report.session.journal.get()); + report_t& report(args.context()); + journal_t& journal(*report.session.journal.get()); string bucket_name; if (report.HANDLED(account_)) diff --git a/src/expr.h b/src/expr.h index fa49cdc4b..e6eeebf6c 100644 --- a/src/expr.h +++ b/src/expr.h @@ -58,7 +58,6 @@ class expr_t : public expr_base_t class op_t; typedef intrusive_ptr ptr_op_t; typedef intrusive_ptr const_ptr_op_t; - protected: ptr_op_t ptr; @@ -142,6 +141,25 @@ class expr_t : public expr_base_t #endif // HAVE_BOOST_SERIALIZATION }; +/** + * Dealing with expr pointers tucked into value objects. + */ +inline bool is_expr(const value_t& val) { + return val.is_any() && val.as_any().type() == typeid(expr_t::ptr_op_t); +} +inline expr_t::ptr_op_t as_expr(const value_t& val) { + VERIFY(val.is_any()); + return val.as_any(); +} +inline void set_expr(value_t& val, expr_t::ptr_op_t op) { + val.set_any(op); +} +inline value_t expr_value(expr_t::ptr_op_t op) { + value_t temp; + temp.set_any(op); + return temp; +} + } // namespace ledger #endif // _EXPR_H diff --git a/src/global.h b/src/global.h index 87f2495ac..115183a50 100644 --- a/src/global.h +++ b/src/global.h @@ -38,7 +38,6 @@ #ifndef _GLOBAL_H #define _GLOBAL_H -#include "interactive.h" #include "option.h" #include "report.h" diff --git a/src/interactive.cc b/src/interactive.cc deleted file mode 100644 index b95057dd4..000000000 --- a/src/interactive.cc +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2003-2010, 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 - -#include "interactive.h" - -namespace ledger { - -void interactive_t::verify_arguments() const -{ - value_t::sequence_t::const_iterator i; - - const char * p = spec.c_str(); - const char * label = _("unknown"); - bool wrong_arg = false; - bool dont_skip = false; - bool optional = *p == '&'; - bool exit_loop = *p == '*'; - std::size_t offset = 1; - bool is_seq = args.value().is_sequence(); - const value_t * next_arg = NULL; - string vlabel; - - if (is_seq) { - i = args.begin(); - if (i != args.end()) - next_arg = &(*i); - } - else if (! args.value().is_null()) { - next_arg = &args.value(); - } - - for (; ! wrong_arg && ! exit_loop && *p && next_arg; p++) { - DEBUG("interactive.verify", - "Want " << *p << " got: " << next_arg->label()); - - wrong_arg = false; - switch (*p) { - case 'a': - label = _("an amount"); - wrong_arg = (! next_arg->is_long() && - ! next_arg->is_amount() && - ! next_arg->is_balance()); - break; - case 'b': - label = _("a boolean"); - wrong_arg = false; // booleans are converted - break; - case 'd': - label = _("a date"); - wrong_arg = (! next_arg->is_date() && - ! next_arg->is_datetime()); - break; - case 't': - label = _("a date/time"); - wrong_arg = (! next_arg->is_date() && - ! next_arg->is_datetime()); - break; - case 'i': - case 'l': - label = _("an integer"); - if (next_arg->is_long() || - (next_arg->is_amount() && - ! next_arg->as_amount().has_commodity())) { - wrong_arg = false; - } - else if (next_arg->is_string()) { - wrong_arg = false; - for (const char * q = next_arg->as_string().c_str(); *q; q++) { - if (! std::isdigit(*q) && *q != '-') { - wrong_arg = true; - break; - } - } - } - else { - wrong_arg = true; - } - break; - case 'm': - label = _("a regex"); - wrong_arg = ! next_arg->is_mask(); - break; - case 's': - label = _("a string"); - wrong_arg = ! next_arg->is_string(); - break; - case 'v': - label = _("any value"); - wrong_arg = false; - break; - case '^': - label = _("a scope"); - wrong_arg = ! next_arg->is_scope(); - break; - case 'X': - label = _("an expression"); - wrong_arg = ! next_arg->is_expr(); - break; - case 'S': - label = _("a sequence"); - wrong_arg = false; - break; - case '&': - optional = true; - dont_skip = true; - break; - case '*': - optional = true; - exit_loop = true; - dont_skip = true; - break; - } - if (wrong_arg && optional && next_arg->is_null()) - wrong_arg = false; - - if (wrong_arg) { - vlabel = next_arg->label(); - break; - } - - if (! dont_skip) { - if (is_seq) { - if (++i != args.end()) { - next_arg = &(*i); - offset++; - } else { - next_arg = NULL; - } - } else { - next_arg = NULL; - } - } - dont_skip = false; - } - - if (*p == '&' || *p == '*') - optional = true; - - DEBUG("interactive.verify", "Remaining args are optional"); - - if (wrong_arg) { - throw_(calc_error, _("Expected %1 for argument %2, but received %3") - << label << offset << vlabel); - } - else if (*p && ! optional && ! next_arg) { - throw_(calc_error, _("Too few arguments to function")); - } - else if (! *p && next_arg) { - throw_(calc_error, _("Too many arguments to function")); - } -} - -string join_args(call_scope_t& args) -{ - std::ostringstream buf; - bool first = true; - - for (std::size_t i = 0; i < args.size(); i++) { - if (first) - first = false; - else - buf << ' '; - buf << args[i]; - } - - return buf.str(); -} - -} // namespace ledger diff --git a/src/interactive.h b/src/interactive.h deleted file mode 100644 index 04c23ae51..000000000 --- a/src/interactive.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2003-2010, 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 interactive.h - * @author John Wiegley - * - * @ingroup expr - */ -#ifndef _INTERACTIVE_H -#define _INTERACTIVE_H - -#include "scope.h" - -namespace ledger { - -class interactive_t : public noncopyable -{ - call_scope_t& args; - string spec; - -public: - explicit interactive_t(call_scope_t& _args, const string& _spec = "") - : args(_args), spec(_spec) { - TRACE_CTOR(interactive_t, "call_scope_t&, const string&"); - verify_arguments(); - } - virtual ~interactive_t() { - TRACE_DTOR(interactive_t); - } - - void verify_arguments() const; - - bool has(std::size_t index) const { - if (index < args.size() && ! args[index].is_null()) - return true; - return false; - } - - value_t& value_at(std::size_t index) { - assert(has(index)); - return args[index]; - } - - template - T get(std::size_t index); -}; - -template <> -inline bool interactive_t::get(std::size_t index) { - return value_at(index).to_boolean(); -} -template <> -inline int interactive_t::get(std::size_t index) { - return value_at(index).to_int(); -} -template <> -inline long interactive_t::get(std::size_t index) { - return value_at(index).to_long(); -} -template <> -inline amount_t interactive_t::get(std::size_t index) { - return value_at(index).to_amount(); -} -template <> -inline string interactive_t::get(std::size_t index) { - return value_at(index).to_string(); -} -template <> -inline mask_t interactive_t::get(std::size_t index) { - return value_at(index).to_mask(); -} -template <> -inline date_t interactive_t::get(std::size_t index) { - return value_at(index).to_date(); -} -template <> -inline datetime_t interactive_t::get(std::size_t index) { - return value_at(index).to_datetime(); -} -template <> -inline value_t::sequence_t& -interactive_t::get(std::size_t index) { - return value_at(index).as_sequence_lval(); -} -template <> -inline const value_t::sequence_t& -interactive_t::get(std::size_t index) { - return value_at(index).as_sequence(); -} -template <> -inline scope_t * -interactive_t::get(std::size_t index) { - return value_at(index).as_scope(); -} -template <> -inline expr_t& interactive_t::get(std::size_t index) { - return value_at(index).as_expr_lval(); -} -template <> -inline const expr_t& interactive_t::get(std::size_t index) { - return value_at(index).as_expr(); -} - -template -class in_context_t : public interactive_t -{ - T& context; - -public: - explicit in_context_t(call_scope_t& args, const string& spec) - : interactive_t(args, spec), context(find_scope(args)) { - TRACE_CTOR(in_context_t, "call_scope_t&, const string&"); - } - virtual ~in_context_t() { - TRACE_DTOR(in_context_t); - } - - T& operator *() { - return context; - } - T * operator->() { - return &context; - } -}; - -string join_args(call_scope_t& args); - -} // namespace ledger - -#endif // _INTERACTIVE_H - diff --git a/src/item.cc b/src/item.cc index f59c9e29f..f0273e590 100644 --- a/src/item.cc +++ b/src/item.cc @@ -32,7 +32,6 @@ #include #include "item.h" -#include "interactive.h" namespace ledger { @@ -256,9 +255,9 @@ namespace { if (args.size() == 1) { if (args[0].is_string()) - return item.has_tag(args[0].as_string()); + return item.has_tag(args.get(0)); else if (args[0].is_mask()) - return item.has_tag(args[0].as_mask()); + return item.has_tag(args.get(0)); else throw_(std::runtime_error, _("Expected string or mask for argument 1, but received %1") @@ -266,7 +265,7 @@ namespace { } else if (args.size() == 2) { if (args[0].is_mask() && args[1].is_mask()) - return item.has_tag(args[0].to_mask(), args[1].to_mask()); + return item.has_tag(args.get(0), args.get(1)); else throw_(std::runtime_error, _("Expected masks for arguments 1 and 2, but received %1 and %2") @@ -288,9 +287,9 @@ namespace { if (args.size() == 1) { if (args[0].is_string()) - val = item.get_tag(args[0].as_string()); + val = item.get_tag(args.get(0)); else if (args[0].is_mask()) - val = item.get_tag(args[0].as_mask()); + val = item.get_tag(args.get(0)); else throw_(std::runtime_error, _("Expected string or mask for argument 1, but received %1") @@ -298,7 +297,7 @@ namespace { } else if (args.size() == 2) { if (args[0].is_mask() && args[1].is_mask()) - val = item.get_tag(args[0].to_mask(), args[1].to_mask()); + val = item.get_tag(args.get(0), args.get(1)); else throw_(std::runtime_error, _("Expected masks for arguments 1 and 2, but received %1 and %2") diff --git a/src/op.cc b/src/op.cc index 0feb157f5..c2001ec32 100644 --- a/src/op.cc +++ b/src/op.cc @@ -39,15 +39,11 @@ namespace ledger { namespace { - value_t split_cons_expr(expr_t::ptr_op_t op, - scope_t& scope, - std::vector& exprs) + value_t split_cons_expr(expr_t::ptr_op_t op) { - value_t seq; - if (op->kind == expr_t::op_t::O_CONS) { - exprs.push_back(expr_t(op->left(), &scope)); - seq.push_back(value_t(exprs.back())); + value_t seq; + seq.push_back(expr_value(op->left())); expr_t::ptr_op_t next = op->right(); while (next) { @@ -59,19 +55,18 @@ namespace { value_op = next; next = NULL; } - exprs.push_back(expr_t(value_op, &scope)); - seq.push_back(value_t(exprs.back())); + seq.push_back(expr_value(value_op)); } + return seq; } else { - exprs.push_back(expr_t(op, &scope)); - seq.push_back(value_t(exprs.back())); + return expr_value(op); } - return seq; } - void check_type_context(scope_t& scope, value_t& result) + inline void check_type_context(scope_t& scope, value_t& result) { - if (scope.type_context() != value_t::VOID && + if (scope.type_required() && + scope.type_context() != value_t::VOID && result.type() != scope.type_context()) { throw_(calc_error, _("Expected return of %1, but received %2") @@ -162,7 +157,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) // Evaluating an identifier is the same as calling its definition // directly, so we create an empty call_scope_t to reflect the scope for // this implicit call. - call_scope_t call_args(scope, scope.type_context()); + call_scope_t call_args(scope, scope.type_context(), scope.type_required()); result = left()->compile(call_args, depth + 1) ->calc(call_args, locus, depth + 1); check_type_context(scope, result); @@ -173,7 +168,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) // Evaluating a FUNCTION is the same as calling it directly; this happens // when certain functions-that-look-like-variables (such as "amount") are // resolved. - call_scope_t call_args(scope, scope.type_context()); + call_scope_t call_args(scope, scope.type_context(), scope.type_required()); result = as_function()(call_args); check_type_context(scope, result); #if defined(DEBUG_ON) @@ -239,24 +234,11 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; } - case O_CALL: - case O_EXPAND: { - call_scope_t call_args(scope, scope.type_context()); - // When evaluating a macro call, these expressions have to live beyond the - // call to calc() below. - optional > args_expr; - - if (has_right()) { - if (kind == O_CALL) { - call_args.set_args(right()->calc(scope, locus, depth + 1)); - } else { - // macros defer calculation to the callee - args_expr = std::vector(); - call_args.set_args(split_cons_expr(right()->kind == O_SEQ ? - right()->left() : right(), - scope, *args_expr)); - } - } + case O_CALL: { + call_scope_t call_args(scope, scope.type_context(), scope.type_required()); + if (has_right()) + call_args.set_args(split_cons_expr(right()->kind == O_SEQ ? + right()->left() : right())); ptr_op_t func = left(); const string& name(func->as_ident()); @@ -656,7 +638,6 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const break; case O_CALL: - case O_EXPAND: if (left() && left()->print(out, context)) found = true; if (has_right()) { @@ -728,7 +709,6 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const case O_DEFINE: out << "O_DEFINE"; break; case O_LOOKUP: out << "O_LOOKUP"; break; case O_CALL: out << "O_CALL"; break; - case O_EXPAND: out << "O_EXPAND"; break; case O_MATCH: out << "O_MATCH"; break; case O_NOT: out << "O_NOT"; break; diff --git a/src/op.h b/src/op.h index 27fdea3be..aa591b28c 100644 --- a/src/op.h +++ b/src/op.h @@ -105,7 +105,6 @@ class expr_t::op_t : public noncopyable O_DEFINE, O_LOOKUP, O_CALL, - O_EXPAND, O_MATCH, BINARY_OPERATORS, @@ -278,7 +277,7 @@ class expr_t::op_t : public noncopyable }; bool print(std::ostream& out, const context_t& context = context_t()) const; - void dump(std::ostream& out, const int depth) const; + void dump(std::ostream& out, const int depth = 0) const; static ptr_op_t wrap_value(const value_t& val); static ptr_op_t wrap_functor(const expr_t::func_t& fobj); diff --git a/src/option.h b/src/option.h index 91ff26f98..e2a4a839c 100644 --- a/src/option.h +++ b/src/option.h @@ -167,7 +167,7 @@ class option_t throw_(std::runtime_error, _("To many arguments provided for %1") << desc()); else if (! args[0].is_string()) throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); - on_with(args[0].as_string(), args[1]); + on_with(args.get(0), args[1]); } else if (args.size() < 1) { throw_(std::runtime_error, _("No argument provided for %1") << desc()); @@ -176,7 +176,7 @@ class option_t throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); } else { - on_only(args[0].as_string()); + on_only(args.get(0)); } handler_thunk(args); diff --git a/src/parser.cc b/src/parser.cc index db989f077..a15775d14 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -59,8 +59,6 @@ expr_t::parser_t::parse_value_term(std::istream& in, tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::LPAREN) { op_t::kind_t kind = op_t::O_CALL; - if (ident == "any" || ident == "all") - kind = op_t::O_EXPAND; ptr_op_t call_node(new op_t(kind)); call_node->set_left(node); node = call_node; @@ -81,7 +79,7 @@ expr_t::parser_t::parse_value_term(std::istream& in, .minus_flags(PARSE_SINGLE)); tok = next_token(in, tflags, ')'); - if (node->kind == op_t::O_CONS) { + if (node && node->kind == op_t::O_CONS) { ptr_op_t prev(node); node = new op_t(op_t::O_SEQ); node->set_left(prev); diff --git a/src/post.cc b/src/post.cc index d4a16122e..bbf432270 100644 --- a/src/post.cc +++ b/src/post.cc @@ -35,7 +35,6 @@ #include "xact.h" #include "account.h" #include "journal.h" -#include "interactive.h" #include "format.h" namespace ledger { @@ -197,15 +196,13 @@ namespace { return post.has_xdata() && post.xdata().has_flags(POST_EXT_DIRECT_AMT); } - value_t get_commodity(call_scope_t& scope) + value_t get_commodity(call_scope_t& args) { - in_context_t env(scope, "&v"); - if (env.has(0)) { - return string_value(env.value_at(0).to_amount().commodity().symbol()); + if (args.has(0)) { + return string_value(args.get(0).commodity().symbol()); } else { - post_t& post(find_scope(scope)); - if (post.has_xdata() && - post.xdata().has_flags(POST_EXT_COMPOUND)) + post_t& post(args.context()); + if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) return string_value(post.xdata().compound_value.to_amount() .commodity().symbol()); else @@ -254,48 +251,48 @@ namespace { return 1L; } - value_t get_account(call_scope_t& scope) + value_t get_account(call_scope_t& args) { - in_context_t env(scope, "&v"); - account_t& account(*env->reported_account()); - string name; + post_t& post(args.context()); + account_t& account(*post.reported_account()); + string name; - if (env.has(0)) { - if (env.value_at(0).is_long()) { - if (env.get(0) > 2) + if (args.has(0)) { + if (args[0].is_long()) { + if (args.get(0) > 2) name = format_t::truncate(account.fullname(), - env.get(0) - 2, + args.get(0) - 2, 2 /* account_abbrev_length */); else name = account.fullname(); } else { - account_t * account = NULL; - account_t * master = env->account; + account_t * acct = NULL; + account_t * master = &account; while (master->parent) master = master->parent; - if (env.value_at(0).is_string()) { - name = env.get(0); - account = master->find_account(name, false); + if (args[0].is_string()) { + name = args.get(0); + acct = master->find_account(name, false); } - else if (env.value_at(0).is_mask()) { - name = env.get(0).str(); - account = master->find_account_re(name); + else if (args[0].is_mask()) { + name = args.get(0).str(); + acct = master->find_account_re(name); } else { throw_(std::runtime_error, _("Expected string or mask for argument 1, but received %1") - << env.value_at(0).label()); + << args[0].label()); } - if (! account) + if (! acct) throw_(std::runtime_error, - _("Could not find an account matching ") << env.value_at(0)); + _("Could not find an account matching ") << args[0]); else - return value_t(static_cast(account)); + return value_t(static_cast(acct)); } } - else if (scope.type_context() == value_t::SCOPE) { + else if (args.type_context() == value_t::SCOPE) { return scope_value(&account); } else { @@ -304,14 +301,13 @@ namespace { return string_value(name); } - value_t get_display_account(call_scope_t& scope) + value_t get_display_account(call_scope_t& args) { - in_context_t env(scope, "&v"); - - value_t acct = get_account(scope); + post_t& post(args.context()); + value_t acct = get_account(args); if (acct.is_string()) { - if (env->has_flags(POST_VIRTUAL)) { - if (env->must_balance()) + if (post.has_flags(POST_VIRTUAL)) { + if (post.must_balance()) acct = string_value(string("[") + acct.as_string() + "]"); else acct = string_value(string("(") + acct.as_string() + ")"); @@ -349,44 +345,40 @@ namespace { return (*Func)(find_scope(scope)); } - value_t fn_any(call_scope_t& scope) + value_t fn_any(call_scope_t& args) { - interactive_t args(scope, "X&X"); - - post_t& post(find_scope(scope)); - expr_t& expr(args.get(0)); + post_t& post(args.context()); + expr_t::ptr_op_t expr(args.get(0)); foreach (post_t * p, post.xact->posts) { - bind_scope_t bound_scope(scope, *p); - if (p == &post && args.has(1) && - ! args.get(1).calc(bound_scope).to_boolean()) { + bind_scope_t bound_scope(args, *p); + if (p == &post && args.has(1) && + ! args.get(1)->calc(bound_scope).to_boolean()) { // If the user specifies any(EXPR, false), and the context is a // posting, then that posting isn't considered by the test. ; // skip it } - else if (expr.calc(bound_scope).to_boolean()) { + else if (expr->calc(bound_scope).to_boolean()) { return true; } } return false; } - value_t fn_all(call_scope_t& scope) + value_t fn_all(call_scope_t& args) { - interactive_t args(scope, "X&X"); - - post_t& post(find_scope(scope)); - expr_t& expr(args.get(0)); + post_t& post(args.context()); + expr_t::ptr_op_t expr(args.get(0)); foreach (post_t * p, post.xact->posts) { - bind_scope_t bound_scope(scope, *p); - if (p == &post && args.has(1) && - ! args.get(1).calc(bound_scope).to_boolean()) { + bind_scope_t bound_scope(args, *p); + if (p == &post && args.has(1) && + ! args.get(1)->calc(bound_scope).to_boolean()) { // If the user specifies any(EXPR, false), and the context is a // posting, then that posting isn't considered by the test. ; // skip it } - else if (! expr.calc(bound_scope).to_boolean()) { + else if (! expr->calc(bound_scope).to_boolean()) { return false; } } diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 76fc05616..8052f6a46 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -286,7 +286,7 @@ value_t python_interpreter_t::python_command(call_scope_t& args) std::strcpy(argv[0], argv0); for (std::size_t i = 0; i < args.size(); i++) { - string arg = args[i].as_string(); + string arg = args.get(i); argv[i + 1] = new char[arg.length() + 1]; std::strcpy(argv[i + 1], arg.c_str()); } diff --git a/src/pyinterp.h b/src/pyinterp.h index 3d747d5c3..1dfd0747d 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -109,8 +109,7 @@ class python_interpreter_t : public session_t virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); - OPTION_(python_interpreter_t, import_, DO_(scope) { - interactive_t args(scope, "ss"); + OPTION_(python_interpreter_t, import_, DO_(args) { parent->import_option(args.get(1)); }); }; diff --git a/src/report.cc b/src/report.cc index 9656d57af..3d44c2d47 100644 --- a/src/report.cc +++ b/src/report.cc @@ -448,44 +448,42 @@ value_t report_t::fn_display_total(call_scope_t& scope) return HANDLER(display_total_).expr.calc(scope); } -value_t report_t::fn_market(call_scope_t& scope) +value_t report_t::fn_market(call_scope_t& args) { - interactive_t args(scope, "a&ts"); - - value_t result; - optional moment = (args.has(1) ? + optional moment = (args.has(1) ? args.get(1) : optional()); - if (args.has(2)) - result = args.value_at(0).exchange_commodities(args.get(2), - /* add_prices= */ false, - moment); + value_t result; + if (args.has(2)) + result = args[0].exchange_commodities(args.get(2), + /* add_prices= */ false, moment); else - result = args.value_at(0).value(moment); + result = args[0].value(moment); + + DEBUG("fn_market", "value = " << args[0]); + if (moment) + DEBUG("fn_market", "moment = " << *moment); + DEBUG("fn_market", "result = " << result); if (! result.is_null()) return result; - return args.value_at(0); + return args[0]; } -value_t report_t::fn_get_at(call_scope_t& scope) +value_t report_t::fn_get_at(call_scope_t& args) { - interactive_t args(scope, "Sl"); - - DEBUG("report.get_at", "get_at[0] = " << args.value_at(0)); - DEBUG("report.get_at", "get_at[1] = " << args.value_at(1)); - - if (args.get(1) == 0) { - if (! args.value_at(0).is_sequence()) - return args.value_at(0); + std::size_t index = static_cast(args.get(1)); + if (index == 0) { + if (! args[0].is_sequence()) + return args[0]; } else { - if (! args.value_at(0).is_sequence()) + if (! args[0].is_sequence()) throw_(std::runtime_error, _("Attempting to get argument at index %1 from %2") - << args.get(1) << args.value_at(0).label()); + << index << args[0].label()); } - return args.get(0)[args.get(1)]; + return args[0].as_sequence()[index]; } value_t report_t::fn_is_seq(call_scope_t& scope) @@ -500,7 +498,7 @@ value_t report_t::fn_strip(call_scope_t& args) value_t report_t::fn_trim(call_scope_t& args) { - string temp(args.value().to_string()); + string temp(args.value().to_string()); scoped_array buf(new char[temp.length() + 1]); std::strcpy(buf.get(), temp.c_str()); @@ -527,31 +525,26 @@ value_t report_t::fn_trim(call_scope_t& args) value_t report_t::fn_print(call_scope_t& args) { std::ostream& out(output_stream); - bool first = true; - for (call_scope_t::iterator i = args.begin(); i != args.end(); i++) { + bool first = true; + for (std::size_t i = 0; i < args.size(); i++) { if (first) first = false; else out << ' '; - (*i).print(out); + args[i].print(out); } return true; } -value_t report_t::scrub(value_t val) +value_t report_t::fn_scrub(call_scope_t& args) { - value_t temp(val.strip_annotations(what_to_keep())); + value_t temp(args.value().strip_annotations(what_to_keep())); if (HANDLED(base)) return temp; else return temp.unreduced(); } -value_t report_t::fn_scrub(call_scope_t& args) -{ - return scrub(args.value()); -} - value_t report_t::fn_rounded(call_scope_t& args) { return args.value().rounded(); @@ -562,57 +555,52 @@ value_t report_t::fn_unrounded(call_scope_t& args) return args.value().unrounded(); } -value_t report_t::fn_quantity(call_scope_t& scope) +value_t report_t::fn_quantity(call_scope_t& args) { - interactive_t args(scope, "a"); return args.get(0).number(); } -value_t report_t::fn_floor(call_scope_t& scope) +value_t report_t::fn_floor(call_scope_t& args) { - interactive_t args(scope, "v"); - return args.value_at(0).floored(); + return args[0].floored(); } -value_t report_t::fn_abs(call_scope_t& scope) +value_t report_t::fn_abs(call_scope_t& args) { - interactive_t args(scope, "v"); - return args.value_at(0).abs(); + return args[0].abs(); } -value_t report_t::fn_truncated(call_scope_t& scope) +value_t report_t::fn_truncated(call_scope_t& args) { - interactive_t args(scope, "v&ll"); return string_value(format_t::truncate (args.get(0), - args.has(1) && args.get(1) > 0 ? args.get(1) : 0, - args.has(2) ? args.get(2) : 0)); + args.has(1) && + args.get(1) > 0 ? args.get(1) : 0, + args.has(2) ? args.get(2) : 0)); } -value_t report_t::fn_justify(call_scope_t& scope) +value_t report_t::fn_justify(call_scope_t& args) { - interactive_t args(scope, "vl&lbb"); - uint_least8_t flags(AMOUNT_PRINT_ELIDE_COMMODITY_QUOTES); - if (args.has(3) && args.get(3)) + if (args.has(3) && args.get(3)) flags |= AMOUNT_PRINT_RIGHT_JUSTIFY; - if (args.has(4) && args.get(4)) + if (args.has(4) && args.get(4)) flags |= AMOUNT_PRINT_COLORIZE; std::ostringstream out; - args.value_at(0) - .print(out, args.get(1), args.has(2) ? args.get(2) : -1, flags); + args[0].print(out, args.get(1), + args.has(2) ? args.get(2) : -1, flags); + return string_value(out.str()); } -value_t report_t::fn_quoted(call_scope_t& scope) +value_t report_t::fn_quoted(call_scope_t& args) { - interactive_t args(scope, "v"); std::ostringstream out; out << '"'; - foreach (const char ch, args.value_at(0).to_string()) { + foreach (const char ch, args.get(0)) { if (ch == '"') out << "\\\""; else @@ -623,9 +611,8 @@ value_t report_t::fn_quoted(call_scope_t& scope) return string_value(out.str()); } -value_t report_t::fn_join(call_scope_t& scope) +value_t report_t::fn_join(call_scope_t& args) { - interactive_t args(scope, "s"); std::ostringstream out; foreach (const char ch, args.get(0)) { @@ -637,21 +624,18 @@ value_t report_t::fn_join(call_scope_t& scope) return string_value(out.str()); } -value_t report_t::fn_format_date(call_scope_t& scope) +value_t report_t::fn_format_date(call_scope_t& args) { - interactive_t args(scope, "d&s"); - if (args.has(1)) + if (args.has(1)) return string_value(format_date(args.get(0), FMT_CUSTOM, args.get(1).c_str())); else return string_value(format_date(args.get(0), FMT_PRINTED)); } -value_t report_t::fn_ansify_if(call_scope_t& scope) +value_t report_t::fn_ansify_if(call_scope_t& args) { - interactive_t args(scope, "v&s"); - - if (args.has(1)) { + if (args.has(1)) { string color = args.get(1); std::ostringstream buf; if (color == "black") buf << "\033[30m"; @@ -665,127 +649,105 @@ value_t report_t::fn_ansify_if(call_scope_t& scope) else if (color == "bold") buf << "\033[1m"; else if (color == "underline") buf << "\033[4m"; else if (color == "blink") buf << "\033[5m"; - buf << args.value_at(0); + buf << args[0]; buf << "\033[0m"; return string_value(buf.str()); - } else { - return args.value_at(0); } + return args[0]; } -value_t report_t::fn_percent(call_scope_t& scope) +value_t report_t::fn_percent(call_scope_t& args) { - interactive_t args(scope, "aa"); return (amount_t("100.00%") * (args.get(0) / args.get(1)).number()); } -value_t report_t::fn_price(call_scope_t& scope) +value_t report_t::fn_price(call_scope_t& args) { - interactive_t args(scope, "v"); - return args.value_at(0).price(); + return args[0].price(); } -value_t report_t::fn_commodity(call_scope_t& scope) +value_t report_t::fn_commodity(call_scope_t& args) { - in_context_t env(scope, "v"); - return string_value(env.value_at(0).to_amount().commodity().symbol()); + return string_value(args.get(0).commodity().symbol()); } -value_t report_t::fn_lot_date(call_scope_t& scope) +value_t report_t::fn_lot_date(call_scope_t& args) { - interactive_t args(scope, "v"); - if (args.value_at(0).has_annotation()) { - const annotation_t& details(args.value_at(0).annotation()); + if (args[0].has_annotation()) { + const annotation_t& details(args[0].annotation()); if (details.date) return *details.date; } return NULL_VALUE; } -value_t report_t::fn_lot_price(call_scope_t& scope) +value_t report_t::fn_lot_price(call_scope_t& args) { - interactive_t args(scope, "v"); - if (args.value_at(0).has_annotation()) { - const annotation_t& details(args.value_at(0).annotation()); + if (args[0].has_annotation()) { + const annotation_t& details(args[0].annotation()); if (details.price) return *details.price; } return NULL_VALUE; } -value_t report_t::fn_lot_tag(call_scope_t& scope) +value_t report_t::fn_lot_tag(call_scope_t& args) { - interactive_t args(scope, "v"); - if (args.value_at(0).has_annotation()) { - const annotation_t& details(args.value_at(0).annotation()); + if (args[0].has_annotation()) { + const annotation_t& details(args[0].annotation()); if (details.tag) return string_value(*details.tag); } return NULL_VALUE; } -value_t report_t::fn_to_boolean(call_scope_t& scope) +value_t report_t::fn_to_boolean(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::BOOLEAN); - return args.value_at(0); + return args.get(0); } -value_t report_t::fn_to_int(call_scope_t& scope) +value_t report_t::fn_to_int(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::INTEGER); - return args.value_at(0); + // This method is not called fn_to_long, because that would be + // confusing to users who don't care about the distinction between + // integer and long. + return args.get(0); } -value_t report_t::fn_to_datetime(call_scope_t& scope) +value_t report_t::fn_to_datetime(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::DATETIME); - return args.value_at(0); + return args.get(0); } -value_t report_t::fn_to_date(call_scope_t& scope) +value_t report_t::fn_to_date(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::DATE); - return args.value_at(0); + return args.get(0); } -value_t report_t::fn_to_amount(call_scope_t& scope) +value_t report_t::fn_to_amount(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::AMOUNT); - return args.value_at(0); + return args.get(0); } -value_t report_t::fn_to_balance(call_scope_t& scope) +value_t report_t::fn_to_balance(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::BALANCE); - return args.value_at(0); + return args.get(0); } -value_t report_t::fn_to_string(call_scope_t& scope) +value_t report_t::fn_to_string(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::STRING); - return args.value_at(0); + return string_value(args.get(0)); } -value_t report_t::fn_to_mask(call_scope_t& scope) +value_t report_t::fn_to_mask(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::MASK); - return args.value_at(0); + return args.get(0); } -value_t report_t::fn_to_sequence(call_scope_t& scope) +value_t report_t::fn_to_sequence(call_scope_t& args) { - interactive_t args(scope, "v"); - args.value_at(0).in_place_cast(value_t::SEQUENCE); - return args.value_at(0); + return args[0].to_sequence(); } namespace { @@ -837,21 +799,19 @@ value_t report_t::reload_command(call_scope_t&) return true; } -value_t report_t::echo_command(call_scope_t& scope) +value_t report_t::echo_command(call_scope_t& args) { - interactive_t args(scope, "s"); std::ostream& out(output_stream); out << args.get(0) << std::endl; return true; } -value_t report_t::pricemap_command(call_scope_t& scope) +value_t report_t::pricemap_command(call_scope_t& args) { - interactive_t args(scope, "&s"); std::ostream& out(output_stream); commodity_pool_t::current_pool->print_pricemap - (out, what_to_keep(), args.has(0) ? + (out, what_to_keep(), args.has(0) ? optional(datetime_t(parse_date(args.get(0)))) : none); return true; diff --git a/src/report.h b/src/report.h index 00cbec286..44aed03b5 100644 --- a/src/report.h +++ b/src/report.h @@ -42,7 +42,6 @@ #ifndef _REPORT_H #define _REPORT_H -#include "interactive.h" #include "expr.h" #include "query.h" #include "chain.h" @@ -145,7 +144,6 @@ class report_t : public scope_t value_t fn_strip(call_scope_t& scope); value_t fn_trim(call_scope_t& scope); value_t fn_print(call_scope_t& scope); - value_t scrub(value_t val); value_t fn_scrub(call_scope_t& scope); value_t fn_quantity(call_scope_t& scope); value_t fn_rounded(call_scope_t& scope); @@ -366,7 +364,7 @@ class report_t : public scope_t on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get(0), args.get(1)); }); OPTION(report_t, amount_data); // -j @@ -394,12 +392,12 @@ class report_t : public scope_t }); OPTION_(report_t, begin_, DO_(args) { // -b - date_interval_t interval(args[1].to_string()); + date_interval_t interval(args.get(1)); optional begin = interval.begin(parent->session.current_year); if (! begin) throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") - << args[1].to_string()); + << args.get(1)); string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; parent->HANDLER(limit_).on(string("--begin"), predicate); @@ -488,10 +486,9 @@ class report_t : public scope_t OPTION(report_t, date_format_); OPTION(report_t, datetime_format_); - OPTION_(report_t, depth_, DO_(scope) { - interactive_t args(scope, "sl"); - parent->HANDLER(display_).on(string("--depth"), - string("depth<=") + args.get(1)); + OPTION_(report_t, depth_, DO_(args) { + parent->HANDLER(display_) + .on(string("--depth"), string("depth<=") + args.get(1)); }); OPTION_(report_t, deviation, DO() { @@ -522,7 +519,7 @@ class report_t : public scope_t on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get(0), args.get(1)); }); OPTION__ @@ -536,7 +533,7 @@ class report_t : public scope_t on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get(0), args.get(1)); }); OPTION(report_t, dow); @@ -544,14 +541,14 @@ class report_t : public scope_t OPTION(report_t, empty); // -E OPTION_(report_t, end_, DO_(args) { // -e - date_interval_t interval(args[1].to_string()); + date_interval_t interval(args.get(1)); // 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 end = interval.begin(parent->session.current_year); if (! end) throw_(std::invalid_argument, _("Could not determine end of period '%1'") - << args[1].to_string()); + << args.get(1)); string predicate = "date<[" + to_iso_extended_string(*end) + "]"; parent->HANDLER(limit_).on(string("--end"), predicate); @@ -563,7 +560,7 @@ class report_t : public scope_t OPTION(report_t, exact); OPTION_(report_t, exchange_, DO_(args) { // -X - on_with(args[0].as_string(), args[1]); + on_with(args.get(0), args[1]); call_scope_t no_args(*parent); no_args.push_back(args[0]); parent->HANDLER(market).parent = parent; @@ -611,7 +608,7 @@ class report_t : public scope_t on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get(0), args.get(1)); }); OPTION__(report_t, group_title_format_, CTOR(report_t, group_title_format_) { @@ -667,12 +664,12 @@ class report_t : public scope_t OPTION(report_t, no_total); OPTION_(report_t, now_, DO_(args) { - date_interval_t interval(args[1].to_string()); + date_interval_t interval(args.get(1)); optional begin = interval.begin(parent->session.current_year); if (! begin) throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") - << args[1].to_string()); + << args.get(1)); ledger::epoch = parent->terminus = datetime_t(*begin); parent->session.current_year = ledger::epoch->date().year(); }); @@ -769,7 +766,7 @@ class report_t : public scope_t OPTION(report_t, prepend_format_); OPTION_(report_t, prepend_width_, DO_(args) { - value = args[1].to_long(); + value = args.get(1); }); OPTION_(report_t, price, DO() { // -I @@ -842,13 +839,13 @@ class report_t : public scope_t on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get(0), args.get(1)); }); OPTION(report_t, seed_); OPTION_(report_t, sort_, DO_(args) { // -S - on_with(args[0].as_string(), args[1]); + on_with(args.get(0), args[1]); parent->HANDLER(sort_xacts_).off(); parent->HANDLER(sort_all_).off(); }); @@ -878,13 +875,13 @@ class report_t : public scope_t on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get(0), args.get(1)); }); OPTION(report_t, total_data); // -J OPTION_(report_t, truncate_, DO_(args) { - string style(args[1].to_string()); + string style(args.get(1)); if (style == "leading") format_t::default_style = format_t::TRUNCATE_LEADING; else if (style == "middle") @@ -934,27 +931,27 @@ class report_t : public scope_t OPTION__(report_t, meta_width_, bool specified; CTOR(report_t, meta_width_) { specified = false; } - DO_(args) { value = args[1].to_long(); specified = true; }); + DO_(args) { value = args.get(1); specified = true; }); OPTION__(report_t, date_width_, bool specified; CTOR(report_t, date_width_) { specified = false; } - DO_(args) { value = args[1].to_long(); specified = true; }); + DO_(args) { value = args.get(1); specified = true; }); OPTION__(report_t, payee_width_, bool specified; CTOR(report_t, payee_width_) { specified = false; } - DO_(args) { value = args[1].to_long(); specified = true; }); + DO_(args) { value = args.get(1); specified = true; }); OPTION__(report_t, account_width_, bool specified; CTOR(report_t, account_width_) { specified = false; } - DO_(args) { value = args[1].to_long(); specified = true; }); + DO_(args) { value = args.get(1); specified = true; }); OPTION__(report_t, amount_width_, bool specified; CTOR(report_t, amount_width_) { specified = false; } - DO_(args) { value = args[1].to_long(); specified = true; }); + DO_(args) { value = args.get(1); specified = true; }); OPTION__(report_t, total_width_, bool specified; CTOR(report_t, total_width_) { specified = false; } - DO_(args) { value = args[1].to_long(); specified = true; }); + DO_(args) { value = args.get(1); specified = true; }); }; diff --git a/src/scope.cc b/src/scope.cc index faad352a0..52cf6a90f 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -71,4 +71,23 @@ expr_t::ptr_op_t symbol_scope_t::lookup(const symbol_t::kind_t kind, return child_scope_t::lookup(kind, name); } +value_t& call_scope_t::resolve(const std::size_t index, + value_t::type_t context, + const bool required) +{ + if (index >= args.size()) + throw_(calc_error, _("Too few arguments to function")); + + value_t& value(args[index]); + if (value.is_any()) { + context_scope_t scope(*this, context, required); + value = as_expr(value)->calc(scope); + if (required && ! value.is_type(context)) + throw_(calc_error, _("Expected %1 for argument %2, but received %3") + << value.label(context) << index + << value.label()); + } + return value; +} + } // namespace ledger diff --git a/src/scope.h b/src/scope.h index 1b10d7a68..f9bea6b6b 100644 --- a/src/scope.h +++ b/src/scope.h @@ -117,6 +117,9 @@ class scope_t virtual value_t::type_t type_context() const { return value_t::VOID; } + virtual bool type_required() const { + return false; + } #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -172,6 +175,76 @@ class child_scope_t : public noncopyable, public scope_t #endif // HAVE_BOOST_SERIALIZATION }; +class bind_scope_t : public child_scope_t +{ + bind_scope_t(); + +public: + scope_t& grandchild; + + explicit bind_scope_t(scope_t& _parent, + scope_t& _grandchild) + : child_scope_t(_parent), grandchild(_grandchild) { + TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&"); + } + virtual ~bind_scope_t() { + TRACE_DTOR(bind_scope_t); + } + + virtual void define(const symbol_t::kind_t kind, const string& name, + expr_t::ptr_op_t def) { + parent->define(kind, name, def); + grandchild.define(kind, name, def); + } + + virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, + const string& name) { + if (expr_t::ptr_op_t def = grandchild.lookup(kind, name)) + return def; + return child_scope_t::lookup(kind, name); + } + +#if defined(HAVE_BOOST_SERIALIZATION) +private: + /** Serialization. */ + + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int /* version */) { + ar & boost::serialization::base_object(*this); + ar & grandchild; + } +#endif // HAVE_BOOST_SERIALIZATION +}; + +template +T * search_scope(scope_t * ptr) +{ + if (T * sought = dynamic_cast(ptr)) + return sought; + + if (bind_scope_t * scope = dynamic_cast(ptr)) { + if (T * sought = search_scope(&scope->grandchild)) + return sought; + return search_scope(scope->parent); + } + else if (child_scope_t * scope = dynamic_cast(ptr)) { + return search_scope(scope->parent); + } + return NULL; +} + +template +inline T& find_scope(child_scope_t& scope, bool skip_this = true) +{ + if (T * sought = search_scope(skip_this ? scope.parent : &scope)) + return *sought; + + throw_(std::runtime_error, _("Could not find scope")); + return reinterpret_cast(scope); // never executed +} + class symbol_scope_t : public child_scope_t { typedef std::map symbol_map; @@ -212,12 +285,15 @@ class symbol_scope_t : public child_scope_t class context_scope_t : public child_scope_t { value_t::type_t value_type_context; + bool required; public: explicit context_scope_t(scope_t& _parent, - value_t::type_t _type_context = value_t::VOID) - : child_scope_t(_parent), value_type_context(_type_context) { - TRACE_CTOR(context_scope_t, "scope_t&, value_t::type_t"); + value_t::type_t _type_context = value_t::VOID, + const bool _required = true) + : child_scope_t(_parent), value_type_context(_type_context), + required(_required) { + TRACE_CTOR(context_scope_t, "scope_t&, value_t::type_t, bool"); } virtual ~context_scope_t() { TRACE_DTOR(context_scope_t); @@ -226,17 +302,26 @@ class context_scope_t : public child_scope_t virtual value_t::type_t type_context() const { return value_type_context; } + virtual bool type_required() const { + return required; + } }; class call_scope_t : public context_scope_t { - value_t args; + value_t args; + mutable void * ptr; + + value_t& resolve(const std::size_t index, + value_t::type_t context = value_t::VOID, + const bool required = false); public: explicit call_scope_t(scope_t& _parent, - value_t::type_t _type_context = value_t::VOID) - : context_scope_t(_parent, _type_context) { - TRACE_CTOR(call_scope_t, "scope_t&, value_t::type_t"); + value_t::type_t _type_context = value_t::VOID, + const bool _required = true) + : context_scope_t(_parent, _type_context, _required), ptr(NULL) { + TRACE_CTOR(call_scope_t, "scope_t&, value_t::type_t, bool"); } virtual ~call_scope_t() { TRACE_DTOR(call_scope_t); @@ -246,15 +331,36 @@ class call_scope_t : public context_scope_t args = _args; } value_t& value() { + // Make sure that all of the arguments have been resolved. + for (std::size_t index = 0; index < args.size(); index++) + resolve(index); return args; } value_t& operator[](const std::size_t index) { - return args[index]; + return resolve(index); } +#if 0 const value_t& operator[](const std::size_t index) const { return args[index]; } +#endif + + bool has(std::size_t index) { + return index < args.size() && ! (*this)[index].is_null(); + } + template + bool has(std::size_t index); + template + T get(std::size_t index, bool convert = true); + + template + T& context() { + if (ptr == NULL) + ptr = &find_scope(*this); + assert(ptr != NULL); + return *static_cast(ptr); + } void push_front(const value_t& val) { args.push_front(val); @@ -309,74 +415,169 @@ class call_scope_t : public context_scope_t #endif // HAVE_BOOST_SERIALIZATION }; -class bind_scope_t : public child_scope_t -{ - bind_scope_t(); - -public: - scope_t& grandchild; - - explicit bind_scope_t(scope_t& _parent, - scope_t& _grandchild) - : child_scope_t(_parent), grandchild(_grandchild) { - TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&"); +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::BOOLEAN, false); + return ! args[index].is_null(); } - virtual ~bind_scope_t() { - TRACE_DTOR(bind_scope_t); + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::INTEGER, false); + return ! args[index].is_null(); } - - virtual void define(const symbol_t::kind_t kind, const string& name, - expr_t::ptr_op_t def) { - parent->define(kind, name, def); - grandchild.define(kind, name, def); + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::INTEGER, false); + return ! args[index].is_null(); } - - virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, - const string& name) { - if (expr_t::ptr_op_t def = grandchild.lookup(kind, name)) - return def; - return child_scope_t::lookup(kind, name); + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::AMOUNT, false); + return ! args[index].is_null(); } - -#if defined(HAVE_BOOST_SERIALIZATION) -private: - /** Serialization. */ - - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int /* version */) { - ar & boost::serialization::base_object(*this); - ar & grandchild; + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::BALANCE, false); + return ! args[index].is_null(); } -#endif // HAVE_BOOST_SERIALIZATION -}; - -template -T * search_scope(scope_t * ptr) -{ - if (T * sought = dynamic_cast(ptr)) - return sought; - - if (bind_scope_t * scope = dynamic_cast(ptr)) { - if (T * sought = search_scope(&scope->grandchild)) - return sought; - return search_scope(scope->parent); + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::STRING, false); + return ! args[index].is_null(); } - else if (child_scope_t * scope = dynamic_cast(ptr)) { - return search_scope(scope->parent); + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::DATE, false); + return ! args[index].is_null(); } - return NULL; + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::DATETIME, false); + return ! args[index].is_null(); + } + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::SCOPE, false); + return ! args[index].is_null(); + } + return false; +} +template <> +inline bool call_scope_t::has(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::ANY, false); + return ! args[index].is_null(); + } + return false; } -template -inline T& find_scope(child_scope_t& scope, bool skip_this = true) -{ - if (T * sought = search_scope(skip_this ? scope.parent : &scope)) - return *sought; +template <> +inline bool call_scope_t::get(std::size_t index, bool convert) { + if (convert) + return resolve(index, value_t::BOOLEAN, false).to_boolean(); + else + return resolve(index, value_t::BOOLEAN).as_boolean(); +} +template <> +inline int call_scope_t::get(std::size_t index, bool) { + return resolve(index, value_t::INTEGER, false).to_int(); +} +template <> +inline long call_scope_t::get(std::size_t index, bool convert) { + if (convert) + return resolve(index, value_t::INTEGER, false).to_long(); + else + return resolve(index, value_t::INTEGER).as_long(); +} +template <> +inline amount_t call_scope_t::get(std::size_t index, bool convert) { + if (convert) + return resolve(index, value_t::AMOUNT, false).to_amount(); + else + return resolve(index, value_t::AMOUNT).as_amount(); +} +template <> +inline balance_t call_scope_t::get(std::size_t index, bool convert) { + if (convert) + return resolve(index, value_t::BALANCE, false).to_balance(); + else + return resolve(index, value_t::BALANCE).as_balance(); +} +template <> +inline string call_scope_t::get(std::size_t index, bool convert) { + if (convert) + return resolve(index, value_t::STRING, false).to_string(); + else + return resolve(index, value_t::STRING).as_string(); +} +template <> +inline mask_t call_scope_t::get(std::size_t index, bool convert) { + if (convert) + return resolve(index, value_t::MASK, false).to_mask(); + else + return resolve(index, value_t::MASK).as_mask(); +} +template <> +inline date_t call_scope_t::get(std::size_t index, bool convert) { + if (convert) + return resolve(index, value_t::DATE, false).to_date(); + else + return resolve(index, value_t::DATE).as_date(); +} +template <> +inline datetime_t call_scope_t::get(std::size_t index, bool convert) { + if (convert) + return resolve(index, value_t::DATETIME, false).to_datetime(); + else + return resolve(index, value_t::DATETIME).as_datetime(); +} - throw_(std::runtime_error, _("Could not find scope")); - return reinterpret_cast(scope); // never executed +#if 0 +template <> +inline value_t::sequence_t& +call_scope_t::get(std::size_t index, bool) { + return resolve(index, value_t::SEQUENCE).as_sequence_lval(); +} +template <> +inline const value_t::sequence_t& +call_scope_t::get(std::size_t index, bool) { + return resolve(index, value_t::SEQUENCE).as_sequence(); +} +#endif + +template <> +inline scope_t * call_scope_t::get(std::size_t index, bool) { + return resolve(index, value_t::SCOPE).as_scope(); +} +template <> +inline expr_t::ptr_op_t +call_scope_t::get(std::size_t index, bool) { + return resolve(index, value_t::ANY).as_any(); } class value_scope_t : public scope_t diff --git a/src/session.cc b/src/session.cc index b5441766d..df6eaf7df 100644 --- a/src/session.cc +++ b/src/session.cc @@ -182,12 +182,11 @@ void session_t::close_journal_files() amount_t::initialize(); } -value_t session_t::fn_account(call_scope_t& scope) +value_t session_t::fn_account(call_scope_t& args) { - interactive_t args(scope, "v"); - if (scope[0].is_string()) + if (args[0].is_string()) return scope_value(journal->find_account(args.get(0), false)); - else if (scope[0].is_mask()) + else if (args[0].is_mask()) return scope_value(journal->find_account_re(args.get(0).str())); else return NULL_VALUE; diff --git a/src/session.h b/src/session.h index 5130e3fba..597268ee0 100644 --- a/src/session.h +++ b/src/session.h @@ -42,7 +42,6 @@ #ifndef _SESSION_H #define _SESSION_H -#include "interactive.h" #include "account.h" #include "journal.h" #include "option.h" @@ -110,7 +109,7 @@ class session_t : public symbol_scope_t (session_t, price_exp_, // -Z CTOR(session_t, price_exp_) { value = 24L * 3600L; } DO_(args) { - value = args[1].to_long() * 60L; + value = args.get(1) * 60L; }); OPTION__ @@ -123,13 +122,13 @@ class session_t : public symbol_scope_t data_files.clear(); parent->flush_on_next_data_file = false; } - data_files.push_back(args[1].as_string()); + data_files.push_back(args.get(1)); }); OPTION_(session_t, input_date_format_, DO_(args) { // This changes static variables inside times.h, which affects the basic // date parser. - set_input_date_format(args[1].as_string().c_str()); + set_input_date_format(args.get(1).c_str()); }); OPTION(session_t, master_account_); diff --git a/src/xact.cc b/src/xact.cc index 0bf1fc2c0..1188fd0f7 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -36,7 +36,6 @@ #include "account.h" #include "journal.h" #include "pool.h" -#include "interactive.h" namespace ledger { @@ -503,31 +502,27 @@ namespace { return (*Func)(find_scope(scope)); } - value_t fn_any(call_scope_t& scope) + value_t fn_any(call_scope_t& args) { - interactive_t args(scope, "X&X"); - - post_t& post(find_scope(scope)); - expr_t& expr(args.get(0)); + post_t& post(args.context()); + expr_t::ptr_op_t expr(args.get(0)); foreach (post_t * p, post.xact->posts) { - bind_scope_t bound_scope(scope, *p); - if (expr.calc(bound_scope).to_boolean()) + bind_scope_t bound_scope(args, *p); + if (expr->calc(bound_scope).to_boolean()) return true; } return false; } - value_t fn_all(call_scope_t& scope) + value_t fn_all(call_scope_t& args) { - interactive_t args(scope, "X&X"); - - post_t& post(find_scope(scope)); - expr_t& expr(args.get(0)); + post_t& post(args.context()); + expr_t::ptr_op_t expr(args.get(0)); foreach (post_t * p, post.xact->posts) { - bind_scope_t bound_scope(scope, *p); - if (! expr.calc(bound_scope).to_boolean()) + bind_scope_t bound_scope(args, *p); + if (! expr->calc(bound_scope).to_boolean()) return false; } return true; diff --git a/tools/Makefile.am b/tools/Makefile.am index 5b032f72b..fa83c1ca9 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -44,7 +44,6 @@ libledger_expr_la_SOURCES = \ src/query.cc \ src/predicate.cc \ src/scope.cc \ - src/interactive.cc \ src/expr.cc \ src/op.cc \ src/parser.cc \ @@ -115,7 +114,6 @@ pkginclude_HEADERS = \ src/exprbase.h \ src/expr.h \ src/scope.h \ - src/interactive.h \ src/predicate.h \ src/query.h \ src/format.h \ From 7146926fd50c1c599a05d92451c3ce593795f14c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 05:25:36 -0400 Subject: [PATCH 22/26] Removed unneeded debug code --- src/report.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/report.cc b/src/report.cc index 3d44c2d47..6b52c52e1 100644 --- a/src/report.cc +++ b/src/report.cc @@ -460,11 +460,6 @@ value_t report_t::fn_market(call_scope_t& args) else result = args[0].value(moment); - DEBUG("fn_market", "value = " << args[0]); - if (moment) - DEBUG("fn_market", "moment = " << *moment); - DEBUG("fn_market", "result = " << result); - if (! result.is_null()) return result; From 2dec00a882a72b4f031d23c641889a216785c345 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 05:31:24 -0400 Subject: [PATCH 23/26] Fixes to scope.h for the sake of Boost.Serialization --- src/scope.h | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/scope.h b/src/scope.h index f9bea6b6b..98b0ee02c 100644 --- a/src/scope.h +++ b/src/scope.h @@ -305,6 +305,24 @@ class context_scope_t : public child_scope_t virtual bool type_required() const { return required; } + +#if defined(HAVE_BOOST_SERIALIZATION) +protected: + explicit context_scope_t() { + TRACE_CTOR(context_scope_t, ""); + } + + /** Serialization. */ + + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int /* version */) { + ar & boost::serialization::base_object(*this); + ar & value_type_context; + ar & required; + } +#endif // HAVE_BOOST_SERIALIZATION }; class call_scope_t : public context_scope_t @@ -398,7 +416,7 @@ class call_scope_t : public context_scope_t } #if defined(HAVE_BOOST_SERIALIZATION) -private: +protected: explicit call_scope_t() { TRACE_CTOR(call_scope_t, ""); } @@ -409,8 +427,9 @@ class call_scope_t : public context_scope_t template void serialize(Archive& ar, const unsigned int /* version */) { - ar & boost::serialization::base_object(*this); + ar & boost::serialization::base_object(*this); ar & args; + //ar & ptr; } #endif // HAVE_BOOST_SERIALIZATION }; From 09fdab0d955561afecfe5b23b3c3bece62c4d0c6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 05:31:04 -0400 Subject: [PATCH 24/26] The --cache option is now officially deprecated --- tools/configure.ac | 61 +++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/tools/configure.ac b/tools/configure.ac index e3149a189..698741010 100644 --- a/tools/configure.ac +++ b/tools/configure.ac @@ -325,36 +325,37 @@ else fi # check for boost_serialization -AC_CACHE_CHECK( - [if boost_serialization is available], - [boost_serialization_cpplib_avail_cv_], - [boost_serialization_save_libs=$LIBS - LIBS="-lboost_serialization$BOOST_SUFFIX -lboost_system$BOOST_SUFFIX $LIBS" - AC_LANG_PUSH(C++) - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - #include - struct foo { - int a; - template - void serialize(Archive & ar, const unsigned int) { - ar & a; - } - };]], - [[boost::archive::binary_oarchive oa(std::cout); - foo x; - oa << x;]])], - [boost_serialization_cpplib_avail_cv_=true], - [boost_serialization_cpplib_avail_cv_=false]) - AC_LANG_POP - LIBS=$boost_serialization_save_libs]) - -if [test x$boost_serialization_cpplib_avail_cv_ = xtrue -a x$cache = xtrue]; then - AC_DEFINE([HAVE_BOOST_SERIALIZATION], [1], [Whether Boost.Serialization is available]) - LIBS="-lboost_serialization$BOOST_SUFFIX $LIBS" -fi -AM_CONDITIONAL(HAVE_BOOST_SERIALIZATION, test x$boost_serialization_cpplib_avail_cv_ = xtrue -a x$cache = xtrue) +#AC_CACHE_CHECK( +# [if boost_serialization is available], +# [boost_serialization_cpplib_avail_cv_], +# [boost_serialization_save_libs=$LIBS +# LIBS="-lboost_serialization$BOOST_SUFFIX -lboost_system$BOOST_SUFFIX $LIBS" +# AC_LANG_PUSH(C++) +# AC_LINK_IFELSE( +# [AC_LANG_PROGRAM( +# [[#include +# #include +# struct foo { +# int a; +# template +# void serialize(Archive & ar, const unsigned int) { +# ar & a; +# } +# };]], +# [[boost::archive::binary_oarchive oa(std::cout); +# foo x; +# oa << x;]])], +# [boost_serialization_cpplib_avail_cv_=true], +# [boost_serialization_cpplib_avail_cv_=false]) +# AC_LANG_POP +# LIBS=$boost_serialization_save_libs]) +# +#if [test x$boost_serialization_cpplib_avail_cv_ = xtrue -a x$cache = xtrue]; then +# AC_DEFINE([HAVE_BOOST_SERIALIZATION], [1], [Whether Boost.Serialization is available]) +# LIBS="-lboost_serialization$BOOST_SUFFIX $LIBS" +#fi +#AM_CONDITIONAL(HAVE_BOOST_SERIALIZATION, test x$boost_serialization_cpplib_avail_cv_ = xtrue -a x$cache = xtrue) +AM_CONDITIONAL(HAVE_BOOST_SERIALIZATION, false) # check for Python if [ test x$python = xtrue ]; then From b423932c3b1103b74838adb24478d0ddb4b6c591 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 05:31:31 -0400 Subject: [PATCH 25/26] Fixed documentation bug in acprep --- acprep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acprep b/acprep index 92bbd89ea..2f15e81c8 100755 --- a/acprep +++ b/acprep @@ -1679,7 +1679,7 @@ your options. Here are some real-world examples: ./acprep ./acprep opt -- make -j3 - ./acprep -- --enable-doxygen""" + ./acprep --enable-doxygen""" sys.exit(0) PrepareBuild().run() From cf0147fcd04fc7ec4b3849350430e47169581e64 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Jun 2010 05:57:02 -0400 Subject: [PATCH 26/26] Removed an invalid called to VERIFY() --- src/value.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/value.h b/src/value.h index 03d2550c3..6fe24da61 100644 --- a/src/value.h +++ b/src/value.h @@ -255,7 +255,6 @@ class value_t * subsequently be modified. */ void _dup() { - VERIFY(storage); if (storage && storage->refc > 1) storage = new storage_t(*storage.get()); }