Redesigned the format_t class
This commit is contained in:
parent
c3535d06c8
commit
fb8be53edb
6 changed files with 97 additions and 86 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
40
src/format.h
40
src/format.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ struct symbol_t
|
|||
OPTION,
|
||||
PRECOMMAND,
|
||||
COMMAND,
|
||||
DIRECTIVE
|
||||
DIRECTIVE,
|
||||
FORMAT
|
||||
};
|
||||
|
||||
kind_t kind;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue