Allow value expressions to gain access to option settings.

For example, "ledger eval options.limit" prints 0 (for false), but:
"ledger -l hello eval options.limit" print "hello"s, since the value of
options.limit, once set to a value, is that string.  For flag options,
such as -Y, eval prints 0 if unset, and 1 if set.

This feature allows value expressions to be conditionalized based on the
presence of user options.
This commit is contained in:
John Wiegley 2009-02-07 05:47:21 -04:00
parent 66d007db9d
commit ea9330adae
8 changed files with 321 additions and 261 deletions

View file

@ -238,12 +238,8 @@ int global_scope_t::execute_command_wrapper(strings_list args, bool at_repl)
return status;
}
expr_t::ptr_op_t global_scope_t::lookup(const string& name)
option_t<global_scope_t> * global_scope_t::lookup_option(const char * p)
{
const char * p = name.c_str();
switch (*p) {
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
switch (*p) {
case 'd':
OPT(debug_);
@ -263,7 +259,19 @@ expr_t::ptr_op_t global_scope_t::lookup(const string& name)
else OPT(version);
break;
}
return NULL;
}
expr_t::ptr_op_t global_scope_t::lookup(const string& name)
{
const char * p = name.c_str();
switch (*p) {
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
if (option_t<global_scope_t> * handler = lookup_option(p))
return MAKE_OPT_HANDLER(global_scope_t, handler);
}
break;
case 'p':
if (WANT_PRECMD()) { p += PRECMD_PREFIX_LEN;

View file

@ -109,6 +109,8 @@ See LICENSE file included with the distribution for details and disclaimer.";
out << std::endl;
}
option_t<global_scope_t> * lookup_option(const char * p);
virtual expr_t::ptr_op_t lookup(const string& name);
OPTION(global_scope_t, debug_);

View file

@ -130,12 +130,27 @@ public:
virtual void handler(call_scope_t& args) {
if (wants_arg)
value = args[0];
handled = true;
}
virtual value_t handler_wrapper(call_scope_t& args) {
handler(args);
return true;
}
virtual value_t operator()(call_scope_t& args) {
handler(args);
handled = true;
return true;
if (! args.empty()) {
return handler_wrapper(args);
}
else if (wants_arg) {
if (handled)
return string_value(str());
else
return false;
}
else {
return handled;
}
}
};
@ -156,9 +171,11 @@ public:
#define COPY_OPT(name, other) name ## _handler(other.name ## _handler)
#define MAKE_OPT_FUNCTOR(x) \
expr_t::op_t::wrap_functor(bind(&x ## _option_t::operator(), \
&x ## _handler, _1))
#define MAKE_OPT_HANDLER(type, x) \
expr_t::op_t::wrap_functor(bind(&option_t<type>::handler_wrapper, x, _1))
#define MAKE_OPT_FUNCTOR(type, x) \
expr_t::op_t::wrap_functor(bind(&option_t<type>::operator(), x, _1))
inline bool is_eq(const char * p, const char * n) {
// Test whether p matches n, substituting - in p for _ in n.
@ -166,25 +183,26 @@ inline bool is_eq(const char * p, const char * n) {
if (! (*p == '-' && *n == '_' ) && *p != *n)
return false;
}
return *p == *n;
// Ignore any trailing underscore
return *p == *n || (! *p && *n == '_' && ! *(n + 1));
}
#define OPT(name) \
if (is_eq(p, #name)) \
return ((name ## _handler).parent = this, MAKE_OPT_FUNCTOR(name))
return ((name ## _handler).parent = this, &(name ## _handler))
#define OPT_(name) \
if (! *(p + 1) || \
((name ## _handler).wants_arg && \
*(p + 1) == '_' && ! *(p + 2)) || \
is_eq(p, #name)) \
return ((name ## _handler).parent = this, MAKE_OPT_FUNCTOR(name))
return ((name ## _handler).parent = this, &(name ## _handler))
#define OPT_CH(name) \
if (! *(p + 1) || \
((name ## _handler).wants_arg && \
*(p + 1) == '_' && ! *(p + 2))) \
return ((name ## _handler).parent = this, MAKE_OPT_FUNCTOR(name))
return ((name ## _handler).parent = this, &(name ## _handler))
#define HANDLER(name) name ## _handler
#define HANDLED(name) HANDLER(name)

View file

@ -288,91 +288,8 @@ namespace {
};
}
expr_t::ptr_op_t report_t::lookup(const string& name)
option_t<report_t> * report_t::lookup_option(const char * p)
{
const char * p = name.c_str();
switch (*p) {
case 'a':
if (is_eq(p, "amount_expr"))
MAKE_FUNCTOR(report_t::fn_amount_expr);
break;
case 'c':
if (WANT_CMD()) { p += CMD_PREFIX_LEN;
switch (*p) {
case 'b':
if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance"))
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, HANDLER(balance_format_).str()),
*this));
break;
case 'c':
if (is_eq(p, "csv"))
return WRAP_FUNCTOR
(reporter<>(new format_xacts(*this, HANDLER(csv_format_).str()),
*this));
break;
case 'e':
if (is_eq(p, "equity"))
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_equity(*this, HANDLER(print_format_).str()), *this));
else if (is_eq(p, "entry"))
return NULL; // jww (2009-02-07): NYI
else if (is_eq(p, "emacs"))
return NULL; // jww (2009-02-07): NYI
break;
case 'p':
if (*(p + 1) == '\0' || is_eq(p, "print"))
return WRAP_FUNCTOR
(reporter<>(new format_xacts(*this, HANDLER(print_format_).str()),
*this));
else if (is_eq(p, "prices"))
return NULL; // jww (2009-02-07): NYI
else if (is_eq(p, "pricesdb"))
return NULL; // jww (2009-02-07): NYI
break;
case 'r':
if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register"))
return WRAP_FUNCTOR
(reporter<>(new format_xacts(*this, HANDLER(register_format_).str()),
*this));
break;
}
}
break;
case 'd':
if (is_eq(p, "display_total"))
return MAKE_FUNCTOR(report_t::fn_display_total);
else if (is_eq(p, "display_total"))
return MAKE_FUNCTOR(report_t::fn_display_amount);
else if (is_eq(p, "display_total"))
return MAKE_FUNCTOR(report_t::fn_display_date);
break;
case 'e':
if (is_eq(p, "escape"))
return MAKE_FUNCTOR(report_t::fn_escape);
break;
case 'j':
if (is_eq(p, "join"))
return MAKE_FUNCTOR(report_t::fn_join);
break;
case 'm':
if (is_eq(p, "market"))
return MAKE_FUNCTOR(report_t::fn_market_value);
break;
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
switch (*p) {
case '%':
OPT_CH(percentage);
@ -580,6 +497,99 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
else OPT(yearly);
break;
}
return NULL;
}
expr_t::ptr_op_t report_t::lookup(const string& name)
{
const char * p = name.c_str();
switch (*p) {
case 'a':
if (is_eq(p, "amount_expr"))
return MAKE_FUNCTOR(report_t::fn_amount_expr);
break;
case 'c':
if (WANT_CMD()) { p += CMD_PREFIX_LEN;
switch (*p) {
case 'b':
if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance"))
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, HANDLER(balance_format_).str()),
*this));
break;
case 'c':
if (is_eq(p, "csv"))
return WRAP_FUNCTOR
(reporter<>(new format_xacts(*this, HANDLER(csv_format_).str()),
*this));
break;
case 'e':
if (is_eq(p, "equity"))
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_equity(*this, HANDLER(print_format_).str()), *this));
else if (is_eq(p, "entry"))
return NULL; // jww (2009-02-07): NYI
else if (is_eq(p, "emacs"))
return NULL; // jww (2009-02-07): NYI
break;
case 'p':
if (*(p + 1) == '\0' || is_eq(p, "print"))
return WRAP_FUNCTOR
(reporter<>(new format_xacts(*this, HANDLER(print_format_).str()),
*this));
else if (is_eq(p, "prices"))
return NULL; // jww (2009-02-07): NYI
else if (is_eq(p, "pricesdb"))
return NULL; // jww (2009-02-07): NYI
break;
case 'r':
if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register"))
return WRAP_FUNCTOR
(reporter<>(new format_xacts(*this, HANDLER(register_format_).str()),
*this));
break;
}
}
break;
case 'd':
if (is_eq(p, "display_date"))
return MAKE_FUNCTOR(report_t::fn_display_date);
else if (is_eq(p, "display_amount"))
return MAKE_FUNCTOR(report_t::fn_display_amount);
else if (is_eq(p, "display_total"))
return MAKE_FUNCTOR(report_t::fn_display_total);
break;
case 'e':
if (is_eq(p, "escape"))
return MAKE_FUNCTOR(report_t::fn_escape);
break;
case 'j':
if (is_eq(p, "join"))
return MAKE_FUNCTOR(report_t::fn_join);
break;
case 'm':
if (is_eq(p, "market"))
return MAKE_FUNCTOR(report_t::fn_market_value);
break;
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
if (option_t<report_t> * handler = lookup_option(p))
return MAKE_OPT_HANDLER(report_t, handler);
}
else if (is_eq(p, "options")) {
return MAKE_FUNCTOR(report_t::fn_options);
}
break;
@ -623,6 +633,10 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
break;
}
// Check if they are trying to access an option's setting or value.
if (option_t<report_t> * handler = lookup_option(p))
return MAKE_OPT_FUNCTOR(report_t, handler);
return session.lookup(name);
}

View file

@ -132,6 +132,10 @@ public:
value_t fn_escape(call_scope_t& scope);
value_t fn_join(call_scope_t& scope);
value_t fn_options(call_scope_t& scope) {
return value_t(static_cast<scope_t *>(this));
}
void append_predicate(const string& str) {
if (HANDLED(limit_))
HANDLER(limit_).on(string("(") + HANDLER(limit_).str() + ")&" + str);
@ -160,6 +164,8 @@ public:
HANDLED(base));
}
option_t<report_t> * report_t::lookup_option(const char * p);
virtual expr_t::ptr_op_t lookup(const string& name);
/**

View file

@ -185,6 +185,9 @@ public:
std::size_t size() const {
return args.size();
}
bool empty() const {
return args.size() == 0;
}
};
/**

View file

@ -255,12 +255,8 @@ value_t session_t::resolve(const string& name, expr_t::scope_t& locals)
}
#endif
expr_t::ptr_op_t session_t::lookup(const string& name)
option_t<session_t> * session_t::lookup_option(const char * p)
{
const char * p = name.c_str();
switch (*p) {
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
switch (*p) {
case 'a':
OPT_(account_); // -a
@ -281,6 +277,17 @@ expr_t::ptr_op_t session_t::lookup(const string& name)
OPT_CH(download); // -Q
break;
}
return NULL;
}
expr_t::ptr_op_t session_t::lookup(const string& name)
{
const char * p = name.c_str();
switch (*p) {
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
if (option_t<session_t> * handler = lookup_option(p))
return MAKE_OPT_HANDLER(session_t, handler);
}
break;
}

View file

@ -107,6 +107,8 @@ public:
clean_accounts();
}
option_t<session_t> * lookup_option(const char * p);
virtual expr_t::ptr_op_t lookup(const string& name);
/**