Redesigned the format_t class

This commit is contained in:
John Wiegley 2009-11-09 01:24:44 -05:00
parent c3535d06c8
commit fb8be53edb
6 changed files with 97 additions and 86 deletions

View file

@ -119,6 +119,7 @@ public:
} }
void set_text(const string& txt) { void set_text(const string& txt) {
str = txt; str = txt;
compiled = false;
} }
void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) { void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) {
@ -128,9 +129,7 @@ public:
virtual void parse(std::istream&, virtual void parse(std::istream&,
const parse_flags_t& = PARSE_DEFAULT, const parse_flags_t& = PARSE_DEFAULT,
const optional<string>& original_string = none) { const optional<string>& original_string = none) {
str = original_string ? *original_string : "<stream>"; set_text(original_string ? *original_string : "<stream>");
context = NULL;
compiled = false;
} }
void mark_uncompiled() { void mark_uncompiled() {
@ -185,7 +184,9 @@ public:
context = scope; context = scope;
} }
virtual string context_to_str() const = 0; virtual string context_to_str() const {
return empty_string;
}
string print_to_str() const { string print_to_str() const {
std::ostringstream out; std::ostringstream out;
@ -203,8 +204,8 @@ public:
return out.str(); return out.str();
} }
virtual void print(std::ostream& out) const = 0; virtual void print(std::ostream&) const {}
virtual void dump(std::ostream& out) const = 0; virtual void dump(std::ostream&) const {}
result_type preview(std::ostream& out, scope_t& scope) const { result_type preview(std::ostream& out, scope_t& scope) const {
out << _("--- Input expression ---") << std::endl; out << _("--- Input expression ---") << std::endl;

View file

@ -61,8 +61,12 @@ void format_t::element_t::dump(std::ostream& out) const
out << std::dec << int(max_width); out << std::dec << int(max_width);
switch (type) { switch (type) {
case STRING: out << " str: '" << chars << "'" << std::endl; break; case STRING:
case EXPR: out << " expr: " << expr << std::endl; break; out << " str: '" << boost::get<string>(data) << "'" << std::endl;
break;
case EXPR:
out << " expr: " << boost::get<expr_t>(data) << std::endl;
break;
} }
} }
@ -117,7 +121,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
if (q != buf) { if (q != buf) {
current->type = element_t::STRING; current->type = element_t::STRING;
current->chars = string(buf, q); current->data = string(buf, q);
q = buf; q = buf;
current->next.reset(new element_t); current->next.reset(new element_t);
@ -128,14 +132,14 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
p++; p++;
current->type = element_t::STRING; current->type = element_t::STRING;
switch (*p) { switch (*p) {
case 'b': current->chars = "\b"; break; case 'b': current->data = string("\b"); break;
case 'f': current->chars = "\f"; break; case 'f': current->data = string("\f"); break;
case 'n': current->chars = "\n"; break; case 'n': current->data = string("\n"); break;
case 'r': current->chars = "\r"; break; case 'r': current->data = string("\r"); break;
case 't': current->chars = "\t"; break; case 't': current->data = string("\t"); break;
case 'v': current->chars = "\v"; break; case 'v': current->data = string("\v"); break;
case '\\': current->chars = "\\"; break; case '\\': current->data = string("\\"); break;
default: current->chars = string(1, *p); break; default: current->data = string(1, *p); break;
} }
continue; continue;
} }
@ -172,7 +176,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
switch (*p) { switch (*p) {
case '%': case '%':
current->type = element_t::STRING; current->type = element_t::STRING;
current->chars = "%"; current->data = string("%");
break; break;
case '$': { case '$': {
@ -207,7 +211,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
if (format_amount) p++; if (format_amount) p++;
current->type = element_t::EXPR; current->type = element_t::EXPR;
current->expr = parse_single_expression(p, ! format_amount); current->data = parse_single_expression(p, ! format_amount);
// Wrap the subexpression in calls to justify and scrub // Wrap the subexpression in calls to justify and scrub
if (format_amount) { if (format_amount) {
@ -216,7 +220,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
else else
p++; p++;
expr_t::ptr_op_t op = current->expr.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 amount_op;
expr_t::ptr_op_t colorize_op; expr_t::ptr_op_t colorize_op;
@ -238,8 +242,10 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
expr_t::ptr_op_t arg2_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)); 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); arg1_node->set_value(current->min_width > 0 ?
arg2_node->set_value(current->max_width > 0 ? long(current->max_width) : -1); 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)); arg3_node->set_value(! current->has_flags(ELEMENT_ALIGN_LEFT));
current->min_width = 0; current->min_width = 0;
@ -264,7 +270,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
call2_node->set_left(justify_node); call2_node->set_left(justify_node);
call2_node->set_right(args3_node); call2_node->set_right(args3_node);
string prev_expr = current->expr.text(); string prev_expr = boost::get<expr_t>(current->data).text();
if (colorize_op) { if (colorize_op) {
expr_t::ptr_op_t ansify_if_node(new expr_t::op_t(expr_t::op_t::IDENT)); expr_t::ptr_op_t ansify_if_node(new expr_t::op_t(expr_t::op_t::IDENT));
@ -278,21 +284,18 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
call3_node->set_left(ansify_if_node); call3_node->set_left(ansify_if_node);
call3_node->set_right(args4_node); call3_node->set_right(args4_node);
current->expr = expr_t(call3_node); current->data = expr_t(call3_node);
} else { } else {
current->expr = expr_t(call2_node); current->data = expr_t(call2_node);
} }
current->expr.set_text(prev_expr); boost::get<expr_t>(current->data).set_text(prev_expr);
} }
break; break;
} }
default: default:
current->type = element_t::EXPR; throw_(format_error, _("Unrecognized formatting character: %1") << *p);
current->chars = string(FMT_PREFIX) + *p;
current->expr.parse(current->chars);
break;
} }
} }
@ -305,14 +308,16 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
current = current->next.get(); current = current->next.get();
} }
current->type = element_t::STRING; current->type = element_t::STRING;
current->chars = string(buf, q); current->data = string(buf, q);
} }
return result.release(); return result.release();
} }
void format_t::format(std::ostream& out_str, scope_t& scope) string format_t::real_calc(scope_t& scope)
{ {
std::ostringstream out_str;
for (element_t * elem = elements.get(); elem; elem = elem->next.get()) { for (element_t * elem = elements.get(); elem; elem = elem->next.get()) {
std::ostringstream out; std::ostringstream out;
string name; string name;
@ -326,20 +331,22 @@ void format_t::format(std::ostream& out_str, scope_t& scope)
case element_t::STRING: case element_t::STRING:
if (elem->min_width > 0) if (elem->min_width > 0)
out.width(elem->min_width); out.width(elem->min_width);
out << elem->chars; out << boost::get<string>(elem->data);
break; break;
case element_t::EXPR: case element_t::EXPR: {
expr_t& expr(boost::get<expr_t>(elem->data));
try { try {
elem->expr.compile(scope);
expr.compile(scope);
value_t value; value_t value;
if (elem->expr.is_function()) { if (expr.is_function()) {
call_scope_t args(scope); call_scope_t args(scope);
args.push_back(long(elem->max_width)); args.push_back(long(elem->max_width));
value = elem->expr.get_function()(args); value = expr.get_function()(args);
} else { } else {
value = elem->expr.calc(scope); value = expr.calc(scope);
} }
DEBUG("format.expr", "value = (" << value << ")"); DEBUG("format.expr", "value = (" << value << ")");
@ -348,10 +355,11 @@ void format_t::format(std::ostream& out_str, scope_t& scope)
} }
catch (const calc_error&) { catch (const calc_error&) {
add_error_context(_("While calculating format expression:")); add_error_context(_("While calculating format expression:"));
add_error_context(elem->expr.context_to_str()); add_error_context(expr.context_to_str());
throw; throw;
} }
break; break;
}
default: default:
assert(false); assert(false);
@ -375,6 +383,8 @@ void format_t::format(std::ostream& out_str, scope_t& scope)
out_str << out.str(); out_str << out.str();
} }
} }
return out_str.str();
} }
string format_t::truncate(const unistring& ustr, string format_t::truncate(const unistring& ustr,

View file

@ -50,8 +50,10 @@ class unistring;
DECLARE_EXCEPTION(format_error, std::runtime_error); DECLARE_EXCEPTION(format_error, std::runtime_error);
class format_t : public noncopyable class format_t : public expr_base_t<string>
{ {
typedef expr_base_t<string> base_type;
struct element_t : public supports_flags<> struct element_t : public supports_flags<>
{ {
#define ELEMENT_ALIGN_LEFT 0x01 #define ELEMENT_ALIGN_LEFT 0x01
@ -61,9 +63,7 @@ class format_t : public noncopyable
kind_t type; kind_t type;
std::size_t min_width; std::size_t min_width;
std::size_t max_width; std::size_t max_width;
string chars; variant<string, expr_t> data;
expr_t expr;
scoped_ptr<struct element_t> next; scoped_ptr<struct element_t> next;
element_t() throw() element_t() throw()
@ -83,8 +83,7 @@ class format_t : public noncopyable
type = elem.type; type = elem.type;
min_width = elem.min_width; min_width = elem.min_width;
max_width = elem.max_width; max_width = elem.max_width;
chars = elem.chars; data = elem.data;
expr = elem.expr;
} }
return *this; return *this;
} }
@ -124,25 +123,28 @@ private:
const optional<format_t&>& tmpl); const optional<format_t&>& tmpl);
public: public:
format_t() { format_t() : base_type() {
TRACE_CTOR(format_t, ""); TRACE_CTOR(format_t, "");
} }
format_t(const string& _format) { format_t(const string& _str, scope_t * context = NULL)
: base_type(context) {
TRACE_CTOR(format_t, "const string&"); TRACE_CTOR(format_t, "const string&");
parse(_format); if (! _str.empty())
parse_format(_str);
} }
~format_t() { ~format_t() {
TRACE_DTOR(format_t); TRACE_DTOR(format_t);
} }
void parse(const string& _format, const optional<format_t&>& tmpl = none) { void parse_format(const string& _format,
const optional<format_t&>& tmpl = none) {
elements.reset(parse_elements(_format, tmpl)); elements.reset(parse_elements(_format, tmpl));
format_string = _format; set_text(_format);
} }
void format(std::ostream& out, scope_t& scope); virtual result_type real_calc(scope_t& scope);
void dump(std::ostream& out) const { virtual void dump(std::ostream& out) const {
for (const element_t * elem = elements.get(); for (const element_t * elem = elements.get();
elem; elem;
elem = elem->next.get()) elem = elem->next.get())
@ -154,12 +156,6 @@ public:
const std::size_t account_abbrev_length = 0); const std::size_t account_abbrev_length = 0);
}; };
#define FMT_PREFIX "fmt_"
#define FMT_PREFIX_LEN 4
#define WANT_FMT() \
(std::strncmp(p, FMT_PREFIX, FMT_PREFIX_LEN) == 0)
} // namespace ledger } // namespace ledger
#endif // _FORMAT_H #endif // _FORMAT_H

View file

@ -51,17 +51,19 @@ format_posts::format_posts(report_t& _report,
const char * f = format.c_str(); const char * f = format.c_str();
if (const char * p = std::strstr(f, "%/")) { if (const char * p = std::strstr(f, "%/")) {
first_line_format.parse(string(f, 0, p - f)); first_line_format.parse_format(string(f, 0, p - f));
const char * n = p + 2; const char * n = p + 2;
if (const char * p = std::strstr(n, "%/")) { if (const char * p = std::strstr(n, "%/")) {
next_lines_format.parse(string(n, 0, p - n), first_line_format); next_lines_format.parse_format(string(n, 0, p - n),
between_format.parse(string(p + 2), first_line_format); first_line_format);
between_format.parse_format(string(p + 2),
first_line_format);
} else { } else {
next_lines_format.parse(n, first_line_format); next_lines_format.parse_format(string(n), first_line_format);
} }
} else { } else {
first_line_format.parse(format); first_line_format.parse_format(format);
next_lines_format.parse(format); next_lines_format.parse_format(format);
} }
} }
@ -80,7 +82,7 @@ void format_posts::operator()(post_t& post)
if (last_xact != post.xact) { if (last_xact != post.xact) {
if (last_xact) { if (last_xact) {
bind_scope_t xact_scope(report, *last_xact); bind_scope_t xact_scope(report, *last_xact);
between_format.format(out, xact_scope); out << between_format(xact_scope);
} }
print_item(out, *post.xact); print_item(out, *post.xact);
out << '\n'; out << '\n';
@ -96,16 +98,16 @@ void format_posts::operator()(post_t& post)
if (last_xact != post.xact) { if (last_xact != post.xact) {
if (last_xact) { if (last_xact) {
bind_scope_t xact_scope(report, *last_xact); bind_scope_t xact_scope(report, *last_xact);
between_format.format(out, xact_scope); out << between_format(xact_scope);
} }
first_line_format.format(out, bound_scope); out << first_line_format(bound_scope);
last_xact = post.xact; last_xact = post.xact;
} }
else if (last_post && last_post->date() != post.date()) { else if (last_post && last_post->date() != post.date()) {
first_line_format.format(out, bound_scope); out << first_line_format(bound_scope);
} }
else { else {
next_lines_format.format(out, bound_scope); out << next_lines_format(bound_scope);
} }
post.xdata().add_flags(POST_EXT_DISPLAYED); post.xdata().add_flags(POST_EXT_DISPLAYED);
@ -122,17 +124,17 @@ format_accounts::format_accounts(report_t& _report,
const char * f = format.c_str(); const char * f = format.c_str();
if (const char * p = std::strstr(f, "%/")) { if (const char * p = std::strstr(f, "%/")) {
account_line_format.parse(string(f, 0, p - f)); account_line_format.parse_format(string(f, 0, p - f));
const char * n = p + 2; const char * n = p + 2;
if (const char * p = std::strstr(n, "%/")) { if (const char * p = std::strstr(n, "%/")) {
total_line_format.parse(string(n, 0, p - n), account_line_format); total_line_format.parse_format(string(n, 0, p - n), account_line_format);
separator_format.parse(string(p + 2), account_line_format); separator_format.parse_format(string(p + 2), account_line_format);
} else { } else {
total_line_format.parse(n, account_line_format); total_line_format.parse_format(n, account_line_format);
} }
} else { } else {
account_line_format.parse(format); account_line_format.parse_format(format);
total_line_format.parse(format, account_line_format); total_line_format.parse_format(format, account_line_format);
} }
} }
@ -148,7 +150,8 @@ std::size_t format_accounts::post_account(account_t& account, const bool flat)
account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED); account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
bind_scope_t bound_scope(report, account); bind_scope_t bound_scope(report, account);
account_line_format.format(report.output_stream, bound_scope); static_cast<std::ostream&>(report.output_stream)
<< account_line_format(bound_scope);
return 1; return 1;
} }
@ -213,8 +216,8 @@ void format_accounts::flush()
if (displayed > 1 && if (displayed > 1 &&
! report.HANDLED(no_total) && ! report.HANDLED(percent)) { ! report.HANDLED(no_total) && ! report.HANDLED(percent)) {
bind_scope_t bound_scope(report, *report.session.journal->master); bind_scope_t bound_scope(report, *report.session.journal->master);
separator_format.format(out, bound_scope); out << separator_format(bound_scope);
total_line_format.format(out, bound_scope); out << total_line_format(bound_scope);
} }
out.flush(); out.flush();

View file

@ -148,7 +148,7 @@ value_t format_command(call_scope_t& args)
out << std::endl << _("--- Formatted string ---") << std::endl; out << std::endl << _("--- Formatted string ---") << std::endl;
bind_scope_t bound_scope(args, *post); bind_scope_t bound_scope(args, *post);
out << '"'; out << '"';
fmt.format(out, bound_scope); out << fmt(bound_scope);
out << "\"\n"; out << "\"\n";
return NULL_VALUE; return NULL_VALUE;

View file

@ -54,7 +54,8 @@ struct symbol_t
OPTION, OPTION,
PRECOMMAND, PRECOMMAND,
COMMAND, COMMAND,
DIRECTIVE DIRECTIVE,
FORMAT
}; };
kind_t kind; kind_t kind;