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

@ -118,7 +118,8 @@ public:
return str;
}
void set_text(const string& txt) {
str = txt;
str = txt;
compiled = false;
}
void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) {
@ -128,9 +129,7 @@ public:
virtual void parse(std::istream&,
const parse_flags_t& = PARSE_DEFAULT,
const optional<string>& original_string = none) {
str = original_string ? *original_string : "<stream>";
context = NULL;
compiled = false;
set_text(original_string ? *original_string : "<stream>");
}
void mark_uncompiled() {
@ -185,7 +184,9 @@ public:
context = scope;
}
virtual string context_to_str() const = 0;
virtual string context_to_str() const {
return empty_string;
}
string print_to_str() const {
std::ostringstream out;
@ -203,8 +204,8 @@ public:
return out.str();
}
virtual void print(std::ostream& out) const = 0;
virtual void dump(std::ostream& out) const = 0;
virtual void print(std::ostream&) const {}
virtual void dump(std::ostream&) const {}
result_type preview(std::ostream& out, scope_t& scope) const {
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);
switch (type) {
case STRING: out << " str: '" << chars << "'" << std::endl; break;
case EXPR: out << " expr: " << expr << std::endl; break;
case STRING:
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) {
current->type = element_t::STRING;
current->chars = string(buf, q);
current->data = string(buf, q);
q = buf;
current->next.reset(new element_t);
@ -128,14 +132,14 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
p++;
current->type = element_t::STRING;
switch (*p) {
case 'b': current->chars = "\b"; break;
case 'f': current->chars = "\f"; break;
case 'n': current->chars = "\n"; break;
case 'r': current->chars = "\r"; break;
case 't': current->chars = "\t"; break;
case 'v': current->chars = "\v"; break;
case '\\': current->chars = "\\"; break;
default: current->chars = string(1, *p); break;
case 'b': current->data = string("\b"); break;
case 'f': current->data = string("\f"); break;
case 'n': current->data = string("\n"); break;
case 'r': current->data = string("\r"); break;
case 't': current->data = string("\t"); break;
case 'v': current->data = string("\v"); break;
case '\\': current->data = string("\\"); break;
default: current->data = string(1, *p); break;
}
continue;
}
@ -171,8 +175,8 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
switch (*p) {
case '%':
current->type = element_t::STRING;
current->chars = "%";
current->type = element_t::STRING;
current->data = string("%");
break;
case '$': {
@ -207,7 +211,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
if (format_amount) p++;
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
if (format_amount) {
@ -216,7 +220,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
else
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 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 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);
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;
@ -264,7 +270,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
call2_node->set_left(justify_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) {
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_right(args4_node);
current->expr = expr_t(call3_node);
current->data = expr_t(call3_node);
} 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;
}
default:
current->type = element_t::EXPR;
current->chars = string(FMT_PREFIX) + *p;
current->expr.parse(current->chars);
break;
throw_(format_error, _("Unrecognized formatting character: %1") << *p);
}
}
@ -304,15 +307,17 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
current->next.reset(new element_t);
current = current->next.get();
}
current->type = element_t::STRING;
current->chars = string(buf, q);
current->type = element_t::STRING;
current->data = string(buf, q);
}
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()) {
std::ostringstream out;
string name;
@ -326,20 +331,22 @@ void format_t::format(std::ostream& out_str, scope_t& scope)
case element_t::STRING:
if (elem->min_width > 0)
out.width(elem->min_width);
out << elem->chars;
out << boost::get<string>(elem->data);
break;
case element_t::EXPR:
case element_t::EXPR: {
expr_t& expr(boost::get<expr_t>(elem->data));
try {
elem->expr.compile(scope);
expr.compile(scope);
value_t value;
if (elem->expr.is_function()) {
if (expr.is_function()) {
call_scope_t args(scope);
args.push_back(long(elem->max_width));
value = elem->expr.get_function()(args);
value = expr.get_function()(args);
} else {
value = elem->expr.calc(scope);
value = expr.calc(scope);
}
DEBUG("format.expr", "value = (" << value << ")");
@ -348,10 +355,11 @@ void format_t::format(std::ostream& out_str, scope_t& scope)
}
catch (const calc_error&) {
add_error_context(_("While calculating format expression:"));
add_error_context(elem->expr.context_to_str());
add_error_context(expr.context_to_str());
throw;
}
break;
}
default:
assert(false);
@ -375,6 +383,8 @@ void format_t::format(std::ostream& out_str, scope_t& scope)
out_str << out.str();
}
}
return out_str.str();
}
string format_t::truncate(const unistring& ustr,

View file

@ -50,20 +50,20 @@ class unistring;
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<>
{
#define ELEMENT_ALIGN_LEFT 0x01
enum kind_t { STRING, EXPR };
kind_t type;
std::size_t min_width;
std::size_t max_width;
string chars;
expr_t expr;
kind_t type;
std::size_t min_width;
std::size_t max_width;
variant<string, expr_t> data;
scoped_ptr<struct element_t> next;
element_t() throw()
@ -83,8 +83,7 @@ class format_t : public noncopyable
type = elem.type;
min_width = elem.min_width;
max_width = elem.max_width;
chars = elem.chars;
expr = elem.expr;
data = elem.data;
}
return *this;
}
@ -124,25 +123,28 @@ private:
const optional<format_t&>& tmpl);
public:
format_t() {
format_t() : base_type() {
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&");
parse(_format);
if (! _str.empty())
parse_format(_str);
}
~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));
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();
elem;
elem = elem->next.get())
@ -154,12 +156,6 @@ public:
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
#endif // _FORMAT_H

View file

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

View file

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

View file

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