parent
8b9b9e9e72
commit
35ace8816a
1 changed files with 173 additions and 99 deletions
272
src/format.cc
272
src/format.cc
|
|
@ -70,6 +70,27 @@ void format_t::element_t::dump(std::ostream& out) const
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
string temp(p);
|
||||
|
|
@ -92,6 +113,13 @@ namespace {
|
|||
}
|
||||
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,
|
||||
|
|
@ -172,122 +200,168 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
|
|||
current->min_width = current->max_width;
|
||||
}
|
||||
|
||||
switch (*p) {
|
||||
case '%':
|
||||
current->type = element_t::STRING;
|
||||
current->data = string("%");
|
||||
break;
|
||||
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) {
|
||||
case '%':
|
||||
current->type = element_t::STRING;
|
||||
current->data = string("%");
|
||||
break;
|
||||
|
||||
case '$': {
|
||||
if (! tmpl)
|
||||
throw_(format_error, _("Prior field reference, but no template"));
|
||||
case '$': {
|
||||
if (! tmpl)
|
||||
throw_(format_error, _("Prior field reference, but no template"));
|
||||
|
||||
p++;
|
||||
if (*p == '0' || (! std::isdigit(*p) &&
|
||||
*p != 'A' && *p != 'B' && *p != 'C' &&
|
||||
*p != 'D' && *p != 'E' && *p != 'F'))
|
||||
throw_(format_error, _("%$ field reference must be a digit from 1-9"));
|
||||
p++;
|
||||
if (*p == '0' || (! std::isdigit(*p) &&
|
||||
*p != 'A' && *p != 'B' && *p != 'C' &&
|
||||
*p != 'D' && *p != 'E' && *p != 'F'))
|
||||
throw_(format_error, _("%$ field reference must be a digit from 1-9"));
|
||||
|
||||
int index = std::isdigit(*p) ? *p - '0' : (*p - 'A' + 10);
|
||||
element_t * tmpl_elem = tmpl->elements.get();
|
||||
int index = std::isdigit(*p) ? *p - '0' : (*p - 'A' + 10);
|
||||
element_t * tmpl_elem = tmpl->elements.get();
|
||||
|
||||
for (int i = 1; i < index && tmpl_elem; i++) {
|
||||
tmpl_elem = tmpl_elem->next.get();
|
||||
while (tmpl_elem && tmpl_elem->type != element_t::EXPR)
|
||||
for (int i = 1; i < index && tmpl_elem; i++) {
|
||||
tmpl_elem = tmpl_elem->next.get();
|
||||
while (tmpl_elem && tmpl_elem->type != element_t::EXPR)
|
||||
tmpl_elem = tmpl_elem->next.get();
|
||||
}
|
||||
|
||||
if (! tmpl_elem)
|
||||
throw_(format_error, _("%$ reference to a non-existent prior field"));
|
||||
|
||||
*current = *tmpl_elem;
|
||||
break;
|
||||
}
|
||||
|
||||
if (! tmpl_elem)
|
||||
throw_(format_error, _("%$ reference to a non-existent prior field"));
|
||||
case '(':
|
||||
case '{': {
|
||||
bool format_amount = *p == '{';
|
||||
|
||||
*current = *tmpl_elem;
|
||||
break;
|
||||
}
|
||||
current->type = element_t::EXPR;
|
||||
current->data = parse_single_expression(p);
|
||||
|
||||
case '(':
|
||||
case '{': {
|
||||
bool format_amount = *p == '{';
|
||||
if (format_amount) p++;
|
||||
|
||||
current->type = element_t::EXPR;
|
||||
current->data = parse_single_expression(p, ! format_amount);
|
||||
|
||||
// Wrap the subexpression in calls to justify and scrub
|
||||
if (format_amount) {
|
||||
if (! *p || *(p + 1) != '}')
|
||||
throw_(format_error, _("Expected closing brace"));
|
||||
else
|
||||
p++;
|
||||
// Wrap the subexpression in calls to justify and scrub
|
||||
if (! format_amount)
|
||||
break;
|
||||
|
||||
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 colorize_op;
|
||||
if (op->kind == expr_t::op_t::O_CONS) {
|
||||
amount_op = op->left();
|
||||
colorize_op = op->has_right() ? op->right() : NULL;
|
||||
} else {
|
||||
amount_op = op;
|
||||
expr_t::ptr_op_t call2_node(new expr_t::op_t(expr_t::op_t::O_CALL));
|
||||
{
|
||||
call2_node->set_left(ident_node("justify"));
|
||||
|
||||
{
|
||||
expr_t::ptr_op_t args3_node(new expr_t::op_t(expr_t::op_t::O_CONS));
|
||||
{
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
args3_node->set_left(call1_node);
|
||||
}
|
||||
|
||||
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));
|
||||
arg1_node->set_value(current->min_width > 0 ?
|
||||
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 ?
|
||||
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));
|
||||
|
||||
args1_node->set_right(arg3_node);
|
||||
}
|
||||
}
|
||||
|
||||
args2_node->set_right(args1_node);
|
||||
}
|
||||
|
||||
args3_node->set_right(args2_node);
|
||||
}
|
||||
}
|
||||
|
||||
call2_node->set_right(args3_node);
|
||||
}
|
||||
}
|
||||
|
||||
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 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();
|
||||
|
||||
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) {
|
||||
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));
|
||||
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);
|
||||
} else {
|
||||
|
|
@ -295,12 +369,12 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
|
|||
}
|
||||
|
||||
boost::get<expr_t>(current->data).set_text(prev_expr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw_(format_error, _("Unrecognized formatting character: %1") << *p);
|
||||
default:
|
||||
throw_(format_error, _("Unrecognized formatting character: %1") << *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue