summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/account.cc79
-rw-r--r--src/account.h4
-rw-r--r--src/convert.cc8
-rw-r--r--src/expr.h20
-rw-r--r--src/format.cc5
-rw-r--r--src/global.h1
-rw-r--r--src/interactive.cc200
-rw-r--r--src/interactive.h164
-rw-r--r--src/item.cc13
-rw-r--r--src/journal.cc10
-rw-r--r--src/journal.h6
-rw-r--r--src/op.cc68
-rw-r--r--src/op.h3
-rw-r--r--src/option.h4
-rw-r--r--src/parser.cc4
-rw-r--r--src/post.cc119
-rw-r--r--src/precmd.cc16
-rw-r--r--src/pyinterp.cc2
-rw-r--r--src/pyinterp.h3
-rw-r--r--src/report.cc213
-rw-r--r--src/report.h55
-rw-r--r--src/scope.cc19
-rw-r--r--src/scope.h375
-rw-r--r--src/session.cc24
-rw-r--r--src/session.h9
-rw-r--r--src/system.hh.in1
-rw-r--r--src/textual.cc174
-rw-r--r--src/times.cc4
-rw-r--r--src/value.cc146
-rw-r--r--src/value.h122
-rw-r--r--src/xact.cc73
-rw-r--r--src/xact.h40
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_))
diff --git a/src/expr.h b/src/expr.h
index fa49cdc4..e6eeebf6 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -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() {
diff --git a/src/op.cc b/src/op.cc
index 9497e068..c2001ec3 100644
--- a/src/op.cc
+++ b/src/op.cc
@@ -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;
diff --git a/src/op.h b/src/op.h
index 27fdea3b..aa591b28 100644
--- a/src/op.h
+++ b/src/op.h
@@ -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);
diff --git a/src/xact.h b/src/xact.h
index 2c3c7d05..a3c639b9 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -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
};