From c3535d06c89732a0ba4c13274702b0f48198ae79 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 8 Nov 2009 23:40:42 -0500 Subject: Redesigned the expr_t, predicate_t, query_t classes --- src/exprbase.h | 253 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 src/exprbase.h (limited to 'src/exprbase.h') diff --git a/src/exprbase.h b/src/exprbase.h new file mode 100644 index 00000000..605c30f7 --- /dev/null +++ b/src/exprbase.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup expr + */ + +/** + * @file exprbase.h + * @author John Wiegley + * + * @ingroup expr + * + * This class provides basic behavior for all the domain specific expression + * languages used in Leger: + * + * | Typename | Description | result_type | Derives | + * |-------------+----------------------------+-----------------+-------------| + * | expr_t | Value expressions | value_t | | + * | predicate_t | Special form of expr_t | bool | expr_t | + * | query_t | Report queries | bool | predicate_t | + * | period_t | Time periods and durations | date_interval_t | | + * | draft_t | Partially filled xacts | xact_t * | | + * | format_t | Format strings | string | | + */ +#ifndef _EXPRBASE_H +#define _EXPRBASE_H + +#include "utils.h" +#include "amount.h" + +namespace ledger { + +DECLARE_EXCEPTION(parse_error, std::runtime_error); +DECLARE_EXCEPTION(compile_error, std::runtime_error); +DECLARE_EXCEPTION(calc_error, std::runtime_error); +DECLARE_EXCEPTION(usage_error, std::runtime_error); + +class scope_t; +class call_scope_t; + +template +class expr_base_t +{ +public: + typedef ResultType result_type; + + typedef function func_t; + +protected: + scope_t * context; + string str; + bool compiled; + + virtual result_type real_calc(scope_t& scope) = 0; + +public: + expr_base_t(const expr_base_t& other) + : context(other.context), str(other.str), compiled(false) { + TRACE_CTOR(expr_base_t, "copy"); + } + expr_base_t(scope_t * _context = NULL) + : context(_context), compiled(false) + { + TRACE_CTOR(expr_base_t, "scope_t *"); + } + + ~expr_base_t() throw() { + TRACE_DTOR(expr_base_t); + } + + expr_base_t& operator=(const expr_base_t& _expr) { + if (this != &_expr) { + str = _expr.str; + context = _expr.context; + compiled = _expr.compiled; + } + return *this; + } + expr_base_t& operator=(const string& _expr) { + parse(_expr); + return *this; + } + + virtual operator bool() const throw() { + return ! str.empty(); + } + + virtual string text() { + return str; + } + void set_text(const string& txt) { + str = txt; + } + + void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) { + std::istringstream stream(str); + return parse(stream, flags, str); + } + virtual void parse(std::istream&, + const parse_flags_t& = PARSE_DEFAULT, + const optional& original_string = none) { + str = original_string ? *original_string : ""; + context = NULL; + compiled = false; + } + + void mark_uncompiled() { + compiled = false; + } + + void recompile(scope_t& scope) { + compiled = false; + compile(scope); + } + + virtual void compile(scope_t& scope) { + if (! compiled) { + // Derived classes need to do something here. + context = &scope; + compiled = true; + } + } + + result_type operator()(scope_t& scope) { + return calc(scope); + } + + result_type calc(scope_t& scope) + { + if (! compiled) { + if (SHOW_DEBUG("expr.compile")) { + DEBUG("expr.compile", "Before compilation:"); + dump(*_log_stream); + } + + compile(scope); + + if (SHOW_DEBUG("expr.compile")) { + DEBUG("expr.compile", "After compilation:"); + dump(*_log_stream); + } + } + + return real_calc(scope); + } + + result_type calc() { + assert(context); + return calc(*context); + } + + scope_t * get_context() { + return context; + } + void set_context(scope_t * scope) { + context = scope; + } + + virtual string context_to_str() const = 0; + + string print_to_str() const { + std::ostringstream out; + print(out); + return out.str(); + } + string dump_to_str() const { + std::ostringstream out; + dump(out); + return out.str(); + } + string preview_to_str(scope_t& scope) const { + std::ostringstream out; + preview(out); + return out.str(); + } + + virtual void print(std::ostream& out) const = 0; + virtual void dump(std::ostream& out) const = 0; + + result_type preview(std::ostream& out, scope_t& scope) const { + out << _("--- Input expression ---") << std::endl; + out << text() << std::endl; + + out << std::endl << _("--- Text as parsed ---") << std::endl; + print(out); + out << std::endl; + + out << std::endl << _("--- Expression tree ---") << std::endl; + dump(out); + + out << std::endl << _("--- Compiled tree ---") << std::endl; + compile(scope); + dump(out); + + out << std::endl << _("--- Result value ---") << std::endl; + return calc(); + } + +#if defined(HAVE_BOOST_SERIALIZATION) +private: + /** Serialization. */ + + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int /* version */) { + ar & context; + ar & str; + if (Archive::is_loading::value) + compiled = false; + } +#endif // HAVE_BOOST_SERIALIZATION +}; + +template +std::ostream& operator<<(std::ostream& out, + const expr_base_t& expr) { + expr.print(out); + return out; +} + +} // namespace ledger + +#endif // _EXPRBASE_H -- cgit v1.2.3 From fb8be53edb9d9fdd91fd906e9a4ce6c3e8e3adb3 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 9 Nov 2009 01:24:44 -0500 Subject: Redesigned the format_t class --- src/exprbase.h | 15 ++++++----- src/format.cc | 80 +++++++++++++++++++++++++++++++++------------------------- src/format.h | 40 +++++++++++++---------------- src/output.cc | 43 ++++++++++++++++--------------- src/precmd.cc | 2 +- src/scope.h | 3 ++- 6 files changed, 97 insertions(+), 86 deletions(-) (limited to 'src/exprbase.h') diff --git a/src/exprbase.h b/src/exprbase.h index 605c30f7..d2bf5a6d 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -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& original_string = none) { - str = original_string ? *original_string : ""; - context = NULL; - compiled = false; + set_text(original_string ? *original_string : ""); } 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; diff --git a/src/format.cc b/src/format.cc index f3f52f20..d949c350 100644 --- a/src/format.cc +++ b/src/format.cc @@ -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(data) << "'" << std::endl; + break; + case EXPR: + out << " expr: " << boost::get(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(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(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(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(elem->data); break; - case element_t::EXPR: + case element_t::EXPR: { + expr_t& expr(boost::get(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, diff --git a/src/format.h b/src/format.h index 8043594b..516b6d7e 100644 --- a/src/format.h +++ b/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 { + typedef expr_base_t 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 data; scoped_ptr 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& 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& tmpl = none) { + void parse_format(const string& _format, + const optional& 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 diff --git a/src/output.cc b/src/output.cc index eee84553..2a6f0c20 100644 --- a/src/output.cc +++ b/src/output.cc @@ -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(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(); diff --git a/src/precmd.cc b/src/precmd.cc index 277e7b89..92483dc8 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -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; diff --git a/src/scope.h b/src/scope.h index cd7d93c7..44ca3229 100644 --- a/src/scope.h +++ b/src/scope.h @@ -54,7 +54,8 @@ struct symbol_t OPTION, PRECOMMAND, COMMAND, - DIRECTIVE + DIRECTIVE, + FORMAT }; kind_t kind; -- cgit v1.2.3