Improvements to format parsing

Fixes #337
This commit is contained in:
John Wiegley 2012-03-02 10:58:16 -06:00
parent 8b9b9e9e72
commit 35ace8816a

View file

@ -70,6 +70,27 @@ void format_t::element_t::dump(std::ostream& out) const
} }
namespace { namespace {
struct format_mapping_t {
char letter;
const char * expr;
} single_letter_mappings[] = {
{ 'd', "date" },
{ 'S', "filename" },
{ 'B', "beg_pos" },
{ 'b', "beg_line" },
{ 'E', "end_pos" },
{ 'e', "end_line" },
{ 'X', "cleared" },
{ 'Y', "xact.cleared" },
{ 'C', "code" },
{ 'P', "payee" },
{ 'a', "account.name" },
{ 'A', "account" },
{ 't', "justify(scrub(display_amount), $min, $max, $left, color)" },
{ 'T', "justify(scrub(display_total), $min, $max, $left, color)" },
{ 'N', "note" },
};
expr_t parse_single_expression(const char *& p, bool single_expr = true) expr_t parse_single_expression(const char *& p, bool single_expr = true)
{ {
string temp(p); string temp(p);
@ -92,6 +113,13 @@ namespace {
} }
return expr; return expr;
} }
inline expr_t::ptr_op_t ident_node(const string& ident)
{
expr_t::ptr_op_t node(new expr_t::op_t(expr_t::op_t::IDENT));
node->set_ident(ident);
return node;
}
} }
format_t::element_t * format_t::parse_elements(const string& fmt, format_t::element_t * format_t::parse_elements(const string& fmt,
@ -172,6 +200,42 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
current->min_width = current->max_width; current->min_width = current->max_width;
} }
if (std::isalpha(*p)) {
bool found = false;
for (std::size_t i = 0; i < (sizeof(single_letter_mappings) /
sizeof(format_mapping_t)); i++) {
if (*p == single_letter_mappings[i].letter) {
std::ostringstream expr;
for (const char * ptr = single_letter_mappings[i].expr; *ptr; ){
if (*ptr == '$') {
const char * beg = ++ptr;
while (*ptr && std::isalpha(*ptr))
++ptr;
string::size_type klen = static_cast<string::size_type>(ptr - beg);
string keyword(beg, 0, klen);
if (keyword == "min")
expr << (current->min_width > 0 ?
static_cast<int>(current->min_width) : -1);
else if (keyword == "max")
expr << (current->max_width > 0 ?
static_cast<int>(current->max_width) : -1);
else if (keyword == "left")
expr << (current->has_flags(ELEMENT_ALIGN_LEFT) ? "false" : "true");
else
assert("Unrecognized format substitution keyword" == NULL);
} else {
expr << *ptr++;
}
}
current->type = element_t::EXPR;
current->data = expr_t(expr.str());
found = true;
break;
}
}
if (! found)
throw_(format_error, _("Unrecognized formatting character: %1") << *p);
} else {
switch (*p) { switch (*p) {
case '%': case '%':
current->type = element_t::STRING; current->type = element_t::STRING;
@ -207,87 +271,97 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
case '(': case '(':
case '{': { case '{': {
bool format_amount = *p == '{'; bool format_amount = *p == '{';
if (format_amount) p++;
current->type = element_t::EXPR; current->type = element_t::EXPR;
current->data = parse_single_expression(p, ! format_amount); current->data = parse_single_expression(p);
// Wrap the subexpression in calls to justify and scrub // Wrap the subexpression in calls to justify and scrub
if (format_amount) { if (! format_amount)
if (! *p || *(p + 1) != '}') break;
throw_(format_error, _("Expected closing brace"));
else
p++;
expr_t::ptr_op_t op = boost::get<expr_t>(current->data).get_op(); expr_t::ptr_op_t op = boost::get<expr_t>(current->data).get_op();
expr_t::ptr_op_t amount_op; expr_t::ptr_op_t call2_node(new expr_t::op_t(expr_t::op_t::O_CALL));
expr_t::ptr_op_t colorize_op; {
if (op->kind == expr_t::op_t::O_CONS) { call2_node->set_left(ident_node("justify"));
amount_op = op->left();
colorize_op = op->has_right() ? op->right() : NULL; {
} else { expr_t::ptr_op_t args3_node(new expr_t::op_t(expr_t::op_t::O_CONS));
amount_op = op; {
{
expr_t::ptr_op_t call1_node(new expr_t::op_t(expr_t::op_t::O_CALL));
{
call1_node->set_left(ident_node("scrub"));
call1_node->set_right(op->kind == expr_t::op_t::O_CONS ? op->left() : op);
} }
expr_t::ptr_op_t scrub_node(new expr_t::op_t(expr_t::op_t::IDENT)); args3_node->set_left(call1_node);
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 args2_node(new expr_t::op_t(expr_t::op_t::O_CONS));
{
{
expr_t::ptr_op_t arg1_node(new expr_t::op_t(expr_t::op_t::VALUE)); 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 ? arg1_node->set_value(current->min_width > 0 ?
long(current->min_width) : -1); long(current->min_width) : -1);
args2_node->set_left(arg1_node);
}
{
expr_t::ptr_op_t args1_node(new expr_t::op_t(expr_t::op_t::O_CONS));
{
{
expr_t::ptr_op_t arg2_node(new expr_t::op_t(expr_t::op_t::VALUE));
arg2_node->set_value(current->max_width > 0 ? arg2_node->set_value(current->max_width > 0 ?
long(current->max_width) : -1); long(current->max_width) : -1);
args1_node->set_left(arg2_node);
}
{
expr_t::ptr_op_t arg3_node(new expr_t::op_t(expr_t::op_t::VALUE));
arg3_node->set_value(! current->has_flags(ELEMENT_ALIGN_LEFT)); arg3_node->set_value(! current->has_flags(ELEMENT_ALIGN_LEFT));
args1_node->set_right(arg3_node);
}
}
args2_node->set_right(args1_node);
}
args3_node->set_right(args2_node);
}
}
call2_node->set_right(args3_node);
}
}
current->min_width = 0; current->min_width = 0;
current->max_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 seq1_node(new expr_t::op_t(expr_t::op_t::O_SEQ));
seq1_node->set_left(args3_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(seq1_node);
string prev_expr = boost::get<expr_t>(current->data).text(); string prev_expr = boost::get<expr_t>(current->data).text();
expr_t::ptr_op_t colorize_op;
if (op->kind == expr_t::op_t::O_CONS)
colorize_op = op->has_right() ? op->right() : NULL;
if (colorize_op) { 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 seq2_node(new expr_t::op_t(expr_t::op_t::O_SEQ));
seq2_node->set_left(args4_node);
expr_t::ptr_op_t call3_node(new expr_t::op_t(expr_t::op_t::O_CALL)); 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(seq2_node); call3_node->set_left(ident_node("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); // from above
args4_node->set_right(colorize_op);
}
call3_node->set_right(args4_node);
}
}
current->data = expr_t(call3_node); current->data = expr_t(call3_node);
} else { } else {
@ -295,7 +369,6 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
} }
boost::get<expr_t>(current->data).set_text(prev_expr); boost::get<expr_t>(current->data).set_text(prev_expr);
}
break; break;
} }
@ -303,6 +376,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
throw_(format_error, _("Unrecognized formatting character: %1") << *p); throw_(format_error, _("Unrecognized formatting character: %1") << *p);
} }
} }
}
if (q != buf) { if (q != buf) {
if (! result.get()) { if (! result.get()) {