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 {
|
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,122 +200,168 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
|
||||||
current->min_width = current->max_width;
|
current->min_width = current->max_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (*p) {
|
if (std::isalpha(*p)) {
|
||||||
case '%':
|
bool found = false;
|
||||||
current->type = element_t::STRING;
|
for (std::size_t i = 0; i < (sizeof(single_letter_mappings) /
|
||||||
current->data = string("%");
|
sizeof(format_mapping_t)); i++) {
|
||||||
break;
|
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 '$': {
|
case '$': {
|
||||||
if (! tmpl)
|
if (! tmpl)
|
||||||
throw_(format_error, _("Prior field reference, but no template"));
|
throw_(format_error, _("Prior field reference, but no template"));
|
||||||
|
|
||||||
p++;
|
p++;
|
||||||
if (*p == '0' || (! std::isdigit(*p) &&
|
if (*p == '0' || (! std::isdigit(*p) &&
|
||||||
*p != 'A' && *p != 'B' && *p != 'C' &&
|
*p != 'A' && *p != 'B' && *p != 'C' &&
|
||||||
*p != 'D' && *p != 'E' && *p != 'F'))
|
*p != 'D' && *p != 'E' && *p != 'F'))
|
||||||
throw_(format_error, _("%$ field reference must be a digit from 1-9"));
|
throw_(format_error, _("%$ field reference must be a digit from 1-9"));
|
||||||
|
|
||||||
int index = std::isdigit(*p) ? *p - '0' : (*p - 'A' + 10);
|
int index = std::isdigit(*p) ? *p - '0' : (*p - 'A' + 10);
|
||||||
element_t * tmpl_elem = tmpl->elements.get();
|
element_t * tmpl_elem = tmpl->elements.get();
|
||||||
|
|
||||||
for (int i = 1; i < index && tmpl_elem; i++) {
|
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();
|
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)
|
case '(':
|
||||||
throw_(format_error, _("%$ reference to a non-existent prior field"));
|
case '{': {
|
||||||
|
bool format_amount = *p == '{';
|
||||||
|
|
||||||
*current = *tmpl_elem;
|
current->type = element_t::EXPR;
|
||||||
break;
|
current->data = parse_single_expression(p);
|
||||||
}
|
|
||||||
|
|
||||||
case '(':
|
// Wrap the subexpression in calls to justify and scrub
|
||||||
case '{': {
|
if (! format_amount)
|
||||||
bool format_amount = *p == '{';
|
break;
|
||||||
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++;
|
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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->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,12 +369,12 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw_(format_error, _("Unrecognized formatting character: %1") << *p);
|
throw_(format_error, _("Unrecognized formatting character: %1") << *p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue