diff options
author | John Wiegley <johnw@newartisans.com> | 2010-06-13 15:04:53 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2010-06-13 15:04:53 -0400 |
commit | 0c699e4d57fe91fa04c4c2f23f9c2f2a6a5da582 (patch) | |
tree | 985c50c080c077fa931ed9bf01c3895cbb851eda /src | |
parent | 40f553228f5a28034c6635fdcb4c86af28a385ed (diff) | |
parent | cf0147fcd04fc7ec4b3849350430e47169581e64 (diff) | |
download | fork-ledger-0c699e4d57fe91fa04c4c2f23f9c2f2a6a5da582.tar.gz fork-ledger-0c699e4d57fe91fa04c4c2f23f9c2f2a6a5da582.tar.bz2 fork-ledger-0c699e4d57fe91fa04c4c2f23f9c2f2a6a5da582.zip |
Merge branch 'next'
Diffstat (limited to 'src')
-rw-r--r-- | src/account.cc | 79 | ||||
-rw-r--r-- | src/account.h | 4 | ||||
-rw-r--r-- | src/convert.cc | 8 | ||||
-rw-r--r-- | src/expr.h | 20 | ||||
-rw-r--r-- | src/format.cc | 5 | ||||
-rw-r--r-- | src/global.h | 1 | ||||
-rw-r--r-- | src/interactive.cc | 200 | ||||
-rw-r--r-- | src/interactive.h | 164 | ||||
-rw-r--r-- | src/item.cc | 13 | ||||
-rw-r--r-- | src/journal.cc | 10 | ||||
-rw-r--r-- | src/journal.h | 6 | ||||
-rw-r--r-- | src/op.cc | 68 | ||||
-rw-r--r-- | src/op.h | 3 | ||||
-rw-r--r-- | src/option.h | 4 | ||||
-rw-r--r-- | src/parser.cc | 4 | ||||
-rw-r--r-- | src/post.cc | 119 | ||||
-rw-r--r-- | src/precmd.cc | 16 | ||||
-rw-r--r-- | src/pyinterp.cc | 2 | ||||
-rw-r--r-- | src/pyinterp.h | 3 | ||||
-rw-r--r-- | src/report.cc | 213 | ||||
-rw-r--r-- | src/report.h | 55 | ||||
-rw-r--r-- | src/scope.cc | 19 | ||||
-rw-r--r-- | src/scope.h | 375 | ||||
-rw-r--r-- | src/session.cc | 24 | ||||
-rw-r--r-- | src/session.h | 9 | ||||
-rw-r--r-- | src/system.hh.in | 1 | ||||
-rw-r--r-- | src/textual.cc | 174 | ||||
-rw-r--r-- | src/times.cc | 4 | ||||
-rw-r--r-- | src/value.cc | 146 | ||||
-rw-r--r-- | src/value.h | 122 | ||||
-rw-r--r-- | src/xact.cc | 73 | ||||
-rw-r--r-- | src/xact.h | 40 |
32 files changed, 1052 insertions, 932 deletions
diff --git a/src/account.cc b/src/account.cc index 4170a4d2..809b6e46 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 { @@ -122,6 +121,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()); @@ -175,15 +188,31 @@ 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<account_t> env(scope, "&b"); - return string_value(env->partial_name(env.has(0) ? - env.get<bool>(0) : false)); - } - - value_t get_account(account_t& account) { // this gets the name - return string_value(account.fullname()); + return string_value(args.context<account_t>() + .partial_name(args.has<bool>(0) && + args.get<bool>(0))); + } + + value_t get_account(call_scope_t& args) { // this gets the name + account_t& account(args.context<account_t>()); + if (args.has<string>(0)) { + account_t * acct = account.parent; + for (; acct && acct->parent; acct = acct->parent) ; + if (args[0].is_string()) + return scope_value(acct->find_account(args.get<string>(0), false)); + else if (args[0].is_mask()) + return scope_value(acct->find_account_re(args.get<mask_t>(0).str())); + else + return NULL_VALUE; + } + else if (args.type_context() == value_t::SCOPE) { + return scope_value(&account); + } + else { + return string_value(account.fullname()); + } } value_t get_account_base(account_t& account) { @@ -251,39 +280,35 @@ namespace { } template <value_t (*Func)(account_t&)> - value_t get_wrapper(call_scope_t& scope) { - return (*Func)(find_scope<account_t>(scope)); + value_t get_wrapper(call_scope_t& args) { + return (*Func)(args.context<account_t>()); } value_t get_parent(account_t& account) { - return value_t(static_cast<scope_t *>(account.parent)); + 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<account_t>(scope)); - expr_t& expr(args.get<expr_t&>(0)); + account_t& account(args.context<account_t>()); + expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(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<account_t>(scope)); - expr_t& expr(args.get<expr_t&>(0)); + account_t& account(args.context<account_t>()); + expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(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; @@ -301,7 +326,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/account.h b/src/account.h index d22d2bb3..26ebe261 100644 --- a/src/account.h +++ b/src/account.h @@ -119,9 +119,7 @@ public: (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() { diff --git a/src/convert.cc b/src/convert.cc index f33a6b2e..d7ee52b7 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<report_t>(scope)); - journal_t& journal(*report.session.journal.get()); + report_t& report(args.context<report_t>()); + journal_t& journal(*report.session.journal.get()); string bucket_name; if (report.HANDLED(account_)) @@ -58,7 +58,6 @@ public: class op_t; typedef intrusive_ptr<op_t> ptr_op_t; typedef intrusive_ptr<const op_t> const_ptr_op_t; - protected: ptr_op_t ptr; @@ -142,6 +141,25 @@ private: #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<expr_t::ptr_op_t>(); +} +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/format.cc b/src/format.cc index bde39882..ae40e1c3 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; diff --git a/src/global.h b/src/global.h index 87f2495a..115183a5 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 54c7fd6c..00000000 --- a/src/interactive.cc +++ /dev/null @@ -1,200 +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 <system.hh> - -#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_(std::logic_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")); - } - else if (! *p && next_arg) { - throw_(std::logic_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 04c23ae5..00000000 --- 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 <typename T> - T get(std::size_t index); -}; - -template <> -inline bool interactive_t::get<bool>(std::size_t index) { - return value_at(index).to_boolean(); -} -template <> -inline int interactive_t::get<int>(std::size_t index) { - return value_at(index).to_int(); -} -template <> -inline long interactive_t::get<long>(std::size_t index) { - return value_at(index).to_long(); -} -template <> -inline amount_t interactive_t::get<amount_t>(std::size_t index) { - return value_at(index).to_amount(); -} -template <> -inline string interactive_t::get<string>(std::size_t index) { - return value_at(index).to_string(); -} -template <> -inline mask_t interactive_t::get<mask_t>(std::size_t index) { - return value_at(index).to_mask(); -} -template <> -inline date_t interactive_t::get<date_t>(std::size_t index) { - return value_at(index).to_date(); -} -template <> -inline datetime_t interactive_t::get<datetime_t>(std::size_t index) { - return value_at(index).to_datetime(); -} -template <> -inline value_t::sequence_t& -interactive_t::get<value_t::sequence_t&>(std::size_t index) { - return value_at(index).as_sequence_lval(); -} -template <> -inline const value_t::sequence_t& -interactive_t::get<const value_t::sequence_t&>(std::size_t index) { - return value_at(index).as_sequence(); -} -template <> -inline scope_t * -interactive_t::get<scope_t *>(std::size_t index) { - return value_at(index).as_scope(); -} -template <> -inline expr_t& interactive_t::get<expr_t&>(std::size_t index) { - return value_at(index).as_expr_lval(); -} -template <> -inline const expr_t& interactive_t::get<const expr_t&>(std::size_t index) { - return value_at(index).as_expr(); -} - -template <typename T> -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<T>(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 f59c9e29..f0273e59 100644 --- a/src/item.cc +++ b/src/item.cc @@ -32,7 +32,6 @@ #include <system.hh> #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<string>(0)); else if (args[0].is_mask()) - return item.has_tag(args[0].as_mask()); + return item.has_tag(args.get<mask_t>(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<mask_t>(0), args.get<mask_t>(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<string>(0)); else if (args[0].is_mask()) - val = item.get_tag(args[0].as_mask()); + val = item.get_tag(args.get<mask_t>(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<mask_t>(0), args.get<mask_t>(1)); else throw_(std::runtime_error, _("Expected masks for arguments 1 and 2, but received %1 and %2") diff --git a/src/journal.cc b/src/journal.cc index fd6d3eac..ed1e26be 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<date_t::year_type> 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<date_t::year_type> 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 ca6b6e4f..183d074d 100644 --- a/src/journal.h +++ b/src/journal.h @@ -140,8 +140,10 @@ public: 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<date_t::year_type> current_year = none); + void extend_xact(xact_base_t * xact, + optional<date_t::year_type> current_year = none); bool remove_xact(xact_t * xact); xacts_list::iterator xacts_begin() { @@ -39,14 +39,11 @@ namespace ledger { namespace { - value_t split_cons_expr(expr_t::ptr_op_t op, scope_t& scope, - std::vector<expr_t>& 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) { @@ -58,14 +55,24 @@ 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); + } + } + + inline void check_type_context(scope_t& scope, value_t& result) + { + 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") + << result.label(scope.type_context()) + << result.label()); } - return seq; } } @@ -150,9 +157,10 @@ 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(), scope.type_required()); result = left()->compile(call_args, depth + 1) ->calc(call_args, locus, depth + 1); + check_type_context(scope, result); break; } @@ -160,8 +168,9 @@ 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(), scope.type_required()); result = as_function()(call_args); + check_type_context(scope, result); #if defined(DEBUG_ON) skip_debug = true; #endif @@ -200,8 +209,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,25 +232,13 @@ 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); - // When evaluating a macro call, these expressions have to live beyond the - // call to calc() below. - optional<std::vector<expr_t> > 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<expr_t>(); - 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()); @@ -253,6 +251,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; } @@ -638,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()) { @@ -710,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; @@ -105,7 +105,6 @@ public: O_DEFINE, O_LOOKUP, O_CALL, - O_EXPAND, O_MATCH, BINARY_OPERATORS, @@ -278,7 +277,7 @@ public: }; 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 91ff26f9..e2a4a839 100644 --- a/src/option.h +++ b/src/option.h @@ -167,7 +167,7 @@ public: 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<string>(0), args[1]); } else if (args.size() < 1) { throw_(std::runtime_error, _("No argument provided for %1") << desc()); @@ -176,7 +176,7 @@ public: throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); } else { - on_only(args[0].as_string()); + on_only(args.get<string>(0)); } handler_thunk(args); diff --git a/src/parser.cc b/src/parser.cc index db989f07..a15775d1 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 fa8bab4f..bbf43227 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 { @@ -126,7 +125,7 @@ optional<date_t> post_t::effective_date() const namespace { value_t get_this(post_t& post) { - return value_t(static_cast<scope_t *>(&post)); + return scope_value(&post); } value_t get_is_calculated(post_t& post) { @@ -146,7 +145,7 @@ namespace { } value_t get_xact(post_t& post) { - return value_t(static_cast<scope_t *>(post.xact)); + return scope_value(post.xact); } value_t get_xact_id(post_t& post) { @@ -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<post_t> env(scope, "&v"); - if (env.has(0)) { - return string_value(env.value_at(0).to_amount().commodity().symbol()); + if (args.has<amount_t>(0)) { + return string_value(args.get<amount_t>(0).commodity().symbol()); } else { - post_t& post(find_scope<post_t>(scope)); - if (post.has_xdata() && - post.xdata().has_flags(POST_EXT_COMPOUND)) + post_t& post(args.context<post_t>()); + if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) return string_value(post.xdata().compound_value.to_amount() .commodity().symbol()); else @@ -254,60 +251,63 @@ namespace { return 1L; } - value_t account_name(call_scope_t& scope) + value_t get_account(call_scope_t& args) { - in_context_t<post_t> env(scope, "&v"); - - string name; - - if (env.has(0)) { - if (env.value_at(0).is_long()) { - if (env.get<long>(0) > 2) - name = format_t::truncate(env->reported_account()->fullname(), - env.get<long>(0) - 2, + post_t& post(args.context<post_t>()); + account_t& account(*post.reported_account()); + string name; + + if (args.has(0)) { + if (args[0].is_long()) { + if (args.get<long>(0) > 2) + name = format_t::truncate(account.fullname(), + args.get<long>(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; + account_t * acct = NULL; + account_t * master = &account; while (master->parent) master = master->parent; - if (env.value_at(0).is_string()) { - name = env.get<string>(0); - account = master->find_account(name, false); + if (args[0].is_string()) { + name = args.get<string>(0); + acct = master->find_account(name, false); } - else if (env.value_at(0).is_mask()) { - name = env.get<mask_t>(0).str(); - account = master->find_account_re(name); + else if (args[0].is_mask()) { + name = args.get<mask_t>(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<scope_t *>(account)); + return value_t(static_cast<scope_t *>(acct)); } - } else { - name = env->reported_account()->fullname(); + } + else if (args.type_context() == value_t::SCOPE) { + return scope_value(&account); + } + else { + name = account.fullname(); } 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<post_t> env(scope, "&v"); - - value_t acct = account_name(scope); + post_t& post(args.context<post_t>()); + 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() + ")"); @@ -316,11 +316,6 @@ namespace { return acct; } - value_t get_account(call_scope_t& scope) - { - return account_name(scope); - } - value_t get_account_id(post_t& post) { return static_cast<long>(post.account_id()); } @@ -350,44 +345,40 @@ namespace { return (*Func)(find_scope<post_t>(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<post_t>(scope)); - expr_t& expr(args.get<expr_t&>(0)); + post_t& post(args.context<post_t>()); + expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(0)); foreach (post_t * p, post.xact->posts) { - bind_scope_t bound_scope(scope, *p); - if (p == &post && args.has(1) && - ! args.get<expr_t&>(1).calc(bound_scope).to_boolean()) { + bind_scope_t bound_scope(args, *p); + if (p == &post && args.has<expr_t::ptr_op_t>(1) && + ! args.get<expr_t::ptr_op_t>(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<post_t>(scope)); - expr_t& expr(args.get<expr_t&>(0)); + post_t& post(args.context<post_t>()); + expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(0)); foreach (post_t * p, post.xact->posts) { - bind_scope_t bound_scope(scope, *p); - if (p == &post && args.has(1) && - ! args.get<expr_t&>(1).calc(bound_scope).to_boolean()) { + bind_scope_t bound_scope(args, *p); + if (p == &post && args.has<expr_t::ptr_op_t>(1) && + ! args.get<expr_t::ptr_op_t>(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/precmd.cc b/src/precmd.cc index f78d766e..4c916608 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) { { diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 76fc0561..8052f6a4 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<string>(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 3d747d5c..1dfd0747 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -109,8 +109,7 @@ public: 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<string>(1)); }); }; diff --git a/src/report.cc b/src/report.cc index 9656d57a..6b52c52e 100644 --- a/src/report.cc +++ b/src/report.cc @@ -448,44 +448,37 @@ 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<datetime_t> moment = (args.has(1) ? + optional<datetime_t> moment = (args.has<datetime_t>(1) ? args.get<datetime_t>(1) : optional<datetime_t>()); - if (args.has(2)) - result = args.value_at(0).exchange_commodities(args.get<string>(2), - /* add_prices= */ false, - moment); + value_t result; + if (args.has<string>(2)) + result = args[0].exchange_commodities(args.get<string>(2), + /* add_prices= */ false, moment); else - result = args.value_at(0).value(moment); + result = args[0].value(moment); 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<long>(1) == 0) { - if (! args.value_at(0).is_sequence()) - return args.value_at(0); + std::size_t index = static_cast<std::size_t>(args.get<long>(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<long>(1) << args.value_at(0).label()); + << index << args[0].label()); } - return args.get<const value_t::sequence_t&>(0)[args.get<long>(1)]; + return args[0].as_sequence()[index]; } value_t report_t::fn_is_seq(call_scope_t& scope) @@ -500,7 +493,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<char> buf(new char[temp.length() + 1]); std::strcpy(buf.get(), temp.c_str()); @@ -527,31 +520,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 +550,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<amount_t>(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<string>(0), - args.has(1) && args.get<int>(1) > 0 ? args.get<int>(1) : 0, - args.has(2) ? args.get<int>(2) : 0)); + args.has<int>(1) && + args.get<int>(1) > 0 ? args.get<int>(1) : 0, + args.has<int>(2) ? args.get<int>(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<bool>(3)) + if (args.has<bool>(3) && args.get<bool>(3)) flags |= AMOUNT_PRINT_RIGHT_JUSTIFY; - if (args.has(4) && args.get<bool>(4)) + if (args.has<bool>(4) && args.get<bool>(4)) flags |= AMOUNT_PRINT_COLORIZE; std::ostringstream out; - args.value_at(0) - .print(out, args.get<int>(1), args.has(2) ? args.get<int>(2) : -1, flags); + args[0].print(out, args.get<int>(1), + args.has<int>(2) ? args.get<int>(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<string>(0)) { if (ch == '"') out << "\\\""; else @@ -623,9 +606,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<string>(0)) { @@ -637,21 +619,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<string>(1)) return string_value(format_date(args.get<date_t>(0), FMT_CUSTOM, args.get<string>(1).c_str())); else return string_value(format_date(args.get<date_t>(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<string>(1)) { string color = args.get<string>(1); std::ostringstream buf; if (color == "black") buf << "\033[30m"; @@ -665,127 +644,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<amount_t>(0) / args.get<amount_t>(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<post_t> env(scope, "v"); - return string_value(env.value_at(0).to_amount().commodity().symbol()); + return string_value(args.get<amount_t>(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<bool>(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<long>(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<datetime_t>(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<date_t>(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<amount_t>(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<balance_t>(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<string>(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<mask_t>(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 +794,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<string>(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<string>(0) ? optional<datetime_t>(datetime_t(parse_date(args.get<string>(0)))) : none); return true; diff --git a/src/report.h b/src/report.h index 082c204b..44aed03b 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 @@ public: 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); @@ -182,7 +180,7 @@ public: } value_t fn_options(call_scope_t&) { - return value_t(static_cast<scope_t *>(this)); + return scope_value(this); } string report_format(option_t<report_t>& option) { @@ -366,7 +364,7 @@ public: on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get<string>(0), args.get<string>(1)); }); OPTION(report_t, amount_data); // -j @@ -394,12 +392,12 @@ public: }); OPTION_(report_t, begin_, DO_(args) { // -b - date_interval_t interval(args[1].to_string()); + date_interval_t interval(args.get<string>(1)); optional<date_t> 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<string>(1)); string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; parent->HANDLER(limit_).on(string("--begin"), predicate); @@ -488,10 +486,9 @@ public: 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<string>(1)); + OPTION_(report_t, depth_, DO_(args) { + parent->HANDLER(display_) + .on(string("--depth"), string("depth<=") + args.get<string>(1)); }); OPTION_(report_t, deviation, DO() { @@ -522,7 +519,7 @@ public: on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get<string>(0), args.get<string>(1)); }); OPTION__ @@ -536,7 +533,7 @@ public: on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get<string>(0), args.get<string>(1)); }); OPTION(report_t, dow); @@ -544,14 +541,14 @@ public: 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<string>(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<date_t> 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<string>(1)); string predicate = "date<[" + to_iso_extended_string(*end) + "]"; parent->HANDLER(limit_).on(string("--end"), predicate); @@ -563,7 +560,7 @@ public: OPTION(report_t, exact); OPTION_(report_t, exchange_, DO_(args) { // -X - on_with(args[0].as_string(), args[1]); + on_with(args.get<string>(0), args[1]); call_scope_t no_args(*parent); no_args.push_back(args[0]); parent->HANDLER(market).parent = parent; @@ -611,7 +608,7 @@ public: on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get<string>(0), args.get<string>(1)); }); OPTION__(report_t, group_title_format_, CTOR(report_t, group_title_format_) { @@ -667,12 +664,12 @@ public: 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<string>(1)); optional<date_t> 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<string>(1)); ledger::epoch = parent->terminus = datetime_t(*begin); parent->session.current_year = ledger::epoch->date().year(); }); @@ -769,7 +766,7 @@ public: OPTION(report_t, prepend_format_); OPTION_(report_t, prepend_width_, DO_(args) { - value = args[1].to_long(); + value = args.get<long>(1); }); OPTION_(report_t, price, DO() { // -I @@ -842,13 +839,13 @@ public: on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get<string>(0), args.get<string>(1)); }); OPTION(report_t, seed_); OPTION_(report_t, sort_, DO_(args) { // -S - on_with(args[0].as_string(), args[1]); + on_with(args.get<string>(0), args[1]); parent->HANDLER(sort_xacts_).off(); parent->HANDLER(sort_all_).off(); }); @@ -878,13 +875,13 @@ public: on(whence, str); } DO_(args) { - set_expr(args[0].to_string(), args[1].to_string()); + set_expr(args.get<string>(0), args.get<string>(1)); }); OPTION(report_t, total_data); // -J OPTION_(report_t, truncate_, DO_(args) { - string style(args[1].to_string()); + string style(args.get<string>(1)); if (style == "leading") format_t::default_style = format_t::TRUNCATE_LEADING; else if (style == "middle") @@ -934,27 +931,27 @@ public: 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<long>(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<long>(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<long>(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<long>(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<long>(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<long>(1); specified = true; }); }; diff --git a/src/scope.cc b/src/scope.cc index faad352a..52cf6a90 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 e3dd3e3f..98b0ee02 100644 --- a/src/scope.h +++ b/src/scope.h @@ -114,6 +114,13 @@ public: 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; + } + virtual bool type_required() const { + return false; + } + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ @@ -168,6 +175,76 @@ private: #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<class Archive> + void serialize(Archive& ar, const unsigned int /* version */) { + ar & boost::serialization::base_object<child_scope_t>(*this); + ar & grandchild; + } +#endif // HAVE_BOOST_SERIALIZATION +}; + +template <typename T> +T * search_scope(scope_t * ptr) +{ + if (T * sought = dynamic_cast<T *>(ptr)) + return sought; + + if (bind_scope_t * scope = dynamic_cast<bind_scope_t *>(ptr)) { + if (T * sought = search_scope<T>(&scope->grandchild)) + return sought; + return search_scope<T>(scope->parent); + } + else if (child_scope_t * scope = dynamic_cast<child_scope_t *>(ptr)) { + return search_scope<T>(scope->parent); + } + return NULL; +} + +template <typename T> +inline T& find_scope(child_scope_t& scope, bool skip_this = true) +{ + if (T * sought = search_scope<T>(skip_this ? scope.parent : &scope)) + return *sought; + + throw_(std::runtime_error, _("Could not find scope")); + return reinterpret_cast<T&>(scope); // never executed +} + class symbol_scope_t : public child_scope_t { typedef std::map<symbol_t, expr_t::ptr_op_t> symbol_map; @@ -205,13 +282,64 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; -class call_scope_t : public child_scope_t +class context_scope_t : public child_scope_t { - value_t args; + value_t::type_t value_type_context; + bool required; public: - explicit call_scope_t(scope_t& _parent) : child_scope_t(_parent) { - TRACE_CTOR(call_scope_t, "scope_t&"); + explicit context_scope_t(scope_t& _parent, + 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); + } + + virtual value_t::type_t type_context() const { + return value_type_context; + } + 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<class Archive> + void serialize(Archive& ar, const unsigned int /* version */) { + ar & boost::serialization::base_object<child_scope_t>(*this); + ar & value_type_context; + ar & required; + } +#endif // HAVE_BOOST_SERIALIZATION +}; + +class call_scope_t : public context_scope_t +{ + 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, + 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); @@ -221,15 +349,36 @@ public: 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 <typename T> + bool has(std::size_t index); + template <typename T> + T get(std::size_t index, bool convert = true); + + template <typename T> + T& context() { + if (ptr == NULL) + ptr = &find_scope<T>(*this); + assert(ptr != NULL); + return *static_cast<T *>(ptr); + } void push_front(const value_t& val) { args.push_front(val); @@ -267,7 +416,7 @@ public: } #if defined(HAVE_BOOST_SERIALIZATION) -private: +protected: explicit call_scope_t() { TRACE_CTOR(call_scope_t, ""); } @@ -278,80 +427,176 @@ private: template<class Archive> void serialize(Archive& ar, const unsigned int /* version */) { - ar & boost::serialization::base_object<child_scope_t>(*this); + ar & boost::serialization::base_object<context_scope_t>(*this); ar & args; + //ar & ptr; } #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<bool>(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<int>(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<long>(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<amount_t>(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<class Archive> - void serialize(Archive& ar, const unsigned int /* version */) { - ar & boost::serialization::base_object<child_scope_t>(*this); - ar & grandchild; + return false; +} +template <> +inline bool call_scope_t::has<balance_t>(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::BALANCE, false); + return ! args[index].is_null(); } -#endif // HAVE_BOOST_SERIALIZATION -}; - -template <typename T> -T * search_scope(scope_t * ptr) -{ - if (T * sought = dynamic_cast<T *>(ptr)) - return sought; - - if (bind_scope_t * scope = dynamic_cast<bind_scope_t *>(ptr)) { - if (T * sought = search_scope<T>(&scope->grandchild)) - return sought; - return search_scope<T>(scope->parent); + return false; +} +template <> +inline bool call_scope_t::has<string>(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<child_scope_t *>(ptr)) { - return search_scope<T>(scope->parent); + return false; +} +template <> +inline bool call_scope_t::has<date_t>(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<datetime_t>(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<scope_t *>(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<expr_t::ptr_op_t>(std::size_t index) { + if (index < args.size()) { + resolve(index, value_t::ANY, false); + return ! args[index].is_null(); + } + return false; } -template <typename T> -inline T& find_scope(child_scope_t& scope, bool skip_this = true) -{ - if (T * sought = search_scope<T>(skip_this ? scope.parent : &scope)) - return *sought; +template <> +inline bool call_scope_t::get<bool>(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<int>(std::size_t index, bool) { + return resolve(index, value_t::INTEGER, false).to_int(); +} +template <> +inline long call_scope_t::get<long>(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<amount_t>(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<balance_t>(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<string>(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<mask_t>(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<date_t>(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<datetime_t>(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<T&>(scope); // never executed +#if 0 +template <> +inline value_t::sequence_t& +call_scope_t::get<value_t::sequence_t&>(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<const value_t::sequence_t&>(std::size_t index, bool) { + return resolve(index, value_t::SEQUENCE).as_sequence(); +} +#endif + +template <> +inline scope_t * call_scope_t::get<scope_t *>(std::size_t index, bool) { + return resolve(index, value_t::SCOPE).as_scope(); +} +template <> +inline expr_t::ptr_op_t +call_scope_t::get<expr_t::ptr_op_t>(std::size_t index, bool) { + return resolve(index, value_t::ANY).as_any<expr_t::ptr_op_t>(); } class value_scope_t : public scope_t diff --git a/src/session.cc b/src/session.cc index 8adfef38..df6eaf7d 100644 --- a/src/session.cc +++ b/src/session.cc @@ -182,6 +182,16 @@ void session_t::close_journal_files() amount_t::initialize(); } +value_t session_t::fn_account(call_scope_t& args) +{ + if (args[0].is_string()) + return scope_value(journal->find_account(args.get<string>(0), false)); + else if (args[0].is_mask()) + return scope_value(journal->find_account_re(args.get<mask_t>(0).str())); + else + return NULL_VALUE; +} + option_t<session_t> * session_t::lookup_option(const char * p) { switch (*p) { @@ -224,15 +234,25 @@ option_t<session_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<session_t> * handler = lookup_option(name.c_str())) + if (option_t<session_t> * handler = lookup_option(p)) return MAKE_OPT_FUNCTOR(session_t, handler); break; case symbol_t::OPTION: - if (option_t<session_t> * handler = lookup_option(name.c_str())) + if (option_t<session_t> * handler = lookup_option(p)) return MAKE_OPT_HANDLER(session_t, handler); break; diff --git a/src/session.h b/src/session.h index 4968f1b8..597268ee 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" @@ -75,6 +74,8 @@ public: 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); @@ -108,7 +109,7 @@ public: (session_t, price_exp_, // -Z CTOR(session_t, price_exp_) { value = 24L * 3600L; } DO_(args) { - value = args[1].to_long() * 60L; + value = args.get<long>(1) * 60L; }); OPTION__ @@ -121,13 +122,13 @@ public: data_files.clear(); parent->flush_on_next_data_file = false; } - data_files.push_back(args[1].as_string()); + data_files.push_back(args.get<string>(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<string>(1).c_str()); }); OPTION(session_t, master_account_); diff --git a/src/system.hh.in b/src/system.hh.in index 34efa424..d9e664e3 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 <boost/algorithm/string.hpp> +#include <boost/any.hpp> #include <boost/bind.hpp> #include <boost/cast.hpp> #include <boost/current_function.hpp> diff --git a/src/textual.cc b/src/textual.cc index 9a4fee8e..113bafe8 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, @@ -158,9 +161,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); @@ -523,20 +526,74 @@ void instance_t::automated_xact_directive(char * line) bool reveal_context = true; try { + std::auto_ptr<auto_xact_t> 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<auto_xact_t> 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()); @@ -546,11 +603,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; @@ -579,7 +634,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; @@ -587,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")); } @@ -613,9 +669,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")); } @@ -825,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]; @@ -847,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': @@ -861,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': @@ -875,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': @@ -1256,7 +1344,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<string>(state).c_str(), context.scope, - true); + true, current_year); } TRACE_STOP(post_details, 1); @@ -1385,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; } } @@ -1428,7 +1542,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<string>(state).c_str(), context.scope, - false); + false, current_year); } TRACE_STOP(xact_details, 1); diff --git a/src/times.cc b/src/times.cc index 26e1dc21..a9768f4f 100644 --- a/src/times.cc +++ b/src/times.cc @@ -191,7 +191,7 @@ namespace { shared_ptr<datetime_io_t> printed_datetime_io; shared_ptr<date_io_t> printed_date_io; - std::vector<shared_ptr<date_io_t> > readers; + std::deque<shared_ptr<date_io_t> > 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<date_io_t>(new date_io_t(format, true))); } void times_initialize() diff --git a/src/value.cc b/src/value.cc index 63e48333..99837832 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()) { @@ -1304,13 +1298,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 +1332,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; } @@ -1378,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); @@ -1410,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); @@ -1491,6 +1479,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 +1499,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 +1543,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 +1566,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 +1589,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 +1612,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; } @@ -1687,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: { @@ -1710,6 +1694,45 @@ value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const return NULL_VALUE; } +string value_t::label(optional<type_t> 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 _("<invalid>"); +} + void value_t::print(std::ostream& out, const int first_width, const int latter_width, @@ -1796,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<expr_t::ptr_op_t>()->print(out); + out << ">"; + } else { + out << "<#OBJECT>"; + } break; default: @@ -1873,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<expr_t::ptr_op_t>()->dump(out); else - out << "null"; + out << boost::unsafe_any_cast<const void *>(&as_any()); break; case SEQUENCE: { @@ -1991,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 4dfd591c..6fe24da6 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 @@ public: 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 @@ private: * The `type' member holds the value_t::type_t value representing * the type of the object stored. */ - variant<bool, // BOOLEAN - datetime_t, // DATETIME - date_t, // DATE - long, // INTEGER - amount_t, // AMOUNT - balance_t *, // BALANCE - string, // STRING - mask_t, // MASK - sequence_t *, // SEQUENCE - scope_t *, // SCOPE - expr_t * // EXPR + variant<bool, // BOOLEAN + datetime_t, // DATETIME + date_t, // DATE + long, // INTEGER + amount_t, // AMOUNT + balance_t *, // BALANCE + string, // STRING + mask_t, // MASK + sequence_t *, // SEQUENCE + scope_t *, // SCOPE + boost::any // ANY > data; type_t type; @@ -256,8 +255,7 @@ private: * subsequently be modified. */ void _dup() { - VERIFY(storage); - if (storage->refc > 1) + if (storage && storage->refc > 1) storage = new storage_t(*storage.get()); } @@ -354,10 +352,13 @@ public: 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 <typename T> + 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 +531,7 @@ public: * 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 +730,45 @@ public: } /** - * 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<type_t>, 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 <typename T> + bool is_any() const { + return (is_type(ANY) && + boost::get<boost::any>(storage->data).type() == typeid(T)); + } + boost::any& as_any_lval() { + VERIFY(is_any()); + _dup(); + return boost::get<boost::any>(storage->data); + } + template <typename T> + T& as_any_lval() { + return any_cast<T&>(as_any_lval()); + } + const boost::any& as_any() const { + VERIFY(is_any()); + return boost::get<boost::any>(storage->data); } - expr_t& as_expr_lval() const { - VERIFY(is_expr()); - return *boost::get<expr_t *>(storage->data); + template <typename T> + const T& as_any() const { + return any_cast<const T&>(as_any()); } - const expr_t& as_expr() const { - VERIFY(is_expr()); - return *boost::get<expr_t *>(storage->data); + void set_any(const boost::any& val) { + set_type(ANY); + storage->data = val; + } + template <typename T> + 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 @@ -904,39 +930,7 @@ public: /** * Informational methods. */ - string label(optional<type_t> 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 _("<invalid>"); - } + string label(optional<type_t> the_type = none) const; /** * Printing methods. @@ -993,6 +987,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 <typename T> inline value_t& add_or_set_value(value_t& lhs, const T& rhs) { if (lhs.is_null()) diff --git a/src/xact.cc b/src/xact.cc index 3b66598c..1188fd0f 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<xact_t>(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<post_t>(scope)); - expr_t& expr(args.get<expr_t&>(0)); + post_t& post(args.context<post_t>()); + expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(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<post_t>(scope)); - expr_t& expr(args.get<expr_t&>(0)); + post_t& post(args.context<post_t>()); + expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(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; @@ -634,7 +629,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<date_t::year_type> current_year) { posts_list initial_posts(xact.posts.begin(), xact.posts.end()); @@ -679,6 +675,33 @@ 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); + } + } + 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; if (post->amount.is_null()) { @@ -686,7 +709,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 +774,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); + } + } } } } @@ -767,13 +799,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); @@ -150,6 +150,32 @@ public: std::map<string, bool> memoized_results; + enum xact_expr_kind_t { + EXPR_GENERAL, + EXPR_ASSERTION, + EXPR_CHECK + }; + + typedef std::pair<expr_t, xact_expr_kind_t> check_expr_pair; + typedef std::list<check_expr_pair> check_expr_list; + + optional<check_expr_list> check_exprs; + + 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_tag_data_t> deferred_notes_list; + + optional<deferred_notes_list> deferred_notes; + auto_xact_t() : try_quick_match(true) { TRACE_CTOR(auto_xact_t, ""); } @@ -168,7 +194,17 @@ public: 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<date_t::year_type> = 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<date_t::year_type> current_year); #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -180,6 +216,8 @@ private: void serialize(Archive& ar, const unsigned int /* version */) { ar & boost::serialization::base_object<xact_base_t>(*this); ar & predicate; + ar & check_exprs; + ar & deferred_notes; } #endif // HAVE_BOOST_SERIALIZATION }; |