diff --git a/entry.cc b/entry.cc index 287f6555..e9b932fe 100644 --- a/entry.cc +++ b/entry.cc @@ -357,17 +357,23 @@ void entry_t::add_xact(xact_t * xact) } namespace { - value_t get_date(call_scope_t& scope) - { - entry_t& entry(downcast(*scope.parent)); + value_t get_date(entry_t& entry) { return entry.date(); } - value_t get_payee(call_scope_t& scope) + value_t get_payee(entry_t& entry) { - entry_t& entry(downcast(*scope.parent)); return string_value(entry.payee); } + + typedef value_t (*entry_func_t)(entry_t&); + + template + value_t get_wrapper(call_scope_t& scope) + { + xact_t& xact(downcast(*scope.parent)); + return (*Func)(*xact.entry); + } } expr_t::ptr_op_t entry_t::lookup(const string& name) @@ -375,21 +381,14 @@ expr_t::ptr_op_t entry_t::lookup(const string& name) switch (name[0]) { case 'd': if (name[1] == '\0' || name == "date") - return WRAP_FUNCTOR(bind(get_date, _1)); + return WRAP_FUNCTOR(get_wrapper<&get_date>); break; case 'p': if (name[1] == '\0' || name == "payee") - return WRAP_FUNCTOR(bind(get_payee, _1)); + return WRAP_FUNCTOR(get_wrapper<&get_payee>); break; } - -#if 0 - // jww (2008-07-29): Should it go to the containing journal next, or to the - // session? - return entry->lookup(name); -#else return expr_t::ptr_op_t(); -#endif } bool entry_t::valid() const @@ -465,10 +464,10 @@ void auto_entry_t::extend_entry(entry_base_t& entry, bool post) } } -void extend_entry_base(journal_t * journal, entry_base_t& entry, bool post) +void extend_entry_base(journal_t * journal, entry_base_t& base, bool post) { foreach (auto_entry_t * entry, journal->auto_entries) - entry->extend_entry(*entry, post); + entry->extend_entry(base, post); } } // namespace ledger diff --git a/error.h b/error.h index 4eaf72a8..fafef08e 100644 --- a/error.h +++ b/error.h @@ -37,7 +37,7 @@ namespace ledger { extern std::ostringstream _desc_buffer; template -inline void throw_func(const std::string& message) { +inline void throw_func(const string& message) { _desc_buffer.str(""); throw T(message); } diff --git a/expr.cc b/expr.cc index c326944f..37433179 100644 --- a/expr.cc +++ b/expr.cc @@ -44,7 +44,7 @@ expr_t::expr_t() : compiled(false) } expr_t::expr_t(const expr_t& other) - : ptr(other.ptr), str(other.str), compiled(false) + : ptr(other.ptr), str(other.str), compiled(other.compiled) { TRACE_CTOR(expr_t, "copy"); } @@ -109,7 +109,7 @@ void expr_t::parse(std::istream& in, const unsigned int flags) void expr_t::compile(scope_t& scope) { - if (ptr.get()) { + if (ptr.get() && ! compiled) { ptr = ptr->compile(scope); compiled = true; } @@ -127,9 +127,16 @@ value_t expr_t::calc(scope_t& scope) bool expr_t::is_constant() const { + assert(compiled); return ptr.get() && ptr->is_value(); } +bool expr_t::is_function() const +{ + assert(compiled); + return ptr.get() && ptr->is_function(); +} + value_t& expr_t::constant_value() { assert(is_constant()); @@ -142,6 +149,12 @@ const value_t& expr_t::constant_value() const return ptr->as_value(); } +function_t& expr_t::get_function() +{ + assert(is_function()); + return ptr->as_function_lval(); +} + value_t expr_t::eval(const string& _expr, scope_t& scope) { return expr_t(_expr).calc(scope); diff --git a/expr.h b/expr.h index 3cdc77a6..8e1ee19a 100644 --- a/expr.h +++ b/expr.h @@ -102,9 +102,13 @@ public: value_t calc(scope_t& scope) const; bool is_constant() const; + bool is_function() const; + value_t& constant_value(); const value_t& constant_value() const; + function_t& get_function(); + void print(std::ostream& out, scope_t& scope) const; void dump(std::ostream& out) const; void read(const char *& data); diff --git a/format.cc b/format.cc index 1255d25b..772fa0bc 100644 --- a/format.cc +++ b/format.cc @@ -242,11 +242,24 @@ void format_t::format(std::ostream& out_str, scope_t& scope) case element_t::EXPR: try { - if (elem->max_width == 0) - elem->expr.calc(scope).dump(out, elem->min_width); - else - out << truncate(elem->expr.calc(scope).as_string(), - elem->max_width); + elem->expr.compile(scope); + + if (elem->expr.is_function()) { + call_scope_t args(scope); + args.push_back(long(elem->max_width)); + + if (elem->max_width == 0) + elem->expr.get_function()(args).dump(out, elem->min_width); + else + out << truncate(elem->expr.get_function()(args).as_string(), + elem->max_width); + } else { + if (elem->max_width == 0) + elem->expr.calc(scope).dump(out, elem->min_width); + else + out << truncate(elem->expr.calc(scope).as_string(), + elem->max_width); + } } catch (const calc_error&) { out << (string("%") + elem->chars); diff --git a/format.h b/format.h index 840ff564..585216bf 100644 --- a/format.h +++ b/format.h @@ -74,6 +74,8 @@ class format_t : public noncopyable static bool ansi_codes; static bool ansi_invert; + static element_t * parse_elements(const string& fmt); + public: format_t() { TRACE_CTOR(format_t, ""); @@ -100,11 +102,8 @@ public: elem->dump(out); } -private: - static element_t * parse_elements(const string& fmt); - static string truncate(const string& str, unsigned int width, - const bool is_account = false); + const bool is_account = false); }; } // namespace ledger diff --git a/main.cc b/main.cc index f58578fd..78a9433b 100644 --- a/main.cc +++ b/main.cc @@ -48,8 +48,8 @@ namespace ledger { value_t register_command(call_scope_t& args) { - var_t report(args, 0); - var_t ostream(args, 1); + ptr_t report(args, 0); + ptr_t ostream(args, 1); report->xacts_report (xact_handler_ptr(new format_xacts diff --git a/scope.h b/scope.h index 1bf50a3e..f0badb54 100644 --- a/scope.h +++ b/scope.h @@ -56,8 +56,28 @@ public: else return NULL_VALUE; } + + virtual optional find_scope(const std::type_info&, bool = true) { + return none; + } }; +template +inline T& find_scope(scope_t& scope, bool skip_this = true) { + optional found = scope.find_scope(typeid(T), skip_this); + assert(found); + return downcast(*found); +} + +template +inline optional maybe_find_scope(scope_t& scope, bool skip_this = true) { + optional found = scope.find_scope(typeid(T), skip_this); + if (found) + return optional(downcast(*found)); + else + return none; +} + class child_scope_t : public noncopyable, public scope_t { public: @@ -79,6 +99,17 @@ public: return parent->lookup(name); return expr_t::ptr_op_t(); } + + virtual optional find_scope(const std::type_info& type, + bool skip_this = true) { + for (scope_t * ptr = (skip_this ? parent : this); + ptr; + ptr = polymorphic_downcast(ptr)->parent) + if (typeid(ptr) == type) + return *ptr; + + return none; + } }; class symbol_scope_t : public child_scope_t @@ -152,30 +183,55 @@ public: }; template -class var_t : public noncopyable +class ptr_t : public noncopyable { T * value; - var_t(); + ptr_t(); public: - // jww (2008-07-21): Give a good exception here if we can't find "name" - var_t(scope_t& scope, const string& name) + ptr_t(scope_t& scope, const string& name) : value(scope.resolve(name).template as_pointer()) { - TRACE_CTOR(var_t, "scope_t&, const string&"); + TRACE_CTOR(ptr_t, "scope_t&, const string&"); } - var_t(call_scope_t& scope, const unsigned int idx) + ptr_t(call_scope_t& scope, const unsigned int idx) : value(scope[idx].template as_pointer()) { - TRACE_CTOR(var_t, "call_scope_t&, const unsigned int"); + TRACE_CTOR(ptr_t, "call_scope_t&, const unsigned int"); } - ~var_t() throw() { - TRACE_DTOR(var_t); + ~ptr_t() throw() { + TRACE_DTOR(ptr_t); } T& operator *() { return *value; } T * operator->() { return value; } }; +template +class var_t : public noncopyable +{ + value_t value; + + var_t(); + +public: + var_t(scope_t& scope, const string& name) : value(scope.resolve(name)) { + TRACE_CTOR(var_t, "scope_t&, const string&"); + } + var_t(call_scope_t& scope, const unsigned int idx) : value(scope[idx]) { + TRACE_CTOR(var_t, "call_scope_t&, const unsigned int"); + } + ~var_t() throw() { + TRACE_DTOR(var_t); + } + + operator T() { assert(false); } +}; + +template <> +inline var_t::operator long() { + return value.to_long(); +} + } // namespace ledger #endif // _SCOPE_H diff --git a/system.hh b/system.hh index ffefafb3..221b81ef 100644 --- a/system.hh +++ b/system.hh @@ -52,6 +52,7 @@ #include #include +#include #include #include #include diff --git a/textual.cc b/textual.cc index ed0e6509..f9499e6e 100644 --- a/textual.cc +++ b/textual.cc @@ -50,10 +50,10 @@ struct time_entry_t #endif namespace { - optional parse_amount_expr(std::istream& in, - amount_t& amount, - xact_t * xact, - unsigned short flags = 0) + optional parse_amount_expr(std::istream& in, + amount_t& amount, + xact_t * xact, + unsigned short flags = 0) { expr_t expr(in, flags | EXPR_PARSE_PARTIAL); @@ -79,8 +79,7 @@ namespace { } } -xact_t * parse_xact(char * line, account_t * account, - entry_t * entry = NULL) +xact_t * parse_xact(char * line, account_t * account, entry_t * entry = NULL) { std::istringstream in(line); @@ -299,23 +298,25 @@ xact_t * parse_xact(char * line, account_t * account, amount_t amt; try { -#if 0 - // jww (2008-08-02): This data must be saved so that it can be - // restored on "print". unsigned long beg = static_cast(in.tellg()); -#endif - if (parse_amount_expr(in, amt, xact.get(), - EXPR_PARSE_NO_MIGRATE)) + optional total_expr = + parse_amount_expr(in, amt, xact.get(), EXPR_PARSE_NO_MIGRATE); + + if (amt.is_null()) throw parse_error ("An assigned balance must evaluate to a constant value"); DEBUG("ledger.textual.parse", "line " << linenum << ": " << "XACT assign: parsed amt = " << amt); -#if 0 - unsigned long end = static_cast(in.tellg()); -#endif + if (total_expr) { + unsigned long end = static_cast(in.tellg()); + total_expr->set_text(string("=") + + string(line, beg, end - beg)); + } + + // jww (2008-08-02): Save total_expr somewhere! amount_t diff; if (xdata.value.is_amount()) { @@ -339,11 +340,10 @@ xact_t * parse_xact(char * line, account_t * account, "XACT assign: diff = " << diff); if (! diff.is_realzero()) { - if (xact->amount) { + if (! xact->amount.is_null()) { xact_t * temp = new xact_t(xact->account, diff, - XACT_GENERATED | - XACT_CALCULATED); + XACT_GENERATED | XACT_CALCULATED); entry->add_xact(temp); DEBUG("ledger.textual.parse", "line " << linenum << ": " << diff --git a/xact.cc b/xact.cc index 6fb0d636..8c9014f4 100644 --- a/xact.cc +++ b/xact.cc @@ -32,6 +32,7 @@ #include "xact.h" #include "journal.h" #include "account.h" +#include "format.h" namespace ledger { @@ -57,32 +58,31 @@ date_t xact_t::effective_date() const } namespace { - value_t get_amount(call_scope_t& scope) + value_t get_amount(xact_t& xact) { - xact_t& xact(downcast(*scope.parent)); return xact.amount; } - value_t get_date(call_scope_t& scope) + value_t get_date(xact_t& xact) { - xact_t& xact(downcast(*scope.parent)); - return xact.entry->date(); - } - - value_t get_payee(call_scope_t& scope) - { - xact_t& xact(downcast(*scope.parent)); - return string_value(xact.entry->payee); + return xact.date(); } value_t get_account(call_scope_t& scope) { xact_t& xact(downcast(*scope.parent)); + long width = 0; + if (scope.size() == 1) + width = var_t(scope, 0); + // jww (2008-08-02): Accept a width here so that we can abbreviate the // string. string name = xact.account->fullname(); + if (width > 2) + name = format_t::truncate(name, width - 2, true); + if (xact.has_flags(XACT_VIRTUAL)) { if (xact.must_balance()) name = string("[") + name + "]"; @@ -92,11 +92,16 @@ namespace { return string_value(name); } - value_t get_account_base(call_scope_t& scope) + value_t get_account_base(xact_t& xact) { assert(false); return NULL_VALUE; } + + template + value_t get_wrapper(call_scope_t& scope) { + return (*Func)(find_scope(scope)); + } } expr_t::ptr_op_t xact_t::lookup(const string& name) @@ -104,15 +109,15 @@ expr_t::ptr_op_t xact_t::lookup(const string& name) switch (name[0]) { case 'a': if (name[1] == '\0' || name == "amount") - return WRAP_FUNCTOR(get_amount); + return WRAP_FUNCTOR(get_wrapper<&get_amount>); else if (name == "account") return WRAP_FUNCTOR(get_account); else if (name == "account_base") - return WRAP_FUNCTOR(get_account_base); + return WRAP_FUNCTOR(get_wrapper<&get_account_base>); break; case 'd': if (name[1] == '\0' || name == "date") - return WRAP_FUNCTOR(get_date); + return WRAP_FUNCTOR(get_wrapper<&get_date>); break; case 'f': if (name.find("fmt_") == 0) { @@ -120,10 +125,6 @@ expr_t::ptr_op_t xact_t::lookup(const string& name) return WRAP_FUNCTOR(get_account); } break; - case 'p': - if (name[1] == '\0' || name == "payee") - return WRAP_FUNCTOR(get_payee); - break; } return entry->lookup(name); }