diff options
author | John Wiegley <johnw@newartisans.com> | 2009-02-21 18:49:43 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-02-21 18:49:43 -0400 |
commit | 1f39d4148e588a5476e96550591131ae65ad0fe2 (patch) | |
tree | 86283fd983d6926c3f008f28e790b1d7ddee43ca /src | |
parent | 45e41b023a197151112199db893df70d21858a92 (diff) | |
download | fork-ledger-1f39d4148e588a5476e96550591131ae65ad0fe2.tar.gz fork-ledger-1f39d4148e588a5476e96550591131ae65ad0fe2.tar.bz2 fork-ledger-1f39d4148e588a5476e96550591131ae65ad0fe2.zip |
Create a new interactive_t helper class
The purpose of this class is much like Emacs' (interactive) form: it
allows a value expression function to declare exactly how many
arguments, and of what type, it intends to receive. It then offers
type-safe access to theese arguments in a consistent manner.
An example value expression function definition in C++:
value_t fn_foo(call_scope_t& scope) {
// We expect a string, an integer, and an optional date
interactive_t args(scope, "sl&d");
std::cout << "String = " << args.get<string>(0)
<< "Integer = " << args.get<long>(1) << std::endl;
if (args.has(2)) // was a date provided?
std::cout << "Date = " << args.get<date_t>(2) << std::endl;
return NULL_VALUE;
}
There is also an in_context_t<T> template, which finds the context type
T in the current scope hierarchy. The in_context_t then also acts as a
smart pointer to reference this context object, in addition to serving
the same duty as interactive_t. This combination of intent is solely
for the sake of brevity.
value_t fn_bar(call_scope_t& scope) {
in_context_t<account_t> env(scope, "sl&d");
std::cout << "Account name = " << env->fullname()
<< "String arg = " << env.get<string>(0)
<< std::endl;
return NULL_VALUE;
}
As you can see here, 'env' acts as a smart pointer to the required
context, and an object to extract the typed arguments.
Diffstat (limited to 'src')
-rw-r--r-- | src/account.cc | 9 | ||||
-rw-r--r-- | src/interactive.cc | 184 | ||||
-rw-r--r-- | src/interactive.h | 142 | ||||
-rw-r--r-- | src/precmd.cc | 1 | ||||
-rw-r--r-- | src/precmd.h | 4 | ||||
-rw-r--r-- | src/report.cc | 44 | ||||
-rw-r--r-- | src/report.h | 4 | ||||
-rw-r--r-- | src/scope.cc | 17 | ||||
-rw-r--r-- | src/scope.h | 97 | ||||
-rw-r--r-- | src/xact.cc | 15 |
10 files changed, 365 insertions, 152 deletions
diff --git a/src/account.cc b/src/account.cc index 741cc2f9..ad94e48a 100644 --- a/src/account.cc +++ b/src/account.cc @@ -30,6 +30,7 @@ */ #include "account.h" +#include "interactive.h" namespace ledger { @@ -152,11 +153,9 @@ std::ostream& operator<<(std::ostream& out, const account_t& account) namespace { value_t get_partial_name(call_scope_t& scope) { - account_t& account(find_scope<account_t>(scope)); - - var_t<bool> flatten(scope, 0); - - return string_value(account.partial_name(flatten ? *flatten : false)); + 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 diff --git a/src/interactive.cc b/src/interactive.cc new file mode 100644 index 00000000..3e2d6d5e --- /dev/null +++ b/src/interactive.cc @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "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++) { + 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(); + 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 't': + label = "a date or time"; + wrong_arg = (! next_arg->is_date() && + ! next_arg->is_datetime()); + break; + case 'v': + label = "any value"; + wrong_arg = false; + break; + case 'P': + label = "a pointer"; + wrong_arg = ! next_arg->is_pointer(); + break; + case 'S': + label = "a sequence"; + wrong_arg = ! next_arg->is_sequence(); + break; + case '&': + optional = true; + dont_skip = true; + break; + case '*': + optional = true; + exit_loop = true; + dont_skip = true; + break; + } + + if (wrong_arg) + vlabel = next_arg->label(); + + 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; + + if (wrong_arg) { + throw_(std::logic_error, + "Expected " << label << " for argument " << offset + << ", but received " << vlabel); + } + else if (! 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) { + buf << args[i]; + first = false; + } else { + buf << ' ' << args[i]; + } + } + + return buf.str(); +} + +} // namespace ledger diff --git a/src/interactive.h b/src/interactive.h new file mode 100644 index 00000000..766953ec --- /dev/null +++ b/src/interactive.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup expr + */ + +/** + * @file interactive.h + * @author John Wiegley + * + * @ingroup expr + * + * @brief Brief + * + * Long. + */ +#ifndef _INTERACTIVE_H +#define _INTERACTIVE_H + +#include "scope.h" + +namespace ledger { + +/** + * @brief Brief + * + * Long. + */ +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()) + return true; + return false; + } + + const value_t& value_at(std::size_t index) const { + assert(has(index)); + return args[index]; + } + + template <typename T> + T get(std::size_t index) const; +}; + +template <> +inline bool interactive_t::get<bool>(std::size_t index) const { + return value_at(index).to_boolean(); +} +template <> +inline long interactive_t::get<long>(std::size_t index) const { + return value_at(index).to_long(); +} +template <> +inline string interactive_t::get<string>(std::size_t index) const { + return value_at(index).to_string(); +} +template <> +inline mask_t interactive_t::get<mask_t>(std::size_t index) const { + return value_at(index).to_mask(); +} +template <> +inline date_t interactive_t::get<date_t>(std::size_t index) const { + return value_at(index).to_date(); +} +template <> +inline datetime_t interactive_t::get<datetime_t>(std::size_t index) const { + return value_at(index).to_datetime(); +} + +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/precmd.cc b/src/precmd.cc index c02be263..4b623904 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -30,6 +30,7 @@ */ #include "precmd.h" +#include "interactive.h" #include "report.h" namespace ledger { diff --git a/src/precmd.h b/src/precmd.h index d489b1af..b36d33e1 100644 --- a/src/precmd.h +++ b/src/precmd.h @@ -46,10 +46,12 @@ #ifndef _PRECMD_H #define _PRECMD_H -#include "scope.h" +#include "value.h" namespace ledger { +class call_scope_t; + value_t parse_command(call_scope_t& args); value_t eval_command(call_scope_t& args); value_t format_command(call_scope_t& args); diff --git a/src/report.cc b/src/report.cc index 1da4699b..a9c930b0 100644 --- a/src/report.cc +++ b/src/report.cc @@ -30,6 +30,7 @@ */ #include "report.h" +#include "interactive.h" #include "iterators.h" #include "filters.h" #include "chain.h" @@ -121,19 +122,20 @@ value_t report_t::fn_display_total(call_scope_t& scope) value_t report_t::fn_market_value(call_scope_t& args) { - var_t<datetime_t> date(args, 1); - var_t<string> in_terms_of(args, 2); + interactive_t env(args, "a&ts"); commodity_t * commodity = NULL; - if (in_terms_of) - commodity = amount_t::current_pool->find_or_create(*in_terms_of); + if (env.has(2)) + commodity = amount_t::current_pool->find_or_create(env.get<string>(2)); - DEBUG("report.market", "getting market value of: " << args[0]); + DEBUG("report.market", "getting market value of: " << env.value_at(0)); value_t result = - args[0].value(date ? optional<datetime_t>(*date) : optional<datetime_t>(), - commodity ? optional<commodity_t&>(*commodity) : - optional<commodity_t&>()); + env.value_at(0).value(env.has(1) ? + env.get<datetime_t>(1) : optional<datetime_t>(), + commodity ? + optional<commodity_t&>(*commodity) : + optional<commodity_t&>()); DEBUG("report.market", "result is: " << result); return result; @@ -160,28 +162,24 @@ value_t report_t::fn_quantity(call_scope_t& args) value_t report_t::fn_truncate(call_scope_t& args) { - var_t<long> width(args, 1); - var_t<long> account_abbrev(args, 2); - - return string_value(format_t::truncate(args[0].as_string(), - width && *width > 0 ? *width : 0, - account_abbrev ? *account_abbrev : -1)); + interactive_t env(args, "v&ll"); + return string_value(format_t::truncate + (env.get<string>(0), + env.has(1) && env.get<long>(1) > 0 ? env.get<long>(1) : 0, + env.has(2) ? env.get<long>(2) : -1)); } value_t report_t::fn_print(call_scope_t& args) { - var_t<long> first_width(args, 1); - var_t<long> latter_width(args, 2); - var_t<string> date_format(args, 3); - + interactive_t env(args, "vl&ls"); std::ostringstream out; - - args[0].strip_annotations(what_to_keep()) - .print(out, *first_width, latter_width ? *latter_width : -1, - date_format ? *date_format : + env.value_at(0) + .strip_annotations(what_to_keep()) + .print(out, env.get<long>(1), + env.has(2) ? env.get<long>(2) : -1, + env.has(3) ? env.get<string>(3) : (HANDLED(date_format_) ? HANDLER(date_format_).str() : optional<string>())); - return string_value(out.str()); } diff --git a/src/report.h b/src/report.h index a4020af8..f0408e89 100644 --- a/src/report.h +++ b/src/report.h @@ -502,13 +502,13 @@ public: "3 + date_width + payee_width + account_width + amount_width))" " %(print(strip(display_total), total_width, " "4 + date_width + payee_width + account_width + amount_width " - "+ total_width, true))\n%/" + "+ total_width))\n%/" "%(print(\" \", 2 + date_width + payee_width))" "%(print(truncate(account, account_width, abbrev_len), account_width))" " %(print(strip(display_amount), amount_width, 3 + date_width " "+ payee_width + account_width + amount_width))" " %(print(strip(display_total), total_width, 4 + date_width " - "+ payee_width + account_width + amount_width + total_width, true))\n"); + "+ payee_width + account_width + amount_width + total_width))\n"); }); OPTION(report_t, related); // -r diff --git a/src/scope.cc b/src/scope.cc index 660f1af2..f4b2dfab 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -60,21 +60,4 @@ expr_t::ptr_op_t symbol_scope_t::lookup(const string& name) return child_scope_t::lookup(name); } -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) { - buf << args[i]; - first = false; - } else { - buf << ' ' << args[i]; - } - } - - return buf.str(); -} - } // namespace ledger diff --git a/src/scope.h b/src/scope.h index b49e3ebe..1cc2858e 100644 --- a/src/scope.h +++ b/src/scope.h @@ -158,13 +158,9 @@ public: } void set_args(const value_t& _args) { - if (_args.is_sequence()) - args = _args; - else - args = _args.to_sequence(); + args = _args; } value_t& value() { - assert(args.is_null() || args.is_sequence()); return args; } @@ -291,97 +287,6 @@ public: T * operator->() { return value; } }; -/** - * @brief Brief - * - * Long. - */ -template <typename T> -class var_t : public noncopyable -{ - optional<value_t> value; - - var_t(); - -public: - var_t(scope_t& scope, const string& name) - { - TRACE_CTOR(var_t, "scope_t&, const string&"); - - try { - value = scope.resolve(name); - } - catch (...) { - DEBUG("scope.var_t", "Failed lookup var_t(\"" << name << "\")"); - value = none; - } - } - - var_t(call_scope_t& scope, const std::size_t idx) - { - TRACE_CTOR(var_t, "call_scope_t&, const std::size_t"); - - if (idx < scope.size()) - value = scope[idx]; - else - value = none; - } - - ~var_t() throw() { - TRACE_DTOR(var_t); - } - - operator bool() { return value; } - - T operator *(); - T operator *() const; - - T * operator->() { - return &**this; - } - const T * operator->() const { - return &**this; - } -}; - -template <> -inline bool var_t<bool>::operator *() { - return value->to_boolean(); -} -template <> -inline bool var_t<bool>::operator *() const { - return value->to_boolean(); -} - -template <> -inline long var_t<long>::operator *() { - return value->to_long(); -} -template <> -inline long var_t<long>::operator *() const { - return value->to_long(); -} - -template <> -inline string var_t<string>::operator *() { - return value->to_string(); -} -template <> -inline string var_t<string>::operator *() const { - return value->to_string(); -} - -template <> -inline datetime_t var_t<datetime_t>::operator *() { - return value->to_datetime(); -} -template <> -inline datetime_t var_t<datetime_t>::operator *() const { - return value->to_datetime(); -} - -string join_args(call_scope_t& args); - } // namespace ledger #endif // _SCOPE_H diff --git a/src/xact.cc b/src/xact.cc index 71fba7a7..ca0b9baf 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -32,6 +32,7 @@ #include "xact.h" #include "journal.h" #include "account.h" +#include "interactive.h" #include "format.h" namespace ledger { @@ -186,17 +187,15 @@ namespace { value_t get_account(call_scope_t& scope) { - xact_t& xact(find_scope<xact_t>(scope)); + in_context_t<xact_t> env(scope, "&l"); - var_t<long> max_width(scope, 0); + string name = env->reported_account()->fullname(); - string name = xact.reported_account()->fullname(); + if (env.has(0) && env.get<long>(0) > 2) + name = format_t::truncate(name, env.get<long>(0) - 2, true); - if (max_width && *max_width > 2) - name = format_t::truncate(name, *max_width - 2, true); - - if (xact.has_flags(XACT_VIRTUAL)) { - if (xact.must_balance()) + if (env->has_flags(XACT_VIRTUAL)) { + if (env->must_balance()) name = string("[") + name + "]"; else name = string("(") + name + ")"; |