Allow special %{} formatting sequence

Although %(amount) inserts an item's amount, it only does exactly that.
There is no special consideration like stripping of lot details, or
reduction to the base commodity, etc.  For those things, and to make
sure it was display in red if negative, the canonical form would be:

  %(ansify_if(justify(scrub(amount), 12, -1, true), red if amount < 0))

You can now use the special %{} form as an alternate to this:

  %12{amount, red if amount < 0}

The two expand to the same underlying expression.
This commit is contained in:
John Wiegley 2009-03-03 15:06:15 -04:00
parent de3bafc0d7
commit 4af1bfdde3
3 changed files with 122 additions and 32 deletions

View file

@ -59,6 +59,32 @@ void format_t::element_t::dump(std::ostream& out) const
} }
} }
namespace {
expr_t parse_single_expression(const char *& p, bool single_expr = true)
{
string temp(p);
ptristream str(const_cast<char *&>(p));
expr_t expr;
expr.parse(str, single_expr ? expr_t::PARSE_SINGLE : expr_t::PARSE_PARTIAL,
&temp);
if (str.eof()) {
expr.set_text(p);
p += std::strlen(p);
} else {
assert(str.good());
istream_pos_type pos = str.tellg();
expr.set_text(string(p, p + long(pos)));
p += long(pos) - 1;
// Don't gobble up any whitespace
const char * base = p;
while (p >= base && std::isspace(*p))
p--;
}
return expr;
}
}
format_t::element_t * format_t::parse_elements(const string& fmt) format_t::element_t * format_t::parse_elements(const string& fmt)
{ {
std::auto_ptr<element_t> result; std::auto_ptr<element_t> result;
@ -171,24 +197,88 @@ format_t::element_t * format_t::parse_elements(const string& fmt)
break; break;
case '(': case '(':
case '[': { case '{': {
std::istringstream str(p); bool format_amount = *p == '{';
current->type = element_t::EXPR; if (format_amount) p++;
string temp(p);
current->expr.parse(str, expr_t::PARSE_SINGLE, &temp);
if (str.eof()) {
current->expr.set_text(p);
p += std::strlen(p);
} else {
assert(str.good());
istream_pos_type pos = str.tellg();
current->expr.set_text(string(p, p + long(pos)));
p += long(pos) - 1;
// Don't gobble up any whitespace current->type = element_t::EXPR;
const char * base = p; current->expr = parse_single_expression(p, ! format_amount);
while (p >= base && std::isspace(*p))
p--; // Wrap the subexpression in calls to justify and scrub
if (format_amount) {
if (! *p || *(p + 1) != '}')
throw_(format_error, _("Expected closing brace"));
else
p++;
expr_t::ptr_op_t op = current->expr.get_op();
expr_t::ptr_op_t amount_op;
expr_t::ptr_op_t colorize_op;
if (op->kind == expr_t::op_t::O_CONS) {
amount_op = op->left();
colorize_op = op->right();
} else {
amount_op = op;
}
expr_t::ptr_op_t scrub_node(new expr_t::op_t(expr_t::op_t::IDENT));
scrub_node->set_ident("scrub");
expr_t::ptr_op_t call1_node(new expr_t::op_t(expr_t::op_t::O_CALL));
call1_node->set_left(scrub_node);
call1_node->set_right(amount_op);
expr_t::ptr_op_t arg1_node(new expr_t::op_t(expr_t::op_t::VALUE));
expr_t::ptr_op_t arg2_node(new expr_t::op_t(expr_t::op_t::VALUE));
expr_t::ptr_op_t arg3_node(new expr_t::op_t(expr_t::op_t::VALUE));
arg1_node->set_value(current->min_width > 0 ? long(current->min_width) : -1);
arg2_node->set_value(current->max_width > 0 ? long(current->max_width) : -1);
arg3_node->set_value(! current->has_flags(ELEMENT_ALIGN_LEFT));
current->min_width = 0;
current->max_width = 0;
expr_t::ptr_op_t args1_node(new expr_t::op_t(expr_t::op_t::O_CONS));
args1_node->set_left(arg2_node);
args1_node->set_right(arg3_node);
expr_t::ptr_op_t args2_node(new expr_t::op_t(expr_t::op_t::O_CONS));
args2_node->set_left(arg1_node);
args2_node->set_right(args1_node);
expr_t::ptr_op_t args3_node(new expr_t::op_t(expr_t::op_t::O_CONS));
args3_node->set_left(call1_node);
args3_node->set_right(args2_node);
expr_t::ptr_op_t justify_node(new expr_t::op_t(expr_t::op_t::IDENT));
justify_node->set_ident("justify");
expr_t::ptr_op_t call2_node(new expr_t::op_t(expr_t::op_t::O_CALL));
call2_node->set_left(justify_node);
call2_node->set_right(args3_node);
string prev_expr = current->expr.text();
if (colorize_op) {
expr_t::ptr_op_t ansify_if_node(new expr_t::op_t(expr_t::op_t::IDENT));
ansify_if_node->set_ident("ansify_if");
expr_t::ptr_op_t args4_node(new expr_t::op_t(expr_t::op_t::O_CONS));
args4_node->set_left(call2_node);
args4_node->set_right(colorize_op);
expr_t::ptr_op_t call3_node(new expr_t::op_t(expr_t::op_t::O_CALL));
call3_node->set_left(ansify_if_node);
call3_node->set_right(args4_node);
current->expr = expr_t(call3_node);
} else {
current->expr = expr_t(call2_node);
}
current->expr.set_text(prev_expr);
} }
break; break;
} }

View file

@ -280,9 +280,9 @@ value_t report_t::fn_format_date(call_scope_t& args)
value_t report_t::fn_ansify_if(call_scope_t& scope) value_t report_t::fn_ansify_if(call_scope_t& scope)
{ {
interactive_t args(scope, "vsb"); interactive_t args(scope, "v&s");
if (args.get<bool>(2)) { if (args.has(1)) {
string color = args.get<string>(1); string color = args.get<string>(1);
std::ostringstream buf; std::ostringstream buf;
if (color == "black") buf << "\e[30m"; if (color == "black") buf << "\e[30m";

View file

@ -237,12 +237,12 @@ public:
}); });
OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) { OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) {
on("%(ansify_if(justify(scrub(display_total), 20, -1, true), \"red\", " on("%(ansify_if(justify(scrub(display_total), 20, -1, true), "
" color & scrub(display_total) < 0))" " red if color & scrub(display_total) < 0))"
" %(!options.flat ? depth_spacer : \"\")" " %(!options.flat ? depth_spacer : \"\")"
"%-(ansify_if(partial_account(options.flat), \"blue\", color))\n%/" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/"
"%(ansify_if(justify(scrub(display_total), 20, -1, true), \"red\", " "%(ansify_if(justify(scrub(display_total), 20, -1, true), "
" color & scrub(display_total) < 0))\n%/" " red if color & scrub(display_total) < 0))\n%/"
"--------------------\n"); "--------------------\n");
}); });
@ -551,26 +551,26 @@ public:
}); });
OPTION__(report_t, register_format_, CTOR(report_t, register_format_) { OPTION__(report_t, register_format_, CTOR(report_t, register_format_) {
on("%(ansify_if(justify(date, date_width), \"green\", color & date > today))" on("%(ansify_if(justify(date, date_width), green if color & date > today))"
" %(ansify_if(justify(truncated(payee, payee_width), payee_width), " " %(ansify_if(justify(truncated(payee, payee_width), payee_width), "
" \"bold\", color & !cleared))" " bold if color & !cleared))"
" %(ansify_if(justify(truncated(account, account_width, abbrev_len), " " %(ansify_if(justify(truncated(account, account_width, abbrev_len), "
" account_width), \"blue\", color))" " account_width), blue if color))"
" %(ansify_if(justify(scrub(display_amount), amount_width, " " %(ansify_if(justify(scrub(display_amount), amount_width, "
" 3 + date_width + payee_width + account_width + amount_width, true), " " 3 + date_width + payee_width + account_width + amount_width, true), "
" \"red\", color & scrub(display_amount) < 0))" " red if color & scrub(display_amount) < 0))"
" %(ansify_if(justify(scrub(display_total), total_width, " " %(ansify_if(justify(scrub(display_total), total_width, "
" 4 + date_width + payee_width + account_width + amount_width " " 4 + date_width + payee_width + account_width + amount_width "
" + total_width, true), \"red\", color & scrub(display_amount) < 0))\n%/" " + total_width, true), red if color & scrub(display_amount) < 0))\n%/"
"%(justify(\" \", 2 + date_width + payee_width))" "%(justify(\" \", 2 + date_width + payee_width))"
"%(ansify_if(justify(truncated(account, account_width, abbrev_len), " "%(ansify_if(justify(truncated(account, account_width, abbrev_len), "
" account_width), \"blue\", color))" " account_width), blue if color))"
" %(ansify_if(justify(scrub(display_amount), amount_width, " " %(ansify_if(justify(scrub(display_amount), amount_width, "
" 3 + date_width + payee_width + account_width + amount_width, true), " " 3 + date_width + payee_width + account_width + amount_width, true), "
" \"red\", color & scrub(display_amount) < 0))" " red if color & scrub(display_amount) < 0))"
" %(ansify_if(justify(scrub(display_total), total_width, " " %(ansify_if(justify(scrub(display_total), total_width, "
" 4 + date_width + payee_width + account_width + amount_width " " 4 + date_width + payee_width + account_width + amount_width "
" + total_width, true), \"red\", color & scrub(display_amount) < 0))\n"); " + total_width, true), red if color & scrub(display_amount) < 0))\n");
}); });
OPTION(report_t, related); // -r OPTION(report_t, related); // -r