From bc9ff7095fbfa1812e4f47dbf8531dec76cd0d00 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 20 Feb 2012 17:20:16 -0600 Subject: Introduced a new SCOPE expression terminal --- src/compare.cc | 2 +- src/format.cc | 2 +- src/op.cc | 93 +++++++++++++++++++++++++++++++++++----------------------- src/op.h | 51 +++++++++++++++++++++++++------- src/parser.cc | 33 +++++++++++---------- src/value.cc | 2 +- 6 files changed, 118 insertions(+), 65 deletions(-) (limited to 'src') diff --git a/src/compare.cc b/src/compare.cc index 12114c7d..cdc96a86 100644 --- a/src/compare.cc +++ b/src/compare.cc @@ -44,7 +44,7 @@ void push_sort_value(std::list& sort_values, if (node->kind == expr_t::op_t::O_CONS) { while (node && node->kind == expr_t::op_t::O_CONS) { push_sort_value(sort_values, node->left(), scope); - node = node->right(); + node = node->has_right() ? node->right() : NULL; } } else { bool inverted = false; diff --git a/src/format.cc b/src/format.cc index 93e7ea08..65c06488 100644 --- a/src/format.cc +++ b/src/format.cc @@ -225,7 +225,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt, expr_t::ptr_op_t colorize_op; if (op->kind == expr_t::op_t::O_CONS) { amount_op = op->left(); - colorize_op = op->right(); + colorize_op = op->has_right() ? op->right() : NULL; } else { amount_op = op; } diff --git a/src/op.cc b/src/op.cc index 537f5371..d5a589c8 100644 --- a/src/op.cc +++ b/src/op.cc @@ -50,7 +50,7 @@ namespace { expr_t::ptr_op_t value_op; if (next->kind == expr_t::op_t::O_CONS) { value_op = next->left(); - next = next->right(); + next = next->has_right() ? next->right() : NULL; } else { value_op = next; next = NULL; @@ -78,10 +78,12 @@ namespace { expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) { + scope_t * scope_ptr = &scope; + if (is_ident()) { DEBUG("expr.compile", "lookup: " << as_ident()); - if (ptr_op_t def = scope.lookup(symbol_t::FUNCTION, as_ident())) { + if (ptr_op_t def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident())) { // Identifier references are first looked up at the point of // definition, and then at the point of every use if they could // not be found there. @@ -91,21 +93,26 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) def->dump(*_log_stream, 0); } #endif // defined(DEBUG_ON) - return copy(def); + return copy(def); } else if (left()) { return copy(); } return this; } - - if (kind < TERMINALS) + else if (is_scope()) { + shared_ptr subscope(new symbol_scope_t(scope)); + DEBUG("expr.compile", "creating scope"); + set_scope(subscope); + scope_ptr = subscope.get(); + } + else if (kind < TERMINALS) { return this; - - if (kind == O_DEFINE) { + } + else if (kind == O_DEFINE) { switch (left()->kind) { case IDENT: - scope.define(symbol_t::FUNCTION, left()->as_ident(), right()); + scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), right()); break; case O_CALL: if (left()->left()->is_ident()) { @@ -113,7 +120,8 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) node->set_left(left()->right()); node->set_right(right()); - scope.define(symbol_t::FUNCTION, left()->left()->as_ident(), node); + scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(), + node); } else { throw_(compile_error, _("Invalid function definition")); } @@ -121,13 +129,13 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) default: throw_(compile_error, _("Invalid function definition")); } - return wrap_value(value_t()); + return wrap_value(NULL_VALUE); } - ptr_op_t lhs(left()->compile(scope, depth)); + ptr_op_t lhs(left()->compile(*scope_ptr, depth)); ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ? (kind == O_LOOKUP ? right() : - right()->compile(scope, depth)) : NULL); + right()->compile(*scope_ptr, depth)) : NULL); if (lhs == left() && (! rhs || rhs == right())) return this; @@ -136,22 +144,19 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) // Reduce constants immediately if possible if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value())) - return wrap_value(intermediate->calc(scope, NULL, depth)); + return wrap_value(intermediate->calc(*scope_ptr, NULL, depth)); return intermediate; } value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) { -#if defined(DEBUG_ON) - bool skip_debug = false; -#endif try { value_t result; #if defined(DEBUG_ON) - if (! skip_debug && SHOW_DEBUG("expr.calc")) { + if (SHOW_DEBUG("expr.calc")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; ledger::_log_buffer << op_context(this) << " => ..."; @@ -196,12 +201,18 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) call_scope_t call_args(scope, locus, depth); result = as_function()(call_args); check_type_context(scope, result); -#if defined(DEBUG_ON) - skip_debug = true; -#endif break; } + case SCOPE: + if (is_scope_unset()) { + symbol_scope_t subscope(scope); + result = left()->calc(subscope, locus, depth + 1); + } else { + result = left()->calc(*as_scope(), locus, depth + 1); + } + break; + case O_LAMBDA: { call_scope_t& call_args(downcast(scope)); std::size_t args_count(call_args.size()); @@ -267,7 +278,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) throw_(calc_error, _("Calling unknown function '%1'") << name); #if defined(DEBUG_ON) - if (! skip_debug && SHOW_DEBUG("expr.calc")) { + if (SHOW_DEBUG("expr.calc")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; ledger::_log_buffer << " args: "; @@ -382,7 +393,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) ptr_op_t value_op; if (next->kind == O_CONS) { value_op = next->left(); - next = next->right(); + next = next->has_right() ? next->right() : NULL; } else { value_op = next; next = NULL; @@ -395,14 +406,13 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; case O_SEQ: { - symbol_scope_t seq_scope(scope); - - // An O_SEQ is very similar to an O_CONS except that only the last result - // value in the series is kept. O_CONS builds up a list. + // An O_SEQ is very similar to an O_CONS except that only the last + // result value in the series is kept. O_CONS builds up a list. // - // Another feature of O_SEQ is that it pushes a new symbol scope onto the - // stack. - result = left()->calc(seq_scope, locus, depth + 1); + // Another feature of O_SEQ is that it pushes a new symbol scope + // onto the stack. We evaluate the left side here to catch any + // side-effects, such as definitions in the case of 'x = 1; x'. + result = left()->calc(scope, locus, depth + 1); if (has_right()) { ptr_op_t next = right(); @@ -415,7 +425,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) value_op = next; next = NULL; } - result = value_op->calc(seq_scope, locus, depth + 1); + result = value_op->calc(scope, locus, depth + 1); } } break; @@ -426,7 +436,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) } #if defined(DEBUG_ON) - if (! skip_debug && SHOW_DEBUG("expr.calc")) { + if (SHOW_DEBUG("expr.calc")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; ledger::_log_buffer << op_context(this) << " => "; @@ -476,9 +486,8 @@ namespace { if (op->has_right()) { out << "; "; - - if (op->right()->kind == expr_t::op_t::O_CONS) - found = print_cons(out, op->right(), context); + if (op->right()->kind == expr_t::op_t::O_SEQ) + found = print_seq(out, op->right(), context); else if (op->right()->print(out, context)) found = true; } @@ -515,6 +524,11 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const out << ""; break; + case SCOPE: + if (left() && left()->print(out, context)) + found = true; + break; + case O_NOT: out << "! "; if (left() && left()->print(out, context)) @@ -625,7 +639,6 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const case O_CONS: found = print_cons(out, this, context); break; - case O_SEQ: found = print_seq(out, this, context); break; @@ -726,6 +739,14 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const out << "FUNCTION"; break; + case SCOPE: + out << "SCOPE: "; + if (is_scope_unset()) + out << "null"; + else + out << as_scope().get(); + break; + case O_DEFINE: out << "O_DEFINE"; break; case O_LOOKUP: out << "O_LOOKUP"; break; case O_LAMBDA: out << "O_LAMBDA"; break; @@ -765,7 +786,7 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const // An identifier is a special non-terminal, in that its left() can // hold the compiled definition of the identifier. - if (kind > TERMINALS || is_ident()) { + if (kind > TERMINALS || is_ident() || is_scope()) { if (left()) { left()->dump(out, depth + 1); if (kind > UNARY_OPERATORS && has_right()) diff --git a/src/op.h b/src/op.h index 6f7d7904..c4d353dc 100644 --- a/src/op.h +++ b/src/op.h @@ -58,10 +58,12 @@ private: mutable short refc; ptr_op_t left_; - variant // used by terminal SCOPE > data; public: @@ -73,6 +75,7 @@ public: CONSTANTS, FUNCTION, + SCOPE, TERMINALS, @@ -173,7 +176,7 @@ public: return kind == FUNCTION; } expr_t::func_t& as_function_lval() { - assert(kind == FUNCTION); + assert(is_function()); return boost::get(data); } const expr_t::func_t& as_function() const { @@ -183,21 +186,41 @@ public: data = val; } + bool is_scope() const { + return kind == SCOPE; + } + bool is_scope_unset() const { + return data.which() == 0; + } + shared_ptr as_scope_lval() { + assert(is_scope()); + return boost::get >(data); + } + const shared_ptr as_scope() const { + return const_cast(this)->as_scope_lval(); + } + void set_scope(shared_ptr val) { + data = val; + } + + // These three functions must use 'kind == IDENT' rather than + // 'is_ident()', because they are called before the `data' member gets + // set, which is_ident() tests. ptr_op_t& left() { - assert(kind > TERMINALS || kind == IDENT); + assert(kind > TERMINALS || kind == IDENT || is_scope()); return left_; } const ptr_op_t& left() const { - assert(kind > TERMINALS || kind == IDENT); + assert(kind > TERMINALS || kind == IDENT || is_scope()); return left_; } void set_left(const ptr_op_t& expr) { - assert(kind > TERMINALS || kind == IDENT); + assert(kind > TERMINALS || kind == IDENT || is_scope()); left_ = expr; } ptr_op_t& as_op_lval() { - assert(kind > TERMINALS || kind == IDENT); + assert(kind > TERMINALS || is_ident()); return boost::get(data); } const ptr_op_t& as_op() const { @@ -219,7 +242,7 @@ public: bool has_right() const { if (kind < TERMINALS) return false; - return as_op(); + return data.which() != 0 && as_op(); } private: @@ -284,6 +307,7 @@ public: static ptr_op_t wrap_value(const value_t& val); static ptr_op_t wrap_functor(expr_t::func_t fobj); + static ptr_op_t wrap_scope(shared_ptr sobj); #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -295,13 +319,13 @@ private: void serialize(Archive& ar, const unsigned int /* version */) { ar & refc; ar & kind; - if (Archive::is_loading::value || ! left_ || left_->kind != FUNCTION) { + if (Archive::is_loading::value || ! left_ || ! left_->is_function()) { ar & left_; } else { ptr_op_t temp_op; ar & temp_op; } - if (Archive::is_loading::value || kind == VALUE || kind == IDENT || + if (Archive::is_loading::value || is_value() || is_ident() || (kind > UNARY_OPERATORS && (! has_right() || ! right()->is_function()))) { ar & data; @@ -335,6 +359,13 @@ expr_t::op_t::wrap_functor(expr_t::func_t fobj) { return temp; } +inline expr_t::ptr_op_t +expr_t::op_t::wrap_scope(shared_ptr sobj) { + ptr_op_t temp(new op_t(op_t::SCOPE)); + temp->set_scope(sobj); + return temp; +} + #define MAKE_FUNCTOR(x) expr_t::op_t::wrap_functor(bind(&x, this, _1)) #define WRAP_FUNCTOR(x) expr_t::op_t::wrap_functor(x) diff --git a/src/parser.cc b/src/parser.cc index a18fa552..ad621106 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -434,7 +434,6 @@ expr_t::parser_t::parse_comma_expr(std::istream& in, ptr_op_t prev(node); node = new op_t(op_t::O_CONS); node->set_left(prev); - next = node; } @@ -493,7 +492,9 @@ expr_t::parser_t::parse_assign_expr(std::istream& in, ptr_op_t prev(node); node = new op_t(op_t::O_DEFINE); node->set_left(prev); - node->set_right(parse_lambda_expr(in, tflags)); + ptr_op_t scope(new op_t(op_t::SCOPE)); + scope->set_left(parse_lambda_expr(in, tflags)); + node->set_right(scope); } else { push_token(tok); } @@ -509,24 +510,24 @@ expr_t::parser_t::parse_value_expr(std::istream& in, ptr_op_t node(parse_assign_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { - ptr_op_t next; + ptr_op_t chain; while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); - if (tok.kind == token_t::SEMI) { - if (! next) { - ptr_op_t prev(node); - node = new op_t(op_t::O_SEQ); - node->set_left(prev); - - next = node; + ptr_op_t seq(new op_t(op_t::O_SEQ)); + if (! chain) { + seq->set_left(node); + ptr_op_t scope(new op_t(op_t::SCOPE)); + scope->set_left(seq); + node = scope; + } else { + seq->set_left(chain->right()); + ptr_op_t scope(new op_t(op_t::SCOPE)); + scope->set_left(seq); + chain->set_right(scope); } - - ptr_op_t chain(new op_t(op_t::O_SEQ)); - chain->set_left(parse_assign_expr(in, tflags)); - - next->set_right(chain); - next = chain; + seq->set_right(parse_assign_expr(in, tflags)); + chain = seq; } else { push_token(tok); break; diff --git a/src/value.cc b/src/value.cc index c62e6f32..f54c06a5 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1727,7 +1727,7 @@ void value_t::print(std::ostream& _out, switch (type()) { case VOID: - out << ""; + out << "(null)"; break; case BOOLEAN: -- cgit v1.2.3 From 1837e323916c7479623bb61e479407b85edee562 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 21 Feb 2012 01:45:26 -0600 Subject: The new SCOPE mechanism is working --- src/op.cc | 139 ++++++++++++++++++++++++----------------------------------- src/scope.cc | 8 +++- src/scope.h | 2 + 3 files changed, 65 insertions(+), 84 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index d5a589c8..bf6fa543 100644 --- a/src/op.cc +++ b/src/op.cc @@ -81,7 +81,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) scope_t * scope_ptr = &scope; if (is_ident()) { - DEBUG("expr.compile", "lookup: " << as_ident()); + DEBUG("expr.compile", "Lookup: " << as_ident()); if (ptr_op_t def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident())) { // Identifier references are first looked up at the point of @@ -93,7 +93,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) def->dump(*_log_stream, 0); } #endif // defined(DEBUG_ON) - return copy(def); + return copy(def); } else if (left()) { return copy(); @@ -102,7 +102,6 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) } else if (is_scope()) { shared_ptr subscope(new symbol_scope_t(scope)); - DEBUG("expr.compile", "creating scope"); set_scope(subscope); scope_ptr = subscope.get(); } @@ -114,22 +113,21 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) case IDENT: scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), right()); break; + case O_CALL: if (left()->left()->is_ident()) { ptr_op_t node(new op_t(op_t::O_LAMBDA)); node->set_left(left()->right()); node->set_right(right()); - scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(), node); - } else { - throw_(compile_error, _("Invalid function definition")); + break; } - break; + // fall through... + default: throw_(compile_error, _("Invalid function definition")); } - return wrap_value(NULL_VALUE); } ptr_op_t lhs(left()->compile(*scope_ptr, depth)); @@ -144,7 +142,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) // Reduce constants immediately if possible if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value())) - return wrap_value(intermediate->calc(*scope_ptr, NULL, depth)); + return wrap_value(intermediate->calc(*scope_ptr, NULL, depth + 1)); return intermediate; } @@ -159,7 +157,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) if (SHOW_DEBUG("expr.calc")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; - ledger::_log_buffer << op_context(this) << " => ..."; + ledger::_log_buffer << op_context(this) << " => ..."; DEBUG("expr.calc", ""); } #endif @@ -170,84 +168,48 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; case O_DEFINE: - //result = left()->calc(scope, locus, depth + 1); result = NULL_VALUE; break; case IDENT: { ptr_op_t definition = left(); + // If no definition was pre-compiled for this identifier, look it up + // in the current scope. if (! definition) { - // If no definition was pre-compiled for this identifier, look it - // up in the current scope. + DEBUG("scope.symbols", "Looking for IDENT '" << as_ident() << "'"); definition = scope.lookup(symbol_t::FUNCTION, as_ident()); } if (! definition) throw_(calc_error, _("Unknown identifier '%1'") << as_ident()); // Evaluating an identifier is the same as calling its definition - // directly, so we create an empty call_scope_t to reflect the scope for - // this implicit call. - call_scope_t call_args(scope, locus, depth); - result = definition->compile(call_args, depth + 1) - ->calc(call_args, locus, depth + 1); + // directly + result = definition->calc(scope, locus, depth + 1); check_type_context(scope, result); break; } case FUNCTION: { - // Evaluating a FUNCTION is the same as calling it directly; this happens - // when certain functions-that-look-like-variables (such as "amount") are - // resolved. - call_scope_t call_args(scope, locus, depth); + // Evaluating a FUNCTION is the same as calling it directly; this + // happens when certain functions-that-look-like-variables (such as + // "amount") are resolved. + call_scope_t call_args(scope, locus, depth + 1); result = as_function()(call_args); check_type_context(scope, result); break; } case SCOPE: + assert(! is_scope_unset()); if (is_scope_unset()) { symbol_scope_t subscope(scope); result = left()->calc(subscope, locus, depth + 1); } else { - result = left()->calc(*as_scope(), locus, depth + 1); + bind_scope_t bound_scope(scope, *as_scope()); + result = left()->calc(bound_scope, locus, depth + 1); } break; - case O_LAMBDA: { - call_scope_t& call_args(downcast(scope)); - std::size_t args_count(call_args.size()); - std::size_t args_index(0); - symbol_scope_t call_scope(call_args); - ptr_op_t sym(left()); - - for (; sym; sym = sym->has_right() ? sym->right() : NULL) { - ptr_op_t varname = sym; - if (sym->kind == O_CONS) - varname = sym->left(); - - if (! varname->is_ident()) { - throw_(calc_error, _("Invalid function definition")); - } - else if (args_index == args_count) { - call_scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(NULL_VALUE)); - } - else { - DEBUG("expr.compile", - "Defining function parameter " << varname->as_ident()); - call_scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(call_args[args_index++])); - } - } - - if (args_index < args_count) - throw_(calc_error, - _("Too few arguments in function call (saw %1)") << args_count); - - result = right()->calc(call_scope, locus, depth + 1); - break; - } - case O_LOOKUP: { context_scope_t context_scope(scope, value_t::SCOPE); bool scope_error = true; @@ -264,10 +226,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) } case O_CALL: { - call_scope_t call_args(scope, locus, depth); - if (has_right()) - call_args.set_args(split_cons_expr(right())); - ptr_op_t func = left(); const string& name(func->as_ident()); @@ -277,20 +235,9 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) if (! func) throw_(calc_error, _("Calling unknown function '%1'") << name); -#if defined(DEBUG_ON) - if (SHOW_DEBUG("expr.calc")) { - for (int i = 0; i < depth; i++) - ledger::_log_buffer << '.'; - ledger::_log_buffer << " args: "; - if (call_args.args.is_sequence()) { - foreach (value_t& arg, call_args) - ledger::_log_buffer << arg << " "; - } else { - ledger::_log_buffer << call_args.args[0] << " "; - } - DEBUG("expr.calc", ""); - } -#endif + call_scope_t call_args(scope, locus, depth + 1); + if (has_right()) + call_args.set_args(split_cons_expr(right())); if (func->is_function()) result = func->as_function()(call_args); @@ -301,6 +248,39 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; } + case O_LAMBDA: { + call_scope_t& call_args(downcast(scope)); + std::size_t args_count(call_args.size()); + std::size_t args_index(0); + symbol_scope_t call_scope(call_args); + + for (ptr_op_t sym = left(); + sym; + sym = sym->has_right() ? sym->right() : NULL) { + ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; + if (! varname->is_ident()) { + throw_(calc_error, _("Invalid function definition")); + } + else if (args_index == args_count) { + call_scope.define(symbol_t::FUNCTION, varname->as_ident(), + wrap_value(NULL_VALUE)); + } + else { + DEBUG("expr.compile", + "Defining function parameter " << varname->as_ident()); + call_scope.define(symbol_t::FUNCTION, varname->as_ident(), + wrap_value(call_args[args_index++])); + } + } + + if (args_index < args_count) + throw_(calc_error, + _("Too few arguments in function call (saw %1)") << args_count); + + result = right()->calc(call_scope, locus, depth + 1); + break; + } + case O_MATCH: result = (right()->calc(scope, locus, depth + 1).as_mask() .match(left()->calc(scope, locus, depth + 1).to_string())); @@ -369,7 +349,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) case O_QUERY: assert(right()); assert(right()->kind == O_COLON); - if (value_t temp = left()->calc(scope, locus, depth + 1)) result = right()->left()->calc(scope, locus, depth + 1); else @@ -382,8 +361,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) case O_CONS: result = left()->calc(scope, locus, depth + 1); - DEBUG("op.cons", "car = " << result); - if (has_right()) { value_t temp; temp.push_back(result); @@ -399,7 +376,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) next = NULL; } temp.push_back(value_op->calc(scope, locus, depth + 1)); - DEBUG("op.cons", "temp now = " << temp); } result = temp; } @@ -413,7 +389,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) // onto the stack. We evaluate the left side here to catch any // side-effects, such as definitions in the case of 'x = 1; x'. result = left()->calc(scope, locus, depth + 1); - if (has_right()) { ptr_op_t next = right(); while (next) { @@ -786,7 +761,7 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const // An identifier is a special non-terminal, in that its left() can // hold the compiled definition of the identifier. - if (kind > TERMINALS || is_ident() || is_scope()) { + if (kind > TERMINALS || is_scope()) { if (left()) { left()->dump(out, depth + 1); if (kind > UNARY_OPERATORS && has_right()) diff --git a/src/scope.cc b/src/scope.cc index e18b5a0a..95347c8d 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -40,7 +40,8 @@ scope_t * scope_t::default_scope = NULL; void symbol_scope_t::define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { - DEBUG("scope.symbols", "Defining '" << name << "' = " << def); + DEBUG("scope.symbols", + "Defining '" << name << "' = " << def << " in " << this); if (! symbols) symbols = symbol_map(); @@ -64,9 +65,12 @@ expr_t::ptr_op_t symbol_scope_t::lookup(const symbol_t::kind_t kind, const string& name) { if (symbols) { + DEBUG("scope.symbols", "Looking for '" << name << "' in " << this); symbol_map::const_iterator i = symbols->find(symbol_t(kind, name)); - if (i != symbols->end()) + if (i != symbols->end()) { + DEBUG("scope.symbols", "Found '" << name << "' in " << this); return (*i).second; + } } return child_scope_t::lookup(kind, name); } diff --git a/src/scope.h b/src/scope.h index a7b3c5cb..fbc1a889 100644 --- a/src/scope.h +++ b/src/scope.h @@ -188,6 +188,8 @@ public: scope_t& _grandchild) : child_scope_t(_parent), grandchild(_grandchild) { TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&"); + DEBUG("scope.symbols", + "Binding scope " << &_parent << " with " << &_grandchild); } virtual ~bind_scope_t() { TRACE_DTOR(bind_scope_t); -- cgit v1.2.3 From 697d097594c158c595910e7f56bf09bdb894abf8 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 21 Feb 2012 02:09:48 -0600 Subject: Whitespace fix --- src/scope.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/scope.cc b/src/scope.cc index 95347c8d..2b9851b0 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -88,8 +88,7 @@ value_t& call_scope_t::resolve(const std::size_t index, value = as_expr(value)->calc(scope, locus, depth); if (required && ! value.is_type(context)) throw_(calc_error, _("Expected %1 for argument %2, but received %3") - << value.label(context) << index - << value.label()); + << value.label(context) << index << value.label()); } return value; } -- cgit v1.2.3 From 36922c5945290f43a3517fb2f5f155b287b9b565 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 21 Feb 2012 03:44:34 -0600 Subject: Whitespace fixes --- src/main.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/main.cc b/src/main.cc index 820f920d..9031341f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -86,7 +86,6 @@ int main(int argc, char * argv[], char * envp[]) // Create the session object, which maintains nearly all state relating to // this invocation of Ledger; and register all known journal parsers. global_scope.reset(new global_scope_t(envp)); - global_scope->session().set_flush_on_next_data_file(true); // Construct an STL-style argument list from the process command arguments @@ -96,7 +95,6 @@ int main(int argc, char * argv[], char * envp[]) // Look for options and a command verb in the command-line arguments bind_scope_t bound_scope(*global_scope.get(), global_scope->report()); - args = global_scope->read_command_arguments(bound_scope, args); if (global_scope->HANDLED(script_)) { @@ -112,8 +110,8 @@ int main(int argc, char * argv[], char * envp[]) char * p = skip_ws(line); if (*p && *p != '#') - status = global_scope->execute_command_wrapper(split_arguments(p), - true); + status = + global_scope->execute_command_wrapper(split_arguments(p), true); } } else if (! args.empty()) { -- cgit v1.2.3 From c47350dce9c666987f924c54507973fc11587b2e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 21 Feb 2012 03:53:00 -0600 Subject: Corrected handling of nested definitions --- src/op.cc | 73 ++++++++++++++++++++++++++++++----------- src/parser.cc | 8 ++--- src/report.cc | 4 +++ test/baseline/cmd-script.test | 3 ++ test/baseline/cmd-script_2.test | 3 ++ 5 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 test/baseline/cmd-script.test create mode 100644 test/baseline/cmd-script_2.test (limited to 'src') diff --git a/src/op.cc b/src/op.cc index bf6fa543..6dff031c 100644 --- a/src/op.cc +++ b/src/op.cc @@ -78,11 +78,19 @@ namespace { expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) { - scope_t * scope_ptr = &scope; + scope_t * scope_ptr = &scope; + expr_t::ptr_op_t result; - if (is_ident()) { - DEBUG("expr.compile", "Lookup: " << as_ident()); +#if defined(DEBUG_ON) + if (SHOW_DEBUG("expr.compile")) { + for (int i = 0; i < depth; i++) + ledger::_log_buffer << '.'; + DEBUG("expr.compile", ""); + } +#endif + if (is_ident()) { + DEBUG("expr.compile", "Lookup: " << as_ident() << " in " << scope_ptr); if (ptr_op_t def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident())) { // Identifier references are first looked up at the point of // definition, and then at the point of every use if they could @@ -93,12 +101,14 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) def->dump(*_log_stream, 0); } #endif // defined(DEBUG_ON) - return copy(def); + result = copy(def); } else if (left()) { - return copy(); + result = copy(); + } + else { + result = this; } - return this; } else if (is_scope()) { shared_ptr subscope(new symbol_scope_t(scope)); @@ -106,19 +116,28 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) scope_ptr = subscope.get(); } else if (kind < TERMINALS) { - return this; + result = this; } else if (kind == O_DEFINE) { switch (left()->kind) { - case IDENT: - scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), right()); + case IDENT: { + ptr_op_t node(right()->compile(*scope_ptr, depth + 1)); + + DEBUG("expr.compile", + "Defining " << left()->as_ident() << " in " << scope_ptr); + scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), node); break; + } case O_CALL: if (left()->left()->is_ident()) { ptr_op_t node(new op_t(op_t::O_LAMBDA)); node->set_left(left()->right()); node->set_right(right()); + node = node->compile(*scope_ptr, depth + 1); + + DEBUG("expr.compile", + "Defining " << left()->left()->as_ident() << " in " << scope_ptr); scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(), node); break; @@ -128,23 +147,37 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) default: throw_(compile_error, _("Invalid function definition")); } + result = wrap_value(NULL_VALUE); } - ptr_op_t lhs(left()->compile(*scope_ptr, depth)); - ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ? - (kind == O_LOOKUP ? right() : - right()->compile(*scope_ptr, depth)) : NULL); + if (! result) { + ptr_op_t lhs(left()->compile(*scope_ptr, depth + 1)); + ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ? + (kind == O_LOOKUP ? right() : + right()->compile(*scope_ptr, depth + 1)) : NULL); - if (lhs == left() && (! rhs || rhs == right())) - return this; + if (lhs == left() && (! rhs || rhs == right())) { + result = this; + } else { + ptr_op_t intermediate(copy(lhs, rhs)); - ptr_op_t intermediate(copy(lhs, rhs)); + // Reduce constants immediately if possible + if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value())) + result = wrap_value(intermediate->calc(*scope_ptr, NULL, depth + 1)); + else + result = intermediate; + } + } - // Reduce constants immediately if possible - if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value())) - return wrap_value(intermediate->calc(*scope_ptr, NULL, depth + 1)); +#if defined(DEBUG_ON) + if (SHOW_DEBUG("expr.compile")) { + for (int i = 0; i < depth; i++) + ledger::_log_buffer << '.'; + DEBUG("expr.compile", ""); + } +#endif - return intermediate; + return result; } value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) diff --git a/src/parser.cc b/src/parser.cc index ad621106..6197af6b 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -517,14 +517,10 @@ expr_t::parser_t::parse_value_expr(std::istream& in, ptr_op_t seq(new op_t(op_t::O_SEQ)); if (! chain) { seq->set_left(node); - ptr_op_t scope(new op_t(op_t::SCOPE)); - scope->set_left(seq); - node = scope; + node = seq; } else { seq->set_left(chain->right()); - ptr_op_t scope(new op_t(op_t::SCOPE)); - scope->set_left(seq); - chain->set_right(scope); + chain->set_right(seq); } seq->set_right(parse_assign_expr(in, tflags)); chain = seq; diff --git a/src/report.cc b/src/report.cc index f4dc450e..38ae596a 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1559,6 +1559,10 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (is_eq(p, "query")) return WRAP_FUNCTOR(query_command); break; + case 's': + if (is_eq(p, "script")) + return WRAP_FUNCTOR(source_command); + break; case 't': if (is_eq(p, "template")) return WRAP_FUNCTOR(template_command); diff --git a/test/baseline/cmd-script.test b/test/baseline/cmd-script.test new file mode 100644 index 00000000..ed665dcc --- /dev/null +++ b/test/baseline/cmd-script.test @@ -0,0 +1,3 @@ +test eval 'foo(w, u)=(z=w+u;z*2); (a=1 + 1; foo(10, 15))' +50 +end test diff --git a/test/baseline/cmd-script_2.test b/test/baseline/cmd-script_2.test new file mode 100644 index 00000000..a6f6f37a --- /dev/null +++ b/test/baseline/cmd-script_2.test @@ -0,0 +1,3 @@ +test eval 'x=total_expr;x=x/count;x=amount_expr-x*count;x' +0 +end test -- cgit v1.2.3 From 4a18317e7c35fea0aafdd99fec93adfea694743d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 26 Feb 2012 02:16:09 -0600 Subject: Added --auto-match option, for use with 'convert' --- doc/ledger.1 | 3 ++- src/convert.cc | 6 ++++-- src/report.cc | 1 + src/report.h | 2 ++ test/baseline/opt-auto-match.test | 0 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 test/baseline/opt-auto-match.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 5a3bd6db..9fe5c84c 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd June 22, 2010 +.Dd February 26, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -270,6 +270,7 @@ transactions they are contained in. See the manual for more information. .It Fl \-amount-width Ar INT .It Fl \-anon .It Fl \-args-only +.It Fl \-auto-match .It Fl \-average Pq Fl A .It Fl \-balance-format Ar FMT .It Fl \-base diff --git a/src/convert.cc b/src/convert.cc index 493fbb7a..1ef3a413 100644 --- a/src/convert.cc +++ b/src/convert.cc @@ -123,8 +123,10 @@ value_t convert_command(call_scope_t& args) if (xact->posts.front()->account == NULL) { // jww (2010-03-07): Bind this logic to an option: --auto-match if (account_t * acct = - lookup_probable_account(xact->payee, current_xacts.rbegin(), - current_xacts.rend(), bucket).second) + (report.HANDLED(auto_match) ? + lookup_probable_account(xact->payee, current_xacts.rbegin(), + current_xacts.rend(), bucket).second : + NULL)) xact->posts.front()->account = acct; else xact->posts.front()->account = unknown; diff --git a/src/report.cc b/src/report.cc index 38ae596a..b3b7233f 100644 --- a/src/report.cc +++ b/src/report.cc @@ -966,6 +966,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(amount_data); else OPT(anon); else OPT_ALT(color, ansi); + else OPT(auto_match); else OPT(average); else OPT(account_width_); else OPT(amount_width_); diff --git a/src/report.h b/src/report.h index 5b403205..a001ffb1 100644 --- a/src/report.h +++ b/src/report.h @@ -229,6 +229,7 @@ public: HANDLER(amount_).report(out); HANDLER(amount_data).report(out); HANDLER(anon).report(out); + HANDLER(auto_match).report(out); HANDLER(average).report(out); HANDLER(balance_format_).report(out); HANDLER(base).report(out); @@ -381,6 +382,7 @@ public: OPTION(report_t, amount_data); // -j OPTION(report_t, anon); + OPTION(report_t, auto_match); OPTION_(report_t, average, DO() { // -A parent->HANDLER(display_total_) diff --git a/test/baseline/opt-auto-match.test b/test/baseline/opt-auto-match.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 4ebcce3067f7b8df36a76ab16cd0a693a82ccbc5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 26 Feb 2012 02:16:39 -0600 Subject: Make CSV importer more sensitive to errors --- src/csv.cc | 126 ++++++++++++++++++++++++++++++------------------------------- 1 file changed, 63 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/csv.cc b/src/csv.cc index f5b72ecf..e2ba523d 100644 --- a/src/csv.cc +++ b/src/csv.cc @@ -132,7 +132,6 @@ void csv_reader::read_index(std::istream& sin) xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket) { - restart: char * line = next_line(in); if (! line || index.empty()) return NULL; @@ -165,82 +164,83 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket) std::vector::size_type n = 0; amount_t amt; - string total; + string total; + string field; - while (instr.good() && ! instr.eof()) { - string field = read_field(instr); + try { + while (instr.good() && ! instr.eof()) { + field = read_field(instr); - switch (index[n]) { - case FIELD_DATE: - if (field.empty()) - goto restart; - try { + switch (index[n]) { + case FIELD_DATE: xact->_date = parse_date(field); - } - catch (date_error&) { - goto restart; - } - break; + break; - case FIELD_DATE_EFF: - xact->_date_eff = parse_date(field); - break; + case FIELD_DATE_EFF: + xact->_date_eff = parse_date(field); + break; - case FIELD_CODE: - if (! field.empty()) - xact->code = field; - break; + case FIELD_CODE: + if (! field.empty()) + xact->code = field; + break; - case FIELD_PAYEE: { - bool found = false; - foreach (payee_mapping_t& value, journal.payee_mappings) { - DEBUG("csv.mappings", "Looking for payee mapping: " << value.first); - if (value.first.match(field)) { - xact->payee = value.second; - found = true; - break; + case FIELD_PAYEE: { + bool found = false; + foreach (payee_mapping_t& value, journal.payee_mappings) { + DEBUG("csv.mappings", "Looking for payee mapping: " << value.first); + if (value.first.match(field)) { + xact->payee = value.second; + found = true; + break; + } } + if (! found) + xact->payee = field; + break; } - if (! found) - xact->payee = field; - break; - } - case FIELD_AMOUNT: { - std::istringstream amount_str(field); - amt.parse(amount_str, PARSE_NO_REDUCE); - if (! amt.has_commodity() && - commodity_pool_t::current_pool->default_commodity) - amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); - post->amount = amt; - break; - } + case FIELD_AMOUNT: { + std::istringstream amount_str(field); + amt.parse(amount_str, PARSE_NO_REDUCE); + if (! amt.has_commodity() && + commodity_pool_t::current_pool->default_commodity) + amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); + post->amount = amt; + break; + } - case FIELD_COST: { - std::istringstream amount_str(field); - amt.parse(amount_str, PARSE_NO_REDUCE); - if (! amt.has_commodity() && - commodity_pool_t::current_pool->default_commodity) - amt.set_commodity - (*commodity_pool_t::current_pool->default_commodity); - post->cost = amt; - break; - } + case FIELD_COST: { + std::istringstream amount_str(field); + amt.parse(amount_str, PARSE_NO_REDUCE); + if (! amt.has_commodity() && + commodity_pool_t::current_pool->default_commodity) + amt.set_commodity + (*commodity_pool_t::current_pool->default_commodity); + post->cost = amt; + break; + } - case FIELD_TOTAL: - total = field; - break; + case FIELD_TOTAL: + total = field; + break; - case FIELD_NOTE: - xact->note = field; - break; + case FIELD_NOTE: + xact->note = field; + break; - case FIELD_UNKNOWN: - if (! names[n].empty() && ! field.empty()) - xact->set_tag(names[n], string_value(field)); - break; + case FIELD_UNKNOWN: + if (! names[n].empty() && ! field.empty()) + xact->set_tag(names[n], string_value(field)); + break; + } + n++; } - n++; + } + catch (const std::exception&) { + add_error_context(_("While parsing CSV field:")); + add_error_context(line_context(field)); + throw; } #if 0 -- cgit v1.2.3 From 40ab81308025e9655074da37671c444b4257f867 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 26 Feb 2012 02:17:45 -0600 Subject: Don't print amounts when not necessary in 'print' --- src/print.cc | 30 ++++++++++++++++++++---------- test/baseline/opt-equity.test | 2 +- 2 files changed, 21 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/print.cc b/src/print.cc index b7f72bf0..215ab30b 100644 --- a/src/print.cc +++ b/src/print.cc @@ -119,7 +119,12 @@ namespace { } } + std::size_t count = xact.posts.size(); + std::size_t index = 0; + foreach (post_t * post, xact.posts) { + index++; + if (! report.HANDLED(generated) && (post->has_flags(ITEM_TEMP | ITEM_GENERATED) && ! post->has_flags(POST_ANONYMIZED))) @@ -163,17 +168,15 @@ namespace { std::string::size_type slip = (static_cast(account_width) - static_cast(name.length())); - if (slip > 0) { - out.width(static_cast(slip)); - out << ' '; - } - - std::ostringstream amtbuf; string amt; if (post->amount_expr) { amt = post->amount_expr->text(); - } else { + } + else if (! (count == 2 && index == 2 && + (*xact.posts.begin())->amount.commodity() == + post->amount.commodity() && + ! (*xact.posts.begin())->cost && ! post->cost)) { int amount_width = (report.HANDLER(amount_width_).specified ? report.HANDLER(amount_width_).value.to_int() : 12); @@ -191,6 +194,7 @@ namespace { (static_cast(amt.length()) - static_cast(trimmed_amt.length())); + std::ostringstream amtbuf; if (slip + amt_slip < 2) amtbuf << string(2 - (slip + amt_slip), ' '); amtbuf << amt; @@ -208,9 +212,15 @@ namespace { amtbuf << " = " << *post->assigned_amount; string trailer = amtbuf.str(); - out << trailer; - - account_width += unistring(trailer).length(); + if (! trailer.empty()) { + if (slip > 0) { + out.width(static_cast(slip)); + out << ' '; + } + out << trailer; + + account_width += unistring(trailer).length(); + } } else { out << pbuf.str(); } diff --git a/test/baseline/opt-equity.test b/test/baseline/opt-equity.test index d8695759..90f4743a 100644 --- a/test/baseline/opt-equity.test +++ b/test/baseline/opt-equity.test @@ -5,5 +5,5 @@ test equity assets 2007/02/02 Opening Balances Assets:Investments:Vanguard:VMMXX 0.350 VMMXX - Equity:Opening Balances -0.350 VMMXX + Equity:Opening Balances end test -- cgit v1.2.3 From 4cf95497f9afaead3d9d308dabe8e8b26949502f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 26 Feb 2012 02:18:22 -0600 Subject: Keep notes on their own line if parsed that way --- src/item.h | 10 ++++++---- src/print.cc | 15 ++++++++++----- src/textual.cc | 2 ++ 3 files changed, 18 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/item.h b/src/item.h index 79d2f23b..908dd1b8 100644 --- a/src/item.h +++ b/src/item.h @@ -100,9 +100,10 @@ private: class item_t : public supports_flags, public scope_t { public: -#define ITEM_NORMAL 0x00 // no flags at all, a basic posting -#define ITEM_GENERATED 0x01 // posting was not found in a journal -#define ITEM_TEMP 0x02 // posting is a managed temporary +#define ITEM_NORMAL 0x00 // no flags at all, a basic posting +#define ITEM_GENERATED 0x01 // posting was not found in a journal +#define ITEM_TEMP 0x02 // posting is a managed temporary +#define ITEM_NOTE_ON_NEXT_LINE 0x04 // did we see a note on the next line? enum state_t { UNCLEARED = 0, CLEARED, PENDING }; @@ -117,7 +118,8 @@ public: optional metadata; item_t(flags_t _flags = ITEM_NORMAL, const optional& _note = none) - : supports_flags(_flags), _state(UNCLEARED), note(_note) + : supports_flags(_flags), _state(UNCLEARED), + note(_note) { TRACE_CTOR(item_t, "flags_t, const string&"); } diff --git a/src/print.cc b/src/print.cc index 215ab30b..6725a336 100644 --- a/src/print.cc +++ b/src/print.cc @@ -43,12 +43,15 @@ namespace ledger { namespace { void print_note(std::ostream& out, const string& note, + const bool note_on_next_line, const std::size_t columns, const std::size_t prior_width) { - // The 4 is for four leading spaces at the beginning of the posting, and - // the 3 is for two spaces and a semi-colon before the note. - if (columns > 0 && note.length() > columns - (prior_width + 3)) + // The 3 is for two spaces and a semi-colon before the note. + if (note_on_next_line || + (columns > 0 && + (columns <= prior_width + 3 || + note.length() > columns - (prior_width + 3)))) out << "\n ;"; else out << " ;"; @@ -103,7 +106,8 @@ namespace { static_cast(report.HANDLER(columns_).value.to_long()) : 80); if (xact.note) - print_note(out, *xact.note, columns, unistring(leader).length()); + print_note(out, *xact.note, xact.has_flags(ITEM_NOTE_ON_NEXT_LINE), + columns, unistring(leader).length()); out << '\n'; if (xact.metadata) { @@ -226,7 +230,8 @@ namespace { } if (post->note) - print_note(out, *post->note, columns, 4 + account_width); + print_note(out, *post->note, post->has_flags(ITEM_NOTE_ON_NEXT_LINE), + columns, 4 + account_width); out << '\n'; } } diff --git a/src/textual.cc b/src/textual.cc index c7c49e2a..ddbd9943 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -577,6 +577,7 @@ void instance_t::automated_xact_directive(char * line) // This is a trailing note, and possibly a metadata info tag item->append_note(p + 1, context.scope, true); + item->add_flags(ITEM_NOTE_ON_NEXT_LINE); item->pos->end_pos = curr_pos; item->pos->end_line++; @@ -1529,6 +1530,7 @@ xact_t * instance_t::parse_xact(char * line, if (*p == ';') { // This is a trailing note, and possibly a metadata info tag item->append_note(p + 1, context.scope, true); + item->add_flags(ITEM_NOTE_ON_NEXT_LINE); item->pos->end_pos = curr_pos; item->pos->end_line++; } -- cgit v1.2.3 From aa9b07d79bff00506b913d1e56575c3859fc173f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 26 Feb 2012 15:45:15 -0600 Subject: Added --rich-data for 'convert', and SHA1 checksum checking --- doc/ledger.1 | 1 + src/convert.cc | 91 ++++++++-------------- src/csv.cc | 162 ++++++++++++++++++--------------------- src/csv.h | 37 +++++++-- src/item.cc | 2 +- src/journal.cc | 11 +++ src/journal.h | 3 + src/report.cc | 1 + src/report.h | 3 + src/textual.cc | 10 ++- src/utils.cc | 4 +- test/baseline/opt-rich-data.test | 0 12 files changed, 167 insertions(+), 158 deletions(-) create mode 100644 test/baseline/opt-rich-data.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 9fe5c84c..21d43ead 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -397,6 +397,7 @@ appeared in the original journal file. .It Fl \-revalued .It Fl \-revalued-only .It Fl \-revalued-total Ar EXPR +.It Fl \-rich-data .It Fl \-seed Ar INT .It Fl \-script .It Fl \-sort Ar EXPR Pq Fl S diff --git a/src/convert.cc b/src/convert.cc index 1ef3a413..da4569cc 100644 --- a/src/convert.cc +++ b/src/convert.cc @@ -56,72 +56,41 @@ value_t convert_command(call_scope_t& args) account_t * bucket = journal.master->find_account(bucket_name); account_t * unknown = journal.master->find_account(_("Expenses:Unknown")); - // Make an amounts mapping for the account under consideration - - typedef std::map > post_map_t; - post_map_t post_map; - - xacts_iterator journal_iter(journal); - while (xact_t * xact = *journal_iter++) { - post_t * post = NULL; - xact_posts_iterator xact_iter(*xact); - while ((post = *xact_iter++) != NULL) { - if (post->account == bucket) - break; - } - if (post) { - post_map_t::iterator i = post_map.find(post->amount); - if (i == post_map.end()) { - std::list post_list; - post_list.push_back(post); - post_map.insert(post_map_t::value_type(post->amount, post_list)); - } else { - (*i).second.push_back(post); - } - } - } - // Create a flat list xacts_list current_xacts(journal.xacts_begin(), journal.xacts_end()); // Read in the series of transactions from the CSV file print_xacts formatter(report); - ifstream data(path(args.get(0))); - csv_reader reader(data); - - while (xact_t * xact = reader.read_xact(journal, bucket)) { - if (report.HANDLED(invert)) { - foreach (post_t * post, xact->posts) - post->amount.in_place_negate(); - } + path csv_file_path(args.get(0)); + ifstream data(csv_file_path); + csv_reader reader(data, csv_file_path); + + try { + while (xact_t * xact = reader.read_xact(journal, bucket, + report.HANDLED(rich_data))) { + if (report.HANDLED(invert)) { + foreach (post_t * post, xact->posts) + post->amount.in_place_negate(); + } - bool matched = false; - if (! xact->posts.front()->amount.is_null()) { - post_map_t::iterator i = post_map.find(- xact->posts.front()->amount); - if (i != post_map.end()) { - std::list& post_list((*i).second); - foreach (post_t * post, post_list) { - if (xact->code && post->xact->code && - *xact->code == *post->xact->code) { - matched = true; - break; - } - else if (xact->actual_date() == post->actual_date()) { - matched = true; - break; - } - } + string ref = (xact->has_tag(_("SHA1")) ? + xact->get_tag(_("SHA1"))->to_string() : + sha1sum(reader.get_last_line())); + + checksum_map_t::const_iterator entry = journal.checksum_map.find(ref); + if (entry != journal.checksum_map.end()) { + INFO(file_context(reader.get_pathname(), + reader.get_linenum()) + << "Ignoring known SHA1 " << ref); + checked_delete(xact); // ignore it + continue; } - } - if (matched) { - DEBUG("convert.csv", "Ignored xact with code: " << *xact->code); - checked_delete(xact); // ignore it - } - else { + if (report.HANDLED(rich_data) && ! xact->has_tag(_("SHA1"))) + xact->set_tag(_("SHA1"), string_value(ref)); + if (xact->posts.front()->account == NULL) { - // jww (2010-03-07): Bind this logic to an option: --auto-match if (account_t * acct = (report.HANDLED(auto_match) ? lookup_probable_account(xact->payee, current_xacts.rbegin(), @@ -143,8 +112,16 @@ value_t convert_command(call_scope_t& args) formatter(*post); } } + formatter.flush(); + } + catch (const std::exception&) { + add_error_context(_("While parsing file %1") + << file_context(reader.get_pathname(), + reader.get_linenum())); + add_error_context(_("While parsing CSV line:")); + add_error_context(line_context(reader.get_last_line())); + throw; } - formatter.flush(); // If not, transform the payee according to regexps diff --git a/src/csv.cc b/src/csv.cc index e2ba523d..c253f246 100644 --- a/src/csv.cc +++ b/src/csv.cc @@ -70,10 +70,12 @@ string csv_reader::read_field(std::istream& sin) else { while (sin.good() && ! sin.eof()) { sin.get(c); - if (c == ',') - break; - if (c != '\0') - field += c; + if (sin.good()) { + if (c == ',') + break; + if (c != '\0') + field += c; + } } } trim(field); @@ -82,8 +84,6 @@ string csv_reader::read_field(std::istream& sin) char * csv_reader::next_line(std::istream& sin) { - static char linebuf[MAX_LINE + 1]; - while (sin.good() && ! sin.eof() && sin.peek() == '#') sin.getline(linebuf, MAX_LINE); @@ -130,11 +130,13 @@ void csv_reader::read_index(std::istream& sin) } } -xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket) +xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket, + bool rich_data) { char * line = next_line(in); if (! line || index.empty()) return NULL; + linenum++; std::istringstream instr(line); @@ -144,20 +146,18 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket) xact->set_state(item_t::CLEARED); xact->pos = position_t(); - xact->pos->pathname = "jww (2010-03-05): unknown"; + xact->pos->pathname = pathname; xact->pos->beg_pos = in.tellg(); - xact->pos->beg_line = 0; - xact->pos->sequence = 0; + xact->pos->beg_line = linenum; + xact->pos->sequence = sequence++; post->xact = xact.get(); -#if 0 post->pos = position_t(); post->pos->pathname = pathname; - post->pos->beg_pos = line_beg_pos; + post->pos->beg_pos = in.tellg(); post->pos->beg_line = linenum; - post->pos->sequence = context.sequence++; -#endif + post->pos->sequence = sequence++; post->set_state(item_t::CLEARED); post->account = NULL; @@ -167,88 +167,80 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket) string total; string field; - try { - while (instr.good() && ! instr.eof()) { - field = read_field(instr); + while (instr.good() && ! instr.eof()) { + field = read_field(instr); - switch (index[n]) { - case FIELD_DATE: - xact->_date = parse_date(field); - break; + switch (index[n]) { + case FIELD_DATE: + xact->_date = parse_date(field); + break; - case FIELD_DATE_EFF: - xact->_date_eff = parse_date(field); - break; + case FIELD_DATE_EFF: + xact->_date_eff = parse_date(field); + break; - case FIELD_CODE: - if (! field.empty()) - xact->code = field; - break; + case FIELD_CODE: + if (! field.empty()) + xact->code = field; + break; - case FIELD_PAYEE: { - bool found = false; - foreach (payee_mapping_t& value, journal.payee_mappings) { - DEBUG("csv.mappings", "Looking for payee mapping: " << value.first); - if (value.first.match(field)) { - xact->payee = value.second; - found = true; - break; - } + case FIELD_PAYEE: { + bool found = false; + foreach (payee_mapping_t& value, journal.payee_mappings) { + DEBUG("csv.mappings", "Looking for payee mapping: " << value.first); + if (value.first.match(field)) { + xact->payee = value.second; + found = true; + break; } - if (! found) - xact->payee = field; - break; } + if (! found) + xact->payee = field; + break; + } - case FIELD_AMOUNT: { - std::istringstream amount_str(field); - amt.parse(amount_str, PARSE_NO_REDUCE); - if (! amt.has_commodity() && - commodity_pool_t::current_pool->default_commodity) - amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); - post->amount = amt; - break; - } + case FIELD_AMOUNT: { + std::istringstream amount_str(field); + amt.parse(amount_str, PARSE_NO_REDUCE); + if (! amt.has_commodity() && + commodity_pool_t::current_pool->default_commodity) + amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); + post->amount = amt; + break; + } - case FIELD_COST: { - std::istringstream amount_str(field); - amt.parse(amount_str, PARSE_NO_REDUCE); - if (! amt.has_commodity() && - commodity_pool_t::current_pool->default_commodity) - amt.set_commodity - (*commodity_pool_t::current_pool->default_commodity); - post->cost = amt; - break; - } + case FIELD_COST: { + std::istringstream amount_str(field); + amt.parse(amount_str, PARSE_NO_REDUCE); + if (! amt.has_commodity() && + commodity_pool_t::current_pool->default_commodity) + amt.set_commodity + (*commodity_pool_t::current_pool->default_commodity); + post->cost = amt; + break; + } - case FIELD_TOTAL: - total = field; - break; + case FIELD_TOTAL: + total = field; + break; - case FIELD_NOTE: - xact->note = field; - break; + case FIELD_NOTE: + xact->note = field; + break; - case FIELD_UNKNOWN: - if (! names[n].empty() && ! field.empty()) - xact->set_tag(names[n], string_value(field)); - break; - } - n++; + case FIELD_UNKNOWN: + if (! names[n].empty() && ! field.empty()) + xact->set_tag(names[n], string_value(field)); + break; } - } - catch (const std::exception&) { - add_error_context(_("While parsing CSV field:")); - add_error_context(line_context(field)); - throw; + n++; } -#if 0 - xact->set_tag(_("Imported"), - string(format_date(CURRENT_DATE(), FMT_WRITTEN))); - xact->set_tag(_("Original"), string(line)); - xact->set_tag(_("SHA1"), string(sha1sum(line))); -#endif + if (rich_data) { + xact->set_tag(_("Imported"), + string_value(format_date(CURRENT_DATE(), FMT_WRITTEN))); + xact->set_tag(_("CSV"), string_value(line)); + } // Translate the account name, if we have enough information to do so @@ -267,13 +259,11 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket) post->xact = xact.get(); -#if 0 post->pos = position_t(); post->pos->pathname = pathname; - post->pos->beg_pos = line_beg_pos; + post->pos->beg_pos = in.tellg(); post->pos->beg_line = linenum; - post->pos->sequence = context.sequence++; -#endif + post->pos->sequence = sequence++; post->set_state(item_t::CLEARED); post->account = bucket; diff --git a/src/csv.h b/src/csv.h index 5ff8b59e..cf350e9d 100644 --- a/src/csv.h +++ b/src/csv.h @@ -52,9 +52,13 @@ class account_t; class csv_reader { - static const std::size_t MAX_LINE = 1024; + static const std::size_t MAX_LINE = 4096; std::istream& in; + path pathname; + char linebuf[MAX_LINE]; + std::size_t linenum; + std::size_t sequence; enum headers_t { FIELD_DATE = 0, @@ -80,13 +84,11 @@ class csv_reader std::vector index; std::vector names; - std::vector fields; - - typedef std::map string_map; public: - csv_reader(std::istream& _in) - : in(_in), + csv_reader(std::istream& _in, const path& _pathname) + : in(_in), pathname(_pathname), + linenum(0), sequence(0), date_mask("date"), date_eff_mask("posted( ?date)?"), code_mask("code"), @@ -98,11 +100,30 @@ public: read_index(in); } + void read_index(std::istream& in); string read_field(std::istream& in); char * next_line(std::istream& in); - void read_index(std::istream& in); - xact_t * read_xact(journal_t& journal, account_t * bucket); + xact_t * read_xact(journal_t& journal, account_t * bucket, bool rich_data); + + const char * get_last_line() const { + return linebuf; + } + + path get_pathname() const { + return pathname; + } + std::size_t get_linenum() const { + return linenum; + } + + void reset() { + pathname.clear(); + index.clear(); + names.clear(); + linenum = 0; + sequence = 0; + } }; } // namespace ledger diff --git a/src/item.cc b/src/item.cc index 056aa04c..7184c0ef 100644 --- a/src/item.cc +++ b/src/item.cc @@ -72,7 +72,7 @@ bool item_t::has_tag(const mask_t& tag_mask, return false; } - optional item_t::get_tag(const string& tag, bool) const +optional item_t::get_tag(const string& tag, bool) const { DEBUG("item.meta", "Getting item tag: " << tag); if (metadata) { diff --git a/src/journal.cc b/src/journal.cc index 0691954f..bbfa205c 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -107,6 +107,17 @@ account_t * journal_t::find_account_re(const string& regexp) bool journal_t::add_xact(xact_t * xact) { + if (optional ref = xact->get_tag(_("SHA1"))) { + std::pair result + = checksum_map.insert(checksum_map_t::value_type(ref->to_string(), xact)); + if (! result.second) { + throw_(std::runtime_error, + _("Found duplicated transaction with SHA1: ") + << ref->to_string()); + return false; + } + } + xact->journal = this; if (! xact->finalize()) { diff --git a/src/journal.h b/src/journal.h index ca6b6e4f..49a6292b 100644 --- a/src/journal.h +++ b/src/journal.h @@ -63,6 +63,7 @@ typedef std::pair payee_mapping_t; typedef std::list payee_mappings_t; typedef std::pair account_mapping_t; typedef std::list account_mappings_t; +typedef std::map checksum_map_t; class journal_t : public noncopyable { @@ -117,6 +118,7 @@ public: std::list sources; payee_mappings_t payee_mappings; account_mappings_t account_mappings; + checksum_map_t checksum_map; bool was_loaded; journal_t(); @@ -198,6 +200,7 @@ private: ar & sources; ar & payee_mappings; ar & account_mappings; + ar & checksum_map; } #endif // HAVE_BOOST_SERIALIZATION }; diff --git a/src/report.cc b/src/report.cc index b3b7233f..c562ab38 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1094,6 +1094,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(revalued); else OPT(revalued_only); else OPT(revalued_total_); + else OPT(rich_data); break; case 's': OPT(sort_); diff --git a/src/report.h b/src/report.h index a001ffb1..565728df 100644 --- a/src/report.h +++ b/src/report.h @@ -313,6 +313,7 @@ public: HANDLER(revalued).report(out); HANDLER(revalued_only).report(out); HANDLER(revalued_total_).report(out); + HANDLER(rich_data).report(out); HANDLER(seed_).report(out); HANDLER(sort_).report(out); HANDLER(sort_all_).report(out); @@ -893,6 +894,8 @@ public: set_expr(args.get(0), args.get(1)); }); + OPTION(report_t, rich_data); + OPTION(report_t, seed_); OPTION_(report_t, sort_, DO_(args) { // -S diff --git a/src/textual.cc b/src/textual.cc index ddbd9943..13032236 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1150,8 +1150,9 @@ post_t * instance_t::parse_post(char * line, if (context.strict && ! post->account->has_flags(ACCOUNT_KNOWN)) { if (post->_state == item_t::UNCLEARED) - warning_(_("\"%1\", line %2: Unknown account '%3'") - << pathname.string() << linenum << post->account->fullname()); + warning_(_("%1Unknown account '%2'") + << file_context(pathname, linenum) + << post->account->fullname()); post->account->add_flags(ACCOUNT_KNOWN); } @@ -1181,8 +1182,9 @@ post_t * instance_t::parse_post(char * line, if (context.strict && ! post->amount.commodity().has_flags(COMMODITY_KNOWN)) { if (post->_state == item_t::UNCLEARED) - warning_(_("\"%1\", line %2: Unknown commodity '%3'") - << pathname.string() << linenum << post->amount.commodity()); + warning_(_("%1Unknown commodity '%2'") + << file_context(pathname, linenum) + << post->amount.commodity()); post->amount.commodity().add_flags(COMMODITY_KNOWN); } diff --git a/src/utils.cc b/src/utils.cc index 42600db3..2f64bb0a 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -50,8 +50,8 @@ void debug_assert(const string& reason, std::size_t line) { std::ostringstream buf; - buf << "Assertion failed in \"" << file << "\", line " << line - << ": " << func << ": " << reason; + buf << "Assertion failed in " << file_context(file, line) + << func << ": " << reason; throw assertion_failed(buf.str()); } diff --git a/test/baseline/opt-rich-data.test b/test/baseline/opt-rich-data.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 7553df5600c89b6724e83b72c7f78e85fa090a32 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 26 Feb 2012 15:52:19 -0600 Subject: Corrected an error message --- src/journal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/journal.cc b/src/journal.cc index bbfa205c..f070ba0e 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -196,7 +196,7 @@ std::size_t journal_t::read(const path& pathname, if (! exists(filename)) throw_(std::runtime_error, - _("Cannot read journal file '%1'") << filename); + _("Cannot read journal file %1") << filename); ifstream stream(filename); std::size_t count = read(stream, filename, master_account, scope); -- cgit v1.2.3 From bb0c5344145cd79fa54a92f236628eebbe6f2f68 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 26 Feb 2012 16:42:01 -0600 Subject: Revert behavior of 40ab81308025e9655074da37671c444b4257f867 --- src/print.cc | 9 ++++++--- test/baseline/opt-equity.test | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/print.cc b/src/print.cc index 6725a336..a90ea220 100644 --- a/src/print.cc +++ b/src/print.cc @@ -176,11 +176,14 @@ namespace { string amt; if (post->amount_expr) { amt = post->amount_expr->text(); - } - else if (! (count == 2 && index == 2 && + } else +#if 0 + if (! (count == 2 && index == 2 && (*xact.posts.begin())->amount.commodity() == post->amount.commodity() && - ! (*xact.posts.begin())->cost && ! post->cost)) { + ! (*xact.posts.begin())->cost && ! post->cost)) +#endif + { int amount_width = (report.HANDLER(amount_width_).specified ? report.HANDLER(amount_width_).value.to_int() : 12); diff --git a/test/baseline/opt-equity.test b/test/baseline/opt-equity.test index 90f4743a..d8695759 100644 --- a/test/baseline/opt-equity.test +++ b/test/baseline/opt-equity.test @@ -5,5 +5,5 @@ test equity assets 2007/02/02 Opening Balances Assets:Investments:Vanguard:VMMXX 0.350 VMMXX - Equity:Opening Balances + Equity:Opening Balances -0.350 VMMXX end test -- cgit v1.2.3 From 0efdc0cf6f47278184eecdf47137b6253043e027 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 00:40:22 -0600 Subject: The "id" of an item now maps to its UUID --- src/convert.cc | 10 +++++----- src/item.cc | 10 ++++++++++ src/journal.cc | 17 +++++++++++++++++ src/post.cc | 10 ---------- src/xact.cc | 27 --------------------------- src/xact.h | 3 --- 6 files changed, 32 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/convert.cc b/src/convert.cc index da4569cc..15995d05 100644 --- a/src/convert.cc +++ b/src/convert.cc @@ -74,21 +74,21 @@ value_t convert_command(call_scope_t& args) post->amount.in_place_negate(); } - string ref = (xact->has_tag(_("SHA1")) ? - xact->get_tag(_("SHA1"))->to_string() : + string ref = (xact->has_tag(_("UUID")) ? + xact->get_tag(_("UUID"))->to_string() : sha1sum(reader.get_last_line())); checksum_map_t::const_iterator entry = journal.checksum_map.find(ref); if (entry != journal.checksum_map.end()) { INFO(file_context(reader.get_pathname(), reader.get_linenum()) - << "Ignoring known SHA1 " << ref); + << "Ignoring known UUID " << ref); checked_delete(xact); // ignore it continue; } - if (report.HANDLED(rich_data) && ! xact->has_tag(_("SHA1"))) - xact->set_tag(_("SHA1"), string_value(ref)); + if (report.HANDLED(rich_data) && ! xact->has_tag(_("UUID"))) + xact->set_tag(_("UUID"), string_value(ref)); if (xact->posts.front()->account == NULL) { if (account_t * acct = diff --git a/src/item.cc b/src/item.cc index 7184c0ef..97411512 100644 --- a/src/item.cc +++ b/src/item.cc @@ -340,6 +340,12 @@ namespace { value_t get_seq(item_t& item) { return item.pos ? long(item.pos->sequence) : 0L; } + value_t get_id(item_t& item) { + if (optional ref = item.get_tag(_("UUID"))) + return *ref; + else + return item.pos ? long(item.pos->sequence) : 0L; + } value_t get_addr(item_t& item) { return long(&item); @@ -447,6 +453,8 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, case 'i': if (name == "is_account") return WRAP_FUNCTOR(get_wrapper<&ignore>); + else if (name == "id") + return WRAP_FUNCTOR(get_wrapper<&get_id>); break; case 'm': @@ -481,6 +489,8 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, case 'u': if (name == "uncleared") return WRAP_FUNCTOR(get_wrapper<&get_uncleared>); + else if (name == "uuid") + return WRAP_FUNCTOR(get_wrapper<&get_id>); break; case 'v': diff --git a/src/journal.cc b/src/journal.cc index f070ba0e..01cff2dc 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -126,6 +126,23 @@ bool journal_t::add_xact(xact_t * xact) } extend_xact(xact); + + // If a transaction with this UUID has already been seen, simply do + // not add this one to the journal. However, all automated checks + // will have been performed by extend_xact, so asserts can still be + // applied to it. + if (optional ref = xact->get_tag(_("UUID"))) { + std::pair result + = checksum_map.insert(checksum_map_t::value_type(ref->to_string(), xact)); + if (! result.second) { + // jww (2012-02-27): Confirm that the xact in + // (*result.first).second is exact match in its significant + // details to xact. + xact->journal = NULL; + return false; + } + } + xacts.push_back(xact); return true; diff --git a/src/post.cc b/src/post.cc index b40e31f0..d88dd869 100644 --- a/src/post.cc +++ b/src/post.cc @@ -186,12 +186,6 @@ namespace { value_t get_magnitude(post_t& post) { return post.xact->magnitude(); } - value_t get_idstring(post_t& post) { - return string_value(post.xact->idstring()); - } - value_t get_id(post_t& post) { - return string_value(post.xact->id()); - } value_t get_amount(post_t& post) { if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) @@ -459,10 +453,6 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, case 'i': if (name == "index") return WRAP_FUNCTOR(get_wrapper<&get_count>); - else if (name == "id") - return WRAP_FUNCTOR(get_wrapper<&get_id>); - else if (name == "idstring") - return WRAP_FUNCTOR(get_wrapper<&get_idstring>); break; case 'm': diff --git a/src/xact.cc b/src/xact.cc index 596b39fa..ce4c032e 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -467,30 +467,10 @@ void xact_t::add_post(post_t * post) xact_base_t::add_post(post); } -string xact_t::idstring() const -{ - std::ostringstream buf; - buf << format_date(*_date, FMT_WRITTEN); - buf << payee; - magnitude().number().print(buf); - return buf.str(); -} - -string xact_t::id() const -{ - return sha1sum(idstring()); -} - namespace { value_t get_magnitude(xact_t& xact) { return xact.magnitude(); } - value_t get_idstring(xact_t& xact) { - return string_value(xact.idstring()); - } - value_t get_id(xact_t& xact) { - return string_value(xact.id()); - } value_t get_code(xact_t& xact) { if (xact.code) @@ -554,13 +534,6 @@ expr_t::ptr_op_t xact_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_code>); break; - case 'i': - if (name == "id") - return WRAP_FUNCTOR(get_wrapper<&get_id>); - else if (name == "idstring") - return WRAP_FUNCTOR(get_wrapper<&get_idstring>); - break; - case 'm': if (name == "magnitude") return WRAP_FUNCTOR(get_wrapper<&get_magnitude>); diff --git a/src/xact.h b/src/xact.h index 5d7912fc..3e628817 100644 --- a/src/xact.h +++ b/src/xact.h @@ -129,9 +129,6 @@ public: virtual void add_post(post_t * post); - string idstring() const; - string id() const; - virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); -- cgit v1.2.3 From 30e8c0bc65a94819d8375c9359d6ce77d908dcfc Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 02:28:11 -0600 Subject: Stubbed out some code-in-progress --- src/print.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/print.cc b/src/print.cc index a90ea220..dd1dbfab 100644 --- a/src/print.cc +++ b/src/print.cc @@ -123,7 +123,9 @@ namespace { } } +#if 0 std::size_t count = xact.posts.size(); +#endif std::size_t index = 0; foreach (post_t * post, xact.posts) { -- cgit v1.2.3 From 61bc7362ca974543c9b851f8fc81fe981569ad6c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 02:29:42 -0600 Subject: Added new account/payee/commodity directives Also added supporting options: --explicit, --permissive, --pedantic, as well as new behavior for --strict. --- doc/ledger.1 | 5 +- src/journal.cc | 164 +++++++++++-- src/journal.h | 45 +++- src/session.cc | 13 ++ src/session.h | 6 + src/textual.cc | 379 ++++++++++++++++--------------- test/baseline/feat-fixated-prices_2.test | 4 +- test/baseline/opt-explicit.test | 0 test/baseline/opt-pedantic.test | 0 test/baseline/opt-permissive.test | 0 10 files changed, 411 insertions(+), 205 deletions(-) create mode 100644 test/baseline/opt-explicit.test create mode 100644 test/baseline/opt-pedantic.test create mode 100644 test/baseline/opt-permissive.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 21d43ead..5e280889 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd February 26, 2012 +.Dd February 27, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -313,6 +313,7 @@ See .It Fl \-equity .It Fl \-exact .It Fl \-exchange Ar COMM Oo , COMM, ... Oc Pq Fl X +.It Fl \-explicit .It Fl \-file Ar FILE .It Fl \-first Ar INT See @@ -367,10 +368,12 @@ See .It Fl \-pager Ar STR .It Fl \-payee .It Fl \-payee-width Ar INT +.It Fl \-pedantic .It Fl \-pending .It Fl \-percent Pq Fl \% .It Fl \-period Ar PERIOD Pq Fl p .It Fl \-period-sort +.It Fl \-permissive .It Fl \-pivot Ar STR .It Fl \-plot-amount-format Ar FMT .It Fl \-plot-total-format Ar FMT diff --git a/src/journal.cc b/src/journal.cc index 01cff2dc..43f6df77 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -36,6 +36,7 @@ #include "commodity.h" #include "pool.h" #include "xact.h" +#include "post.h" #include "account.h" namespace ledger { @@ -80,9 +81,15 @@ journal_t::~journal_t() void journal_t::initialize() { - master = new account_t; - bucket = NULL; - was_loaded = false; + master = new account_t; + bucket = NULL; + fixed_accounts = false; + fixed_payees = false; + fixed_commodities = false; + fixed_metadata = false; + was_loaded = false; + force_checking = false; + checking_style = CHECK_PERMISSIVE; } void journal_t::add_account(account_t * acct) @@ -105,19 +112,147 @@ account_t * journal_t::find_account_re(const string& regexp) return master->find_account_re(regexp); } -bool journal_t::add_xact(xact_t * xact) +account_t * journal_t::register_account(const string& name, post_t * post, + const string& location, + account_t * master_account) { - if (optional ref = xact->get_tag(_("SHA1"))) { - std::pair result - = checksum_map.insert(checksum_map_t::value_type(ref->to_string(), xact)); - if (! result.second) { - throw_(std::runtime_error, - _("Found duplicated transaction with SHA1: ") - << ref->to_string()); - return false; + account_t * result = NULL; + + if (account_aliases.size() > 0) { + accounts_map::const_iterator i = account_aliases.find(name); + if (i != account_aliases.end()) + result = (*i).second; + } + + if (! result) + result = master_account->find_account(name); + + if (! result->has_flags(ACCOUNT_KNOWN)) { + if (! post) { + if (force_checking) + fixed_accounts = true; + result->add_flags(ACCOUNT_KNOWN); + } + else if (! fixed_accounts && post->_state != item_t::UNCLEARED) { + result->add_flags(ACCOUNT_KNOWN); + } + else if (checking_style == CHECK_WARNING) { + warning_(_("%1Unknown account '%2'") << location + << result->fullname()); + } + else if (checking_style == CHECK_ERROR) { + throw_(parse_error, _("Unknown account '%1'") << result->fullname()); + } + } + + if (result->name == _("Unknown")) { + foreach (account_mapping_t& value, payees_for_unknown_accounts) { + if (value.first.match(post->xact->payee)) { + result = value.second; + break; + } } } + return result; +} + +string journal_t::register_payee(const string& name, xact_t * xact, + const string& location) +{ +#if 0 + std::set::iterator i = known_payees.find(name); + + if (i == known_payees.end()) { + if (! xact) { + if (force_checking) + fixed_payees = true; + known_payees.insert(name); + } + else if (! fixed_payees && xact->_state != item_t::UNCLEARED) { + known_payees.insert(name); + } + else if (checking_style == CHECK_WARNING) { + warning_(_("%1Unknown payee '%2'") << location << name); + } + else if (checking_style == CHECK_ERROR) { + throw_(parse_error, _("Unknown payee '%1'") << name); + } + } +#endif + +#if 0 + foreach (payee_mapping_t& value, context.journal.payee_mappings) { + if (value.first.match(next)) { + xact->payee = value.second; + break; + } + } + if (xact->payee.empty()) + xact->payee = next; +#else + return name; +#endif +} + +void journal_t::register_commodity(commodity_t& comm, + variant context, + const string& location) +{ + if (! comm.has_flags(COMMODITY_KNOWN)) { + if (context.which() == 0) { + if (force_checking) + fixed_commodities = true; + comm.add_flags(COMMODITY_KNOWN); + } + else if (! fixed_commodities && + ((context.which() == 1 && + boost::get(context)->_state != item_t::UNCLEARED) || + (context.which() == 2 && + boost::get(context)->_state != item_t::UNCLEARED))) { + comm.add_flags(COMMODITY_KNOWN); + } + else if (checking_style == CHECK_WARNING) { + warning_(_("%1Unknown commodity '%2'") << location << comm); + } + else if (checking_style == CHECK_ERROR) { + throw_(parse_error, _("Unknown commodity '%1'") << comm); + } + } +} + +#if 0 +void journal_t::register_metadata(const string& key, const string& value, + variant context, + const string& location) +{ + std::set::iterator i = known_tags.find(key); + + if (i == known_tags.end()) { + if (context.which() == 0) { + if (force_checking) + fixed_metadata = true; + known_tags.insert(key); + } + else if (! fixed_metadata && + ((context.which() == 1 && + boost::get(context)->_state != item_t::UNCLEARED) || + (context.which() == 2 && + boost::get(context)->_state != item_t::UNCLEARED))) { + known_tags.insert(key); + } + else if (checking_style == CHECK_WARNING) { + warning_(_("%1Unknown metadata tag '%2'") << location << key); + } + else if (checking_style == CHECK_ERROR) { + throw_(parse_error, _("Unknown metadata tag '%1'") << key); + } + } +} +#endif + +bool journal_t::add_xact(xact_t * xact) +{ xact->journal = this; if (! xact->finalize()) { @@ -187,10 +322,7 @@ std::size_t journal_t::read(std::istream& in, _("No default scope in which to read journal file '%1'") << pathname); - value_t strict = expr_t("strict").calc(*scope); - - count = parse(in, *scope, master_alt ? master_alt : master, - &pathname, strict.to_boolean()); + count = parse(in, *scope, master_alt ? master_alt : master, &pathname); } catch (...) { clear_xdata(); diff --git a/src/journal.h b/src/journal.h index 49a6292b..7411aaf6 100644 --- a/src/journal.h +++ b/src/journal.h @@ -48,10 +48,12 @@ namespace ledger { +class commodity_t; class xact_base_t; class xact_t; class auto_xact_t; class period_xact_t; +class post_t; class account_t; class scope_t; @@ -59,11 +61,12 @@ typedef std::list xacts_list; typedef std::list auto_xacts_list; typedef std::list period_xacts_list; -typedef std::pair payee_mapping_t; -typedef std::list payee_mappings_t; -typedef std::pair account_mapping_t; -typedef std::list account_mappings_t; -typedef std::map checksum_map_t; +typedef std::pair payee_mapping_t; +typedef std::list payee_mappings_t; +typedef std::pair account_mapping_t; +typedef std::list account_mappings_t; +typedef std::map accounts_map; +typedef std::map checksum_map_t; class journal_t : public noncopyable { @@ -116,10 +119,25 @@ public: auto_xacts_list auto_xacts; period_xacts_list period_xacts; std::list sources; + std::set known_payees; + std::set known_tags; + bool fixed_accounts; + bool fixed_payees; + bool fixed_commodities; + bool fixed_metadata; payee_mappings_t payee_mappings; account_mappings_t account_mappings; + accounts_map account_aliases; + account_mappings_t payees_for_unknown_accounts; checksum_map_t checksum_map; bool was_loaded; + bool force_checking; + + enum checking_style_t { + CHECK_PERMISSIVE, + CHECK_WARNING, + CHECK_ERROR + } checking_style; journal_t(); journal_t(const path& pathname); @@ -142,6 +160,20 @@ public: account_t * find_account(const string& name, bool auto_create = true); account_t * find_account_re(const string& regexp); + account_t * register_account(const string& name, post_t * post, + const string& location, + account_t * master = NULL); + string register_payee(const string& name, xact_t * xact, + const string& location); + void register_commodity(commodity_t& comm, + variant context, + const string& location); +#if 0 + void register_metadata(const string& key, const string& value, + variant context, + const string& location); +#endif + bool add_xact(xact_t * xact); void extend_xact(xact_base_t * xact); bool remove_xact(xact_t * xact); @@ -176,8 +208,7 @@ public: std::size_t parse(std::istream& in, scope_t& session_scope, account_t * master = NULL, - const path * original_file = NULL, - bool strict = false); + const path * original_file = NULL); bool has_xdata(); void clear_xdata(); diff --git a/src/session.cc b/src/session.cc index 72e29895..cc069efe 100644 --- a/src/session.cc +++ b/src/session.cc @@ -97,6 +97,16 @@ std::size_t session_t::read_data(const string& master_account) if (HANDLED(price_db_)) price_db_path = resolve_path(HANDLER(price_db_).str()); + if (HANDLED(explicit)) + journal->force_checking = true; + + if (HANDLED(permissive)) + journal->checking_style = journal_t::CHECK_PERMISSIVE; + else if (HANDLED(pedantic)) + journal->checking_style = journal_t::CHECK_ERROR; + else if (HANDLED(strict)) + journal->checking_style = journal_t::CHECK_WARNING; + #if defined(HAVE_BOOST_SERIALIZATION) optional cache; if (HANDLED(cache_) && master_account.empty()) @@ -251,6 +261,7 @@ option_t * session_t::lookup_option(const char * p) break; case 'l': OPT_ALT(price_exp_, leeway_); + else OPT(explicit); break; case 'm': OPT(master_account_); @@ -258,6 +269,8 @@ option_t * session_t::lookup_option(const char * p) case 'p': OPT(price_db_); else OPT(price_exp_); + else OPT(pedantic); + else OPT(permissive); break; case 's': OPT(strict); diff --git a/src/session.h b/src/session.h index b8fd52f2..5d6d4bed 100644 --- a/src/session.h +++ b/src/session.h @@ -91,7 +91,10 @@ public: HANDLER(decimal_comma).report(out); HANDLER(file_).report(out); HANDLER(input_date_format_).report(out); + HANDLER(explicit).report(out); HANDLER(master_account_).report(out); + HANDLER(pedantic).report(out); + HANDLER(permissive).report(out); HANDLER(price_db_).report(out); HANDLER(price_exp_).report(out); HANDLER(strict).report(out); @@ -139,7 +142,10 @@ public: set_input_date_format(args.get(1).c_str()); }); + OPTION(session_t, explicit); OPTION(session_t, master_account_); + OPTION(session_t, pedantic); + OPTION(session_t, permissive); OPTION(session_t, price_db_); OPTION(session_t, strict); }; diff --git a/src/textual.cc b/src/textual.cc index 13032236..8a1968fc 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -49,43 +49,45 @@ namespace ledger { namespace { - typedef std::pair fixed_rate_t; - typedef variant state_t; + typedef std::pair fixed_rate_t; + + struct application_t + { + string label; + variant value; + + application_t(string _label, account_t * acct) + : label(_label), value(acct) {} + application_t(string _label, string tag) + : label(_label), value(tag) {} + application_t(string _label, fixed_rate_t rate) + : label(_label), value(rate) {} + }; class parse_context_t : public noncopyable { public: - journal_t& journal; - scope_t& scope; - std::list state_stack; + std::list apply_stack; + + journal_t& journal; + scope_t& scope; #if defined(TIMELOG_SUPPORT) - time_log_t timelog; + time_log_t timelog; #endif - bool strict; - std::size_t count; - std::size_t errors; - std::size_t sequence; + std::size_t count; + std::size_t errors; + std::size_t sequence; parse_context_t(journal_t& _journal, scope_t& _scope) : journal(_journal), scope(_scope), timelog(journal, scope), - strict(false), count(0), errors(0), sequence(1) { + count(0), errors(0), sequence(1) { timelog.context_count = &count; } - bool front_is_account() { - return state_stack.front().type() == typeid(account_t *); - } - bool front_is_string() { - return state_stack.front().type() == typeid(string); - } - bool front_is_fixed_rate() { - return state_stack.front().type() == typeid(fixed_rate_t); - } - account_t * top_account() { - foreach (state_t& state, state_stack) - if (state.type() == typeid(account_t *)) - return boost::get(state); + foreach (application_t& state, apply_stack) + if (state.value.type() == typeid(account_t *)) + return boost::get(state.value); return NULL; } @@ -101,7 +103,6 @@ namespace { public: parse_context_t& context; instance_t * parent; - accounts_map account_aliases; const path * original_file; path pathname; std::istream& in; @@ -136,36 +137,52 @@ namespace { void clock_out_directive(char * line, bool capitalized); #endif - void default_commodity_directive(char * line); - void default_account_directive(char * line); - void price_conversion_directive(char * line); + bool general_directive(char * line); + + void account_directive(char * line); + void account_alias_directive(char * line); + void account_payee_directive(char * line); + + void payee_directive(char * line); + void payee_alias_directive(char * line); + + void commodity_directive(char * line); +#if 0 + void commodity_alias_directive(char * line); + void commodity_format_directive(char * line); + void commodity_nomarket_directive(char * line); +#endif + + void apply_directive(char * line); + void apply_account_directive(char * line); + void apply_tag_directive(char * line); + void apply_rate_directive(char * line); + void apply_year_directive(char * line); + void end_apply_directive(char * line); + + void xact_directive(char * line, std::streamsize len); + void period_xact_directive(char * line); + void automated_xact_directive(char * line); void price_xact_directive(char * line); + void price_conversion_directive(char * line); void nomarket_directive(char * line); - void year_directive(char * line); - void option_directive(char * line); - void automated_xact_directive(char * line); - void period_xact_directive(char * line); - void xact_directive(char * line, std::streamsize len); + + void default_account_directive(char * line); + void default_commodity_directive(char * line); + void include_directive(char * line); - void master_account_directive(char * line); - void end_directive(char * line); - void alias_directive(char * line); - void fixed_directive(char * line); - void payee_mapping_directive(char * line); - void account_mapping_directive(char * line); - void tag_directive(char * line); + void option_directive(char * line); void define_directive(char * line); + void expr_directive(char * line); void assert_directive(char * line); void check_directive(char * line); void comment_directive(char * line); - void expr_directive(char * line); - bool general_directive(char * line); post_t * parse_post(char * line, std::streamsize len, account_t * account, xact_t * xact, - bool defer_expr = false); + bool defer_expr = false); bool parse_posts(account_t * account, xact_base_t& xact, @@ -402,7 +419,7 @@ void instance_t::read_next_directive() price_xact_directive(line); break; case 'Y': // set the current year - year_directive(line); + apply_year_directive(line); break; } } @@ -514,16 +531,6 @@ void instance_t::nomarket_directive(char * line) commodity->add_flags(COMMODITY_NOMARKET | COMMODITY_KNOWN); } -void instance_t::year_directive(char * line) -{ - unsigned short year(lexical_cast(skip_ws(line + 1))); - DEBUG("times.epoch", "Setting current year to " << year); - // This must be set to the last day of the year, otherwise partial - // dates like "11/01" will refer to last year's november, not the - // current year. - epoch = datetime_t(date_t(year, 12, 31)); -} - void instance_t::option_directive(char * line) { char * p = next_element(line); @@ -774,44 +781,97 @@ void instance_t::include_directive(char * line) if (! files_found) throw_(std::runtime_error, - _("File to include was not found: '%1'") << filename); + _("File to include was not found: %1") << filename); } -void instance_t::master_account_directive(char * line) +void instance_t::apply_directive(char * line) +{ + char * b = next_element(line); + string keyword(line); + if (keyword == "account") + apply_account_directive(b); + else if (keyword == "tag") + apply_tag_directive(b); + else if (keyword == "fixed" || keyword == "rate") + apply_rate_directive(b); + else if (keyword == "year") + apply_year_directive(b); +} + +void instance_t::apply_account_directive(char * line) { if (account_t * acct = context.top_account()->find_account(line)) - context.state_stack.push_front(acct); + context.apply_stack.push_front(application_t("account", acct)); #if !defined(NO_ASSERTS) else assert("Failed to create account" == NULL); #endif } -void instance_t::end_directive(char * kind) +void instance_t::apply_tag_directive(char * line) { - string name(kind ? kind : ""); + string tag(trim_ws(line)); - if ((name.empty() || name == "account") && ! context.front_is_account()) - throw_(std::runtime_error, - _("'end account' directive does not match open directive")); - else if (name == "tag" && ! context.front_is_string()) - throw_(std::runtime_error, - _("'end tag' directive does not match open directive")); - else if (name == "fixed" && ! context.front_is_fixed_rate()) + if (tag.find(':') == string::npos) + tag = string(":") + tag + ":"; + + context.apply_stack.push_front(application_t("tag", tag)); +} + +void instance_t::apply_rate_directive(char * line) +{ + if (optional > price_point = + commodity_pool_t::current_pool->parse_price_directive(trim_ws(line), true)) { + context.apply_stack.push_front + (application_t("fixed", fixed_rate_t(price_point->first, + price_point->second.price))); + } else { + throw_(std::runtime_error, _("Error in fixed directive")); + } +} + +void instance_t::apply_year_directive(char * line) +{ + unsigned short year(lexical_cast(skip_ws(line + 1))); + DEBUG("times.epoch", "Setting current year to " << year); + // This must be set to the last day of the year, otherwise partial + // dates like "11/01" will refer to last year's november, not the + // current year. + epoch = datetime_t(date_t(year, 12, 31)); +} + +void instance_t::end_apply_directive(char * kind) +{ + char * b = next_element(kind); + string name(b ? b : "account"); + + if (context.apply_stack.size() <= 1) throw_(std::runtime_error, - _("'end fixed' directive does not match open directive")); + _("'end %1' found, but no enclosing '%2' directive") + << name << name); - if (context.state_stack.size() <= 1) + if (name != context.apply_stack.front().label) throw_(std::runtime_error, - _("'end' found, but no enclosing tag or account directive")); - else - context.state_stack.pop_front(); + _("'end %1' directive does not match 'apply %2' directive") + << name << context.apply_stack.front().label); + + context.apply_stack.pop_front(); +} + +void instance_t::account_directive(char * line) +{ + char * p = skip_ws(line); + //account_t * account = + context.journal.register_account(p, NULL, + file_context(pathname, linenum), + context.top_account()); } -void instance_t::alias_directive(char * line) +void instance_t::account_alias_directive(char * line) { char * b = skip_ws(line); +#if 0 if (char * e = std::strchr(b, '=')) { char * z = e - 1; while (std::isspace(*z)) @@ -828,21 +888,19 @@ void instance_t::alias_directive(char * line) = account_aliases.insert(accounts_map::value_type(b, acct)); assert(result.second); } +#endif } -void instance_t::fixed_directive(char * line) +void instance_t::account_payee_directive(char * line) { - if (optional > price_point = - commodity_pool_t::current_pool->parse_price_directive(trim_ws(line), - true)) { - context.state_stack.push_front(fixed_rate_t(price_point->first, - price_point->second.price)); - } else { - throw_(std::runtime_error, _("Error in fixed directive")); - } } -void instance_t::payee_mapping_directive(char * line) +void instance_t::payee_directive(char * line) +{ + context.journal.register_payee(line, NULL, file_context(pathname, linenum)); +} + +void instance_t::payee_alias_directive(char * line) { char * payee = skip_ws(line); char * regex = next_element(payee, true); @@ -868,6 +926,27 @@ void instance_t::payee_mapping_directive(char * line) } } +void instance_t::commodity_directive(char * line) +{ + char * p = skip_ws(line); + string symbol; + commodity_t::parse_symbol(p, symbol); + + if (commodity_t * commodity = + commodity_pool_t::current_pool->find_or_create(symbol)) + context.journal.register_commodity(*commodity, 0, + file_context(pathname, linenum)); +} + +#if 0 +void instance_t::commodity_alias_directive(char * line) +{ +} + +void instance_t::commodity_nomarket_directive(char * line) +{ +} + void instance_t::account_mapping_directive(char * line) { char * account_name = skip_ws(line); @@ -895,16 +974,7 @@ void instance_t::account_mapping_directive(char * line) context.top_account()->find_account(account_name))); } } - -void instance_t::tag_directive(char * line) -{ - string tag(trim_ws(line)); - - if (tag.find(':') == string::npos) - tag = string(":") + tag + ":"; - - context.state_stack.push_front(tag); -} +#endif void instance_t::define_directive(char * line) { @@ -958,11 +1028,11 @@ bool instance_t::general_directive(char * line) switch (*p) { case 'a': if (std::strcmp(p, "account") == 0) { - master_account_directive(arg); + account_directive(arg); return true; } - else if (std::strcmp(p, "alias") == 0) { - alias_directive(arg); + else if (std::strcmp(p, "apply") == 0) { + apply_directive(arg); return true; } else if (std::strcmp(p, "assert") == 0) { @@ -979,11 +1049,7 @@ bool instance_t::general_directive(char * line) break; case 'c': - if (std::strcmp(p, "capture") == 0) { - account_mapping_directive(arg); - return true; - } - else if (std::strcmp(p, "check") == 0) { + if (std::strcmp(p, "check") == 0) { check_directive(arg); return true; } @@ -991,6 +1057,10 @@ bool instance_t::general_directive(char * line) comment_directive(arg); return true; } + else if (std::strcmp(p, "commodity") == 0) { + commodity_directive(arg); + return true; + } break; case 'd': @@ -1002,7 +1072,7 @@ bool instance_t::general_directive(char * line) case 'e': if (std::strcmp(p, "end") == 0) { - end_directive(arg); + end_apply_directive(arg); return true; } else if (std::strcmp(p, "expr") == 0) { @@ -1011,13 +1081,6 @@ bool instance_t::general_directive(char * line) } break; - case 'f': - if (std::strcmp(p, "fixed") == 0) { - fixed_directive(arg); - return true; - } - break; - case 'i': if (std::strcmp(p, "include") == 0) { include_directive(arg); @@ -1027,28 +1090,17 @@ bool instance_t::general_directive(char * line) case 'p': if (std::strcmp(p, "payee") == 0) { - payee_mapping_directive(arg); + payee_directive(arg); return true; } break; case 't': - if (std::strcmp(p, "tag") == 0) { - tag_directive(arg); - return true; - } - else if (std::strcmp(p, "test") == 0) { + if (std::strcmp(p, "test") == 0) { comment_directive(arg); return true; } break; - - case 'y': - if (std::strcmp(p, "year") == 0) { - year_directive(arg); - return true; - } - break; } if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) { @@ -1140,30 +1192,10 @@ post_t * instance_t::parse_post(char * line, DEBUG("textual.parse", "line " << linenum << ": " << "Parsed account name " << name); - if (account_aliases.size() > 0) { - accounts_map::const_iterator i = account_aliases.find(name); - if (i != account_aliases.end()) - post->account = (*i).second; - } - if (! post->account) - post->account = account->find_account(name); - - if (context.strict && ! post->account->has_flags(ACCOUNT_KNOWN)) { - if (post->_state == item_t::UNCLEARED) - warning_(_("%1Unknown account '%2'") - << file_context(pathname, linenum) - << post->account->fullname()); - post->account->add_flags(ACCOUNT_KNOWN); - } - - if (post->account->name == _("Unknown")) { - foreach (account_mapping_t& value, context.journal.account_mappings) { - if (value.first.match(xact->payee)) { - post->account = value.second; - break; - } - } - } + post->account = + context.journal.register_account(name, post.get(), + file_context(pathname, linenum), + account); // Parse the optional amount @@ -1179,19 +1211,13 @@ post_t * instance_t::parse_post(char * line, defer_expr, &post->amount_expr); if (! post->amount.is_null() && post->amount.has_commodity()) { - if (context.strict && - ! post->amount.commodity().has_flags(COMMODITY_KNOWN)) { - if (post->_state == item_t::UNCLEARED) - warning_(_("%1Unknown commodity '%2'") - << file_context(pathname, linenum) - << post->amount.commodity()); - post->amount.commodity().add_flags(COMMODITY_KNOWN); - } + context.journal.register_commodity(post->amount.commodity(), post.get(), + file_context(pathname, linenum)); if (! post->amount.has_annotation()) { - foreach (state_t& state, context.state_stack) { - if (state.type() == typeid(fixed_rate_t)) { - fixed_rate_t& rate(boost::get(state)); + foreach (application_t& state, context.apply_stack) { + if (state.value.type() == typeid(fixed_rate_t)) { + fixed_rate_t& rate(boost::get(state.value)); if (*rate.first == post->amount.commodity()) { annotation_t details(rate.second); details.add_flags(ANNOTATION_PRICE_FIXATED); @@ -1388,10 +1414,11 @@ post_t * instance_t::parse_post(char * line, post->pos->end_pos = curr_pos; post->pos->end_line = linenum; - if (! context.state_stack.empty()) { - foreach (const state_t& state, context.state_stack) - if (state.type() == typeid(string)) - post->parse_tags(boost::get(state).c_str(), context.scope, true); + if (! context.apply_stack.empty()) { + foreach (const application_t& state, context.apply_stack) + if (state.value.type() == typeid(string)) + post->parse_tags(boost::get(state.value).c_str(), + context.scope, true); } TRACE_STOP(post_details, 1); @@ -1488,14 +1515,9 @@ xact_t * instance_t::parse_xact(char * line, if (next && *next) { char * p = next_element(next, true); - foreach (payee_mapping_t& value, context.journal.payee_mappings) { - if (value.first.match(next)) { - xact->payee = value.second; - break; - } - } - if (xact->payee.empty()) - xact->payee = next; + xact->payee = + context.journal.register_payee(next, xact.get(), + file_context(pathname, linenum)); next = p; } else { xact->payee = _(""); @@ -1572,7 +1594,7 @@ xact_t * instance_t::parse_xact(char * line, #if 0 if (xact->_state == item_t::UNCLEARED) { - item_t::state_t result = item_t::CLEARED; + item_t::application_t result = item_t::CLEARED; foreach (post_t * post, xact->posts) { if (post->_state == item_t::UNCLEARED) { @@ -1589,11 +1611,11 @@ xact_t * instance_t::parse_xact(char * line, xact->pos->end_pos = curr_pos; xact->pos->end_line = linenum; - if (! context.state_stack.empty()) { - foreach (const state_t& state, context.state_stack) - if (state.type() == typeid(string)) - xact->parse_tags(boost::get(state).c_str(), context.scope, - false); + if (! context.apply_stack.empty()) { + foreach (const application_t& state, context.apply_stack) + if (state.value.type() == typeid(string)) + xact->parse_tags(boost::get(state.value).c_str(), + context.scope, false); } TRACE_STOP(xact_details, 1); @@ -1620,16 +1642,15 @@ expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind, std::size_t journal_t::parse(std::istream& in, scope_t& scope, account_t * master_account, - const path * original_file, - bool strict) + const path * original_file) { TRACE_START(parsing_total, 1, "Total time spent parsing text:"); parse_context_t context(*this, scope); - context.strict = strict; if (master_account || this->master) - context.state_stack.push_front(master_account ? - master_account : this->master); + context.apply_stack.push_front(application_t("account", + master_account ? + master_account : this->master)); instance_t instance(context, in, original_file); instance.parse(); diff --git a/test/baseline/feat-fixated-prices_2.test b/test/baseline/feat-fixated-prices_2.test index b7b71c83..ecbdfe9a 100644 --- a/test/baseline/feat-fixated-prices_2.test +++ b/test/baseline/feat-fixated-prices_2.test @@ -1,10 +1,10 @@ -fixed XCD $0.374531835206 +apply fixed XCD $0.374531835206 2008/04/08 KFC Expenses:Food XCD 43.00 Assets:Cash -end fixed +end apply fixed test reg 08-Apr-08 KFC Expenses:Food XCD 43.00 XCD 43.00 diff --git a/test/baseline/opt-explicit.test b/test/baseline/opt-explicit.test new file mode 100644 index 00000000..e69de29b diff --git a/test/baseline/opt-pedantic.test b/test/baseline/opt-pedantic.test new file mode 100644 index 00000000..e69de29b diff --git a/test/baseline/opt-permissive.test b/test/baseline/opt-permissive.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 9449ca2c807754826ba63831c247779370e9059d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 03:55:02 -0600 Subject: Whitespace fix --- src/xact.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index ce4c032e..08dccaf2 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -667,12 +667,11 @@ void auto_xact_t::extend_xact(xact_base_t& xact) pair.first.calc(bound_scope); } else if (! pair.first.calc(bound_scope).to_boolean()) { - if (pair.second == auto_xact_t::EXPR_ASSERTION) { + if (pair.second == auto_xact_t::EXPR_ASSERTION) throw_(parse_error, _("Transaction assertion failed: %1") << pair.first); - } else { + else warning_(_("Transaction check failed: %1") << pair.first); - } } } } -- cgit v1.2.3 From c1e23c8746fae291c149083b8574d7e2cb21c08a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 03:55:13 -0600 Subject: Added TODO comment --- src/print.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/print.cc b/src/print.cc index dd1dbfab..d46a4b3a 100644 --- a/src/print.cc +++ b/src/print.cc @@ -180,6 +180,8 @@ namespace { amt = post->amount_expr->text(); } else #if 0 + // jww (2012-02-27): Disabled for now because it's not + // outputted valid transactions in every case if (! (count == 2 && index == 2 && (*xact.posts.begin())->amount.commodity() == post->amount.commodity() && -- cgit v1.2.3 From 822919984ec8ba2d02bf4fb33c8df9e4378bc519 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 03:55:20 -0600 Subject: Comment out unneeded code --- src/scope.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index fbc1a889..2720e8fc 100644 --- a/src/scope.h +++ b/src/scope.h @@ -201,7 +201,9 @@ public: virtual void define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { +#if 0 parent->define(kind, name, def); +#endif grandchild.define(kind, name, def); } -- cgit v1.2.3 From 4dd536c670c69a2c65d30281b32597d6e708ff8c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 03:55:36 -0600 Subject: Add "note" valexpr function to accounts --- src/account.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index e201be64..42c10839 100644 --- a/src/account.cc +++ b/src/account.cc @@ -243,6 +243,10 @@ namespace { return long(account.depth); } + value_t get_note(account_t& account) { + return account.note ? string_value(*account.note) : NULL_VALUE; + } + value_t ignore(account_t&) { return false; } @@ -368,6 +372,8 @@ expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, case 'n': if (fn_name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_subcount>); + else if (fn_name == "note") + return WRAP_FUNCTOR(get_wrapper<&get_note>); break; case 'p': -- cgit v1.2.3 From 4b58047ecb578d31ddc13337b92699727dacfd32 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 03:56:07 -0600 Subject: Move when Unknown accounts are processed --- src/journal.cc | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/journal.cc b/src/journal.cc index 43f6df77..aaa53ea4 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -123,10 +123,18 @@ account_t * journal_t::register_account(const string& name, post_t * post, if (i != account_aliases.end()) result = (*i).second; } - if (! result) result = master_account->find_account(name); + if (result->name == _("Unknown")) { + foreach (account_mapping_t& value, payees_for_unknown_accounts) { + if (value.first.match(post->xact->payee)) { + result = value.second; + break; + } + } + } + if (! result->has_flags(ACCOUNT_KNOWN)) { if (! post) { if (force_checking) @@ -145,15 +153,6 @@ account_t * journal_t::register_account(const string& name, post_t * post, } } - if (result->name == _("Unknown")) { - foreach (account_mapping_t& value, payees_for_unknown_accounts) { - if (value.first.match(post->xact->payee)) { - result = value.second; - break; - } - } - } - return result; } -- cgit v1.2.3 From 3d8cad77c4fc5f8221fdf0a29766360b47c5f63c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 03:56:16 -0600 Subject: Enable payee mappings --- src/journal.cc | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/journal.cc b/src/journal.cc index aaa53ea4..17fcb687 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -156,9 +156,10 @@ account_t * journal_t::register_account(const string& name, post_t * post, return result; } -string journal_t::register_payee(const string& name, xact_t * xact, - const string& location) +string journal_t::register_payee(const string& name, xact_t *, const string&) { + string payee; + #if 0 std::set::iterator i = known_payees.find(name); @@ -180,18 +181,14 @@ string journal_t::register_payee(const string& name, xact_t * xact, } #endif -#if 0 - foreach (payee_mapping_t& value, context.journal.payee_mappings) { - if (value.first.match(next)) { - xact->payee = value.second; - break; - } + foreach (payee_mapping_t& value, payee_mappings) { + if (value.first.match(name)) { + payee = value.second; + break; } - if (xact->payee.empty()) - xact->payee = next; -#else - return name; -#endif + } + + return payee.empty() ? name : payee; } void journal_t::register_commodity(commodity_t& comm, -- cgit v1.2.3 From ee530f94338ab8c3130a1e8b52dfa481bdfe596c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 04:08:39 -0600 Subject: Fixed the scoping of auto xact predicates --- src/xact.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index 08dccaf2..5da61b7b 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -619,6 +619,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact) if (initial_post->has_flags(ITEM_GENERATED)) continue; + bind_scope_t bound_scope(*scope_t::default_scope, *initial_post); + bool matches_predicate = false; if (try_quick_match) { try { @@ -646,14 +648,13 @@ void auto_xact_t::extend_xact(xact_base_t& xact) DEBUG("xact.extend.fail", "The quick matcher failed, going back to regular eval"); try_quick_match = false; - matches_predicate = predicate(*initial_post); + matches_predicate = predicate(bound_scope); } } else { - matches_predicate = predicate(*initial_post); + matches_predicate = predicate(bound_scope); } - if (matches_predicate) { - bind_scope_t bound_scope(*scope_t::default_scope, *initial_post); + if (matches_predicate) { if (deferred_notes) { foreach (deferred_tag_data_t& data, *deferred_notes) { if (data.apply_to_post == NULL) @@ -661,6 +662,7 @@ void auto_xact_t::extend_xact(xact_base_t& xact) data.overwrite_existing); } } + if (check_exprs) { foreach (check_expr_pair& pair, *check_exprs) { if (pair.second == auto_xact_t::EXPR_GENERAL) { -- cgit v1.2.3 From 1d89093059b6a017fe882d3e3415503938b126ee Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 04:09:08 -0600 Subject: Output some line numbers with warnings --- src/textual.cc | 6 ++++-- test/baseline/feat-check.test | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 8a1968fc..90dab52d 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -993,7 +993,8 @@ void instance_t::check_directive(char * line) { expr_t expr(line); if (! expr.calc(context.scope).to_boolean()) - warning_(_("Check failed: %1") << line); + warning_(_("%1Check failed: %2") + << file_context(pathname, linenum) << line); } void instance_t::comment_directive(char * line) @@ -1575,7 +1576,8 @@ xact_t * instance_t::parse_xact(char * line, if (c == 'a') { throw_(parse_error, _("Transaction assertion failed: %1") << p); } else { - warning_(_("Transaction check failed: %1") << p); + warning_(_("%1Transaction check failed: %2") + << file_context(pathname, linenum) << p); } } } diff --git a/test/baseline/feat-check.test b/test/baseline/feat-check.test index a5f0c8ad..9a2e72df 100644 --- a/test/baseline/feat-check.test +++ b/test/baseline/feat-check.test @@ -14,5 +14,5 @@ test bal 0 __ERROR__ Warning: Transaction check failed: (account =~ /Foo/) -Warning: Check failed: account("Assets:Checking").all(account =~ /Expense/) +Warning: "$sourcepath/test/baseline/feat-check.test", line 8: Check failed: account("Assets:Checking").all(account =~ /Expense/) end test -- cgit v1.2.3 From 0e7b4fb1821a80ee43fafd55447a01255564eb3d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 04:10:31 -0600 Subject: Implemented account and commodity directives --- src/textual.cc | 269 ++++++++++++++++++++++++++--------------- test/baseline/dir-account.test | 25 ++++ 2 files changed, 199 insertions(+), 95 deletions(-) create mode 100644 test/baseline/dir-account.test (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 90dab52d..de28325f 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -140,18 +140,23 @@ namespace { bool general_directive(char * line); void account_directive(char * line); - void account_alias_directive(char * line); - void account_payee_directive(char * line); + void account_alias_directive(account_t * account, string alias); + void account_payee_directive(account_t * account, string payee); + void account_default_directive(account_t * account); + + void default_account_directive(char * line); + void alias_directive(char * line); void payee_directive(char * line); - void payee_alias_directive(char * line); + void payee_alias_directive(const string& payee, string alias); void commodity_directive(char * line); -#if 0 - void commodity_alias_directive(char * line); - void commodity_format_directive(char * line); - void commodity_nomarket_directive(char * line); -#endif + void commodity_alias_directive(commodity_t& comm, string alias); + void commodity_format_directive(commodity_t& comm, string format); + void commodity_nomarket_directive(commodity_t& comm); + void commodity_default_directive(commodity_t& comm); + + void default_commodity_directive(char * line); void apply_directive(char * line); void apply_account_directive(char * line); @@ -167,16 +172,13 @@ namespace { void price_conversion_directive(char * line); void nomarket_directive(char * line); - void default_account_directive(char * line); - void default_commodity_directive(char * line); - void include_directive(char * line); void option_directive(char * line); - void define_directive(char * line); - void expr_directive(char * line); + void comment_directive(char * line); + + void eval_directive(char * line); void assert_directive(char * line); void check_directive(char * line); - void comment_directive(char * line); post_t * parse_post(char * line, std::streamsize len, @@ -598,7 +600,8 @@ void instance_t::automated_xact_directive(char * line) (remlen > 6 && *p == 'c' && std::strncmp(p, "check", 5) == 0 && std::isspace(p[5])) || (remlen > 5 && *p == 'e' && - std::strncmp(p, "expr", 4) == 0 && std::isspace(p[4]))) { + ((std::strncmp(p, "expr", 4) == 0 && std::isspace(p[4])) || + (std::strncmp(p, "eval", 4) == 0 && std::isspace(p[4]))))) { const char c = *p; p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]); if (! ae->check_exprs) @@ -861,17 +864,89 @@ void instance_t::end_apply_directive(char * kind) void instance_t::account_directive(char * line) { + istream_pos_type beg_pos = line_beg_pos; + std::size_t beg_linenum = linenum; + char * p = skip_ws(line); - //account_t * account = - context.journal.register_account(p, NULL, - file_context(pathname, linenum), - context.top_account()); + account_t * account = + context.journal.register_account(p, NULL, file_context(pathname, linenum), + context.top_account()); + std::auto_ptr ae; + + while (peek_whitespace_line()) { + read_line(line); + char * q = skip_ws(line); + if (! *q) + break; + + char * b = next_element(q); + string keyword(q); + if (keyword == "alias") { + account_alias_directive(account, b); + } + else if (keyword == "payee") { + account_payee_directive(account, b); + } + else if (keyword == "default") { + account_default_directive(account); + } + else if (keyword == "assert" || keyword == "check") { + keep_details_t keeper(true, true, true); + expr_t expr(string("account == \"") + account->fullname() + "\""); + predicate_t pred(expr.get_op(), keeper); + + if (! ae.get()) { + ae.reset(new auto_xact_t(pred)); + + ae->pos = position_t(); + ae->pos->pathname = pathname; + ae->pos->beg_pos = beg_pos; + ae->pos->beg_line = beg_linenum; + ae->pos->sequence = context.sequence++; + ae->check_exprs = auto_xact_t::check_expr_list(); + } + + ae->check_exprs->push_back + (auto_xact_t::check_expr_pair(expr_t(b), + keyword == "assert" ? + auto_xact_t::EXPR_ASSERTION : + auto_xact_t::EXPR_CHECK)); + } + else if (keyword == "eval" || keyword == "expr") { + bind_scope_t bound_scope(context.scope, *account); + expr_t(b).calc(bound_scope); + } + else if (keyword == "note") { + account->note = b; + } + } + + if (ae.get()) { + context.journal.auto_xacts.push_back(ae.get()); + + ae->journal = &context.journal; + ae->pos->end_pos = in.tellg(); + ae->pos->end_line = linenum; + + ae.release(); + } } -void instance_t::account_alias_directive(char * line) +void instance_t::account_alias_directive(account_t * account, string alias) { - char * b = skip_ws(line); -#if 0 + // Once we have an alias name (alias) and the target account + // (account), add a reference to the account in the `account_aliases' + // map, which is used by the post parser to resolve alias references. + trim(alias); + std::pair result + = context.journal + .account_aliases.insert(accounts_map::value_type(alias, account)); + assert(result.second); +} + +void instance_t::alias_directive(char * line) +{ + char * b = next_element(line); if (char * e = std::strchr(b, '=')) { char * z = e - 1; while (std::isspace(*z)) @@ -879,53 +954,47 @@ void instance_t::account_alias_directive(char * line) *e++ = '\0'; e = skip_ws(e); - // Once we have an alias name (b) and the target account - // name (e), add a reference to the account in the - // `account_aliases' map, which is used by the post - // parser to resolve alias references. - account_t * acct = context.top_account()->find_account(e); - std::pair result - = account_aliases.insert(accounts_map::value_type(b, acct)); - assert(result.second); + account_alias_directive(context.top_account()->find_account(e), b); } -#endif } -void instance_t::account_payee_directive(char * line) +void instance_t::account_payee_directive(account_t * account, string payee) { + trim(payee); + context.journal.payees_for_unknown_accounts + .push_back(account_mapping_t(mask_t(payee), account)); } -void instance_t::payee_directive(char * line) +void instance_t::account_default_directive(account_t * account) { - context.journal.register_payee(line, NULL, file_context(pathname, linenum)); + context.journal.bucket = account; } -void instance_t::payee_alias_directive(char * line) +void instance_t::payee_directive(char * line) { - char * payee = skip_ws(line); - char * regex = next_element(payee, true); - - if (regex) - context.journal.payee_mappings.push_back - (payee_mapping_t(mask_t(regex), payee)); + string payee = context.journal + .register_payee(line, NULL, file_context(pathname, linenum)); while (peek_whitespace_line()) { -#if defined(NO_ASSERTS) read_line(line); -#else - std::streamsize len = read_line(line); - assert(len > 0); -#endif - - regex = skip_ws(line); - if (! *regex) + char * p = skip_ws(line); + if (! *p) break; - context.journal.payee_mappings.push_back - (payee_mapping_t(mask_t(regex), payee)); + char * b = next_element(p); + string keyword(p); + if (keyword == "alias") + payee_alias_directive(payee, b); } } +void instance_t::payee_alias_directive(const string& payee, string alias) +{ + trim(alias); + context.journal.payee_mappings + .push_back(payee_mapping_t(mask_t(alias), payee)); +} + void instance_t::commodity_directive(char * line) { char * p = skip_ws(line); @@ -933,53 +1002,65 @@ void instance_t::commodity_directive(char * line) commodity_t::parse_symbol(p, symbol); if (commodity_t * commodity = - commodity_pool_t::current_pool->find_or_create(symbol)) + commodity_pool_t::current_pool->find_or_create(symbol)) { context.journal.register_commodity(*commodity, 0, file_context(pathname, linenum)); + + while (peek_whitespace_line()) { + read_line(line); + char * q = skip_ws(line); + if (! *q) + break; + + char * b = next_element(q); + string keyword(q); + if (keyword == "alias") + commodity_alias_directive(*commodity, b); + else if (keyword == "format") + commodity_format_directive(*commodity, b); + else if (keyword == "nomarket") + commodity_nomarket_directive(*commodity); + else if (keyword == "default") + commodity_default_directive(*commodity); + } + } } -#if 0 -void instance_t::commodity_alias_directive(char * line) +void instance_t::commodity_alias_directive(commodity_t&, string) { +#if 0 + trim(alias); + std::pair result + = commodity_pool_t::current_pool->commodities.insert + (commodity_pool_t::commodities_map::value_type(alias, &comm)); + if (! result.second) + throw_(parse_error, + _("Cannot use existing commodity name as an alias: %1") << alias); +#endif } -void instance_t::commodity_nomarket_directive(char * line) +void instance_t::commodity_format_directive(commodity_t&, string format) { + trim(format); + amount_t amt; + amt.parse(format); + VERIFY(amt.valid()); } -void instance_t::account_mapping_directive(char * line) +void instance_t::commodity_nomarket_directive(commodity_t& comm) { - char * account_name = skip_ws(line); - char * payee_regex = next_element(account_name, true); - - if (payee_regex) - context.journal.account_mappings.push_back - (account_mapping_t(mask_t(payee_regex), - context.top_account()->find_account(account_name))); - - while (peek_whitespace_line()) { -#if defined(NO_ASSERTS) - read_line(line); -#else - std::streamsize len = read_line(line); - assert(len > 0); -#endif - - payee_regex = skip_ws(line); - if (! *payee_regex) - break; + comm.add_flags(COMMODITY_NOMARKET); +} - context.journal.account_mappings.push_back - (account_mapping_t(mask_t(payee_regex), - context.top_account()->find_account(account_name))); - } +void instance_t::commodity_default_directive(commodity_t& comm) +{ + commodity_pool_t::current_pool->default_commodity = &comm; } -#endif -void instance_t::define_directive(char * line) +void instance_t::eval_directive(char * line) { - expr_t def(skip_ws(line)); - def.compile(context.scope); // causes definitions to be established + expr_t expr(line); + expr.calc(context.scope); } void instance_t::assert_directive(char * line) @@ -1008,12 +1089,6 @@ void instance_t::comment_directive(char * line) } } -void instance_t::expr_directive(char * line) -{ - expr_t expr(line); - expr.calc(context.scope); -} - bool instance_t::general_directive(char * line) { char buf[8192]; @@ -1032,6 +1107,10 @@ bool instance_t::general_directive(char * line) account_directive(arg); return true; } + else if (std::strcmp(p, "alias") == 0) { + alias_directive(arg); + return true; + } else if (std::strcmp(p, "apply") == 0) { apply_directive(arg); return true; @@ -1066,7 +1145,7 @@ bool instance_t::general_directive(char * line) case 'd': if (std::strcmp(p, "def") == 0 || std::strcmp(p, "define") == 0) { - define_directive(arg); + eval_directive(arg); return true; } break; @@ -1076,8 +1155,8 @@ bool instance_t::general_directive(char * line) end_apply_directive(arg); return true; } - else if (std::strcmp(p, "expr") == 0) { - expr_directive(arg); + else if (std::strcmp(p, "expr") == 0 || std::strcmp(p, "eval") == 0) { + eval_directive(arg); return true; } break; @@ -1189,7 +1268,7 @@ post_t * instance_t::parse_post(char * line, p++; e--; } - string name(p, static_cast(e - p)); + string name(p, static_cast(e - p)); DEBUG("textual.parse", "line " << linenum << ": " << "Parsed account name " << name); @@ -1429,8 +1508,8 @@ post_t * instance_t::parse_post(char * line, } catch (const std::exception&) { add_error_context(_("While parsing posting:")); - add_error_context(line_context(buf, static_cast(beg), - static_cast(len))); + add_error_context(line_context(buf, static_cast(beg), + static_cast(len))); throw; } } diff --git a/test/baseline/dir-account.test b/test/baseline/dir-account.test new file mode 100644 index 00000000..b049ae83 --- /dev/null +++ b/test/baseline/dir-account.test @@ -0,0 +1,25 @@ +account Assets:Cash + assert abs(amount) <= 20 + check commodity == '$' + +account Expenses:Food + alias food + payee KFC + +commodity $ + format $1,000.00 + +2012-02-27 KFC + Expenses:Unknown $20.00 + Assets:Cash + +2012-02-28 KFC + food $20.00 + Assets:Cash + +test reg --strict +12-Feb-27 KFC Expenses:Food $20.00 $20.00 + Assets:Cash $-20.00 0 +12-Feb-28 KFC Expenses:Food $20.00 $20.00 + Assets:Cash $-20.00 0 +end test -- cgit v1.2.3 From 2ec35ea6e98dcfb03fb8c2e90b85991aaa7be2fa Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 04:59:04 -0600 Subject: Implement the "tag" metadata directive --- src/expr.h | 9 +++++++ src/journal.cc | 66 +++++++++++++++++++++++++++++++++++++++++++--- src/journal.h | 28 ++++++++++---------- src/textual.cc | 58 +++++++++++++++++++++++++++++++--------- src/xact.cc | 6 ++--- src/xact.h | 16 +++-------- test/baseline/dir-tag.test | 21 +++++++++++++++ 7 files changed, 158 insertions(+), 46 deletions(-) create mode 100644 test/baseline/dir-tag.test (limited to 'src') diff --git a/src/expr.h b/src/expr.h index c4cd5dc5..79ae2864 100644 --- a/src/expr.h +++ b/src/expr.h @@ -58,6 +58,15 @@ public: typedef intrusive_ptr ptr_op_t; typedef intrusive_ptr const_ptr_op_t; + enum check_expr_kind_t { + EXPR_GENERAL, + EXPR_ASSERTION, + EXPR_CHECK + }; + + typedef std::pair check_expr_pair; + typedef std::list check_expr_list; + protected: ptr_op_t ptr; diff --git a/src/journal.cc b/src/journal.cc index 17fcb687..8f382125 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -217,8 +217,38 @@ void journal_t::register_commodity(commodity_t& comm, } } -#if 0 -void journal_t::register_metadata(const string& key, const string& value, +namespace { + void check_metadata(journal_t& journal, + const string& key, const value_t& value, + variant context, + const string& location) + { + std::pair range = + journal.tag_check_exprs.equal_range(key); + + for (tag_check_exprs_map::iterator i = range.first; + i != range.second; + ++i) { + value_scope_t val_scope + (context.which() == 1 ? + static_cast(*boost::get(context)) : + static_cast(*boost::get(context)), value); + + if (! (*i).second.first.calc(val_scope).to_boolean()) { + if ((*i).second.second == expr_t::EXPR_ASSERTION) + throw_(parse_error, + _("Metadata assertion failed for (%1: %2): %3") + << key << value << (*i).second.first); + else + warning_(_("%1Metadata check failed for (%2: %3): %4") + << location << key << value << (*i).second.first); + } + } + } +} + +void journal_t::register_metadata(const string& key, const value_t& value, variant context, const string& location) { @@ -244,8 +274,34 @@ void journal_t::register_metadata(const string& key, const string& value, throw_(parse_error, _("Unknown metadata tag '%1'") << key); } } + + if (! value.is_null()) + check_metadata(*this, key, value, context, location); +} + +namespace { + void check_all_metadata(journal_t& journal, + variant context) + { + xact_t * xact = context.which() == 1 ? boost::get(context) : NULL; + post_t * post = context.which() == 2 ? boost::get(context) : NULL; + + if ((xact || post) && xact ? xact->metadata : post->metadata) { + foreach (const item_t::string_map::value_type& pair, + xact ? *xact->metadata : *post->metadata) { + const string& key(pair.first); + + // jww (2012-02-27): We really need to know the parsing context, + // both here and for the call to warning_ in + // xact_t::extend_xact. + if (optional value = pair.second.first) + journal.register_metadata(key, *value, context, ""); + else + journal.register_metadata(key, NULL_VALUE, context, ""); + } + } + } } -#endif bool journal_t::add_xact(xact_t * xact) { @@ -258,6 +314,10 @@ bool journal_t::add_xact(xact_t * xact) extend_xact(xact); + check_all_metadata(*this, xact); + foreach (post_t * post, xact->posts) + check_all_metadata(*this, post); + // If a transaction with this UUID has already been seen, simply do // not add this one to the journal. However, all automated checks // will have been performed by extend_xact, so asserts can still be diff --git a/src/journal.h b/src/journal.h index 7411aaf6..9c42ec6a 100644 --- a/src/journal.h +++ b/src/journal.h @@ -45,10 +45,10 @@ #include "utils.h" #include "times.h" #include "mask.h" +#include "expr.h" namespace ledger { -class commodity_t; class xact_base_t; class xact_t; class auto_xact_t; @@ -57,16 +57,17 @@ class post_t; class account_t; class scope_t; -typedef std::list xacts_list; -typedef std::list auto_xacts_list; -typedef std::list period_xacts_list; - -typedef std::pair payee_mapping_t; -typedef std::list payee_mappings_t; -typedef std::pair account_mapping_t; -typedef std::list account_mappings_t; -typedef std::map accounts_map; -typedef std::map checksum_map_t; +typedef std::list xacts_list; +typedef std::list auto_xacts_list; +typedef std::list period_xacts_list; +typedef std::pair payee_mapping_t; +typedef std::list payee_mappings_t; +typedef std::pair account_mapping_t; +typedef std::list account_mappings_t; +typedef std::map accounts_map; +typedef std::map checksum_map_t; +typedef std::multimap tag_check_exprs_map; class journal_t : public noncopyable { @@ -130,6 +131,7 @@ public: accounts_map account_aliases; account_mappings_t payees_for_unknown_accounts; checksum_map_t checksum_map; + tag_check_exprs_map tag_check_exprs; bool was_loaded; bool force_checking; @@ -168,11 +170,9 @@ public: void register_commodity(commodity_t& comm, variant context, const string& location); -#if 0 - void register_metadata(const string& key, const string& value, + void register_metadata(const string& key, const value_t& value, variant context, const string& location); -#endif bool add_xact(xact_t * xact); void extend_xact(xact_base_t * xact); diff --git a/src/textual.cc b/src/textual.cc index de28325f..5b0193cb 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -158,6 +158,8 @@ namespace { void default_commodity_directive(char * line); + void tag_directive(char * line); + void apply_directive(char * line); void apply_account_directive(char * line); void apply_tag_directive(char * line); @@ -605,14 +607,14 @@ void instance_t::automated_xact_directive(char * line) const char c = *p; p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]); if (! ae->check_exprs) - ae->check_exprs = auto_xact_t::check_expr_list(); + ae->check_exprs = expr_t::check_expr_list(); ae->check_exprs->push_back - (auto_xact_t::check_expr_pair(expr_t(p), - c == 'a' ? - auto_xact_t::EXPR_ASSERTION : - (c == 'c' ? - auto_xact_t::EXPR_CHECK : - auto_xact_t::EXPR_GENERAL))); + (expr_t::check_expr_pair(expr_t(p), + c == 'a' ? + expr_t::EXPR_ASSERTION : + (c == 'c' ? + expr_t::EXPR_CHECK : + expr_t::EXPR_GENERAL))); } else { reveal_context = false; @@ -903,14 +905,14 @@ void instance_t::account_directive(char * line) ae->pos->beg_pos = beg_pos; ae->pos->beg_line = beg_linenum; ae->pos->sequence = context.sequence++; - ae->check_exprs = auto_xact_t::check_expr_list(); + ae->check_exprs = expr_t::check_expr_list(); } ae->check_exprs->push_back - (auto_xact_t::check_expr_pair(expr_t(b), - keyword == "assert" ? - auto_xact_t::EXPR_ASSERTION : - auto_xact_t::EXPR_CHECK)); + (expr_t::check_expr_pair(expr_t(b), + keyword == "assert" ? + expr_t::EXPR_ASSERTION : + expr_t::EXPR_CHECK)); } else if (keyword == "eval" || keyword == "expr") { bind_scope_t bound_scope(context.scope, *account); @@ -1057,6 +1059,32 @@ void instance_t::commodity_default_directive(commodity_t& comm) commodity_pool_t::current_pool->default_commodity = &comm; } +void instance_t::tag_directive(char * line) +{ + char * p = skip_ws(line); + context.journal.register_metadata(p, NULL_VALUE, 0, + file_context(pathname, linenum)); + + while (peek_whitespace_line()) { + read_line(line); + char * q = skip_ws(line); + if (! *q) + break; + + char * b = next_element(q); + string keyword(q); + if (keyword == "assert" || keyword == "check") { + context.journal.tag_check_exprs.insert + (tag_check_exprs_map::value_type + (string(p), + expr_t::check_expr_pair(expr_t(b), + keyword == "assert" ? + expr_t::EXPR_ASSERTION : + expr_t::EXPR_CHECK))); + } + } +} + void instance_t::eval_directive(char * line) { expr_t expr(line); @@ -1176,7 +1204,11 @@ bool instance_t::general_directive(char * line) break; case 't': - if (std::strcmp(p, "test") == 0) { + if (std::strcmp(p, "tag") == 0) { + tag_directive(arg); + return true; + } + else if (std::strcmp(p, "test") == 0) { comment_directive(arg); return true; } diff --git a/src/xact.cc b/src/xact.cc index 5da61b7b..ae571b62 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -664,12 +664,12 @@ void auto_xact_t::extend_xact(xact_base_t& xact) } if (check_exprs) { - foreach (check_expr_pair& pair, *check_exprs) { - if (pair.second == auto_xact_t::EXPR_GENERAL) { + foreach (expr_t::check_expr_pair& pair, *check_exprs) { + if (pair.second == expr_t::EXPR_GENERAL) { pair.first.calc(bound_scope); } else if (! pair.first.calc(bound_scope).to_boolean()) { - if (pair.second == auto_xact_t::EXPR_ASSERTION) + if (pair.second == expr_t::EXPR_ASSERTION) throw_(parse_error, _("Transaction assertion failed: %1") << pair.first); else diff --git a/src/xact.h b/src/xact.h index 3e628817..ff4b7bc2 100644 --- a/src/xact.h +++ b/src/xact.h @@ -152,21 +152,11 @@ private: class auto_xact_t : public xact_base_t { public: - predicate_t predicate; - bool try_quick_match; - + predicate_t predicate; + bool try_quick_match; std::map memoized_results; - enum xact_expr_kind_t { - EXPR_GENERAL, - EXPR_ASSERTION, - EXPR_CHECK - }; - - typedef std::pair check_expr_pair; - typedef std::list check_expr_list; - - optional check_exprs; + optional check_exprs; struct deferred_tag_data_t { string tag_data; diff --git a/test/baseline/dir-tag.test b/test/baseline/dir-tag.test new file mode 100644 index 00000000..b1858146 --- /dev/null +++ b/test/baseline/dir-tag.test @@ -0,0 +1,21 @@ +tag Happy + check value == 'Valley' + +2012-02-27 * KFC + ; Happy: Valley + Expenses:Unknown $20.00 + ; Happy: Summer + Assets:Cash + +2012-02-28 * KFC + food $20.00 + Assets:Cash + +test reg +12-Feb-27 KFC Expenses:Unknown $20.00 $20.00 + Assets:Cash $-20.00 0 +12-Feb-28 KFC food $20.00 $20.00 + Assets:Cash $-20.00 0 +__ERROR__ +Warning: Metadata check failed for (Happy: Summer): (value == "Valley") +end test -- cgit v1.2.3 From 5532a1a8b756f0f7499570afccb1d4fc3e9bc76d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 11:52:23 -0600 Subject: Added --check-payees option --- doc/ledger.1 | 1 + src/journal.cc | 36 +++++++++++++++++++----------------- src/journal.h | 1 + src/session.cc | 3 +++ src/session.h | 2 ++ test/baseline/opt-check-payees.test | 0 6 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 test/baseline/opt-check-payees.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 5e280889..656a3866 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -281,6 +281,7 @@ transactions they are contained in. See the manual for more information. .It Fl \-budget-format Ar FMT .It Fl \-by-payee Pq Fl P .It Fl \-cache Ar FILE +.It Fl \-check-payees .It Fl \-cleared Pq Fl C .It Fl \-cleared-format Ar FMT .It Fl \-collapse Pq Fl n diff --git a/src/journal.cc b/src/journal.cc index 8f382125..355f3ee6 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -89,6 +89,7 @@ void journal_t::initialize() fixed_metadata = false; was_loaded = false; force_checking = false; + check_payees = false; checking_style = CHECK_PERMISSIVE; } @@ -160,26 +161,27 @@ string journal_t::register_payee(const string& name, xact_t *, const string&) { string payee; -#if 0 - std::set::iterator i = known_payees.find(name); + if (check_payees && + (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR)) { + std::set::iterator i = known_payees.find(name); - if (i == known_payees.end()) { - if (! xact) { - if (force_checking) - fixed_payees = true; - known_payees.insert(name); - } - else if (! fixed_payees && xact->_state != item_t::UNCLEARED) { - known_payees.insert(name); - } - else if (checking_style == CHECK_WARNING) { - warning_(_("%1Unknown payee '%2'") << location << name); - } - else if (checking_style == CHECK_ERROR) { - throw_(parse_error, _("Unknown payee '%1'") << name); + if (i == known_payees.end()) { + if (! xact) { + if (force_checking) + fixed_payees = true; + known_payees.insert(name); + } + else if (! fixed_payees && xact->_state != item_t::UNCLEARED) { + known_payees.insert(name); + } + else if (checking_style == CHECK_WARNING) { + warning_(_("%1Unknown payee '%2'") << location << name); + } + else if (checking_style == CHECK_ERROR) { + throw_(parse_error, _("Unknown payee '%1'") << name); + } } } -#endif foreach (payee_mapping_t& value, payee_mappings) { if (value.first.match(name)) { diff --git a/src/journal.h b/src/journal.h index 9c42ec6a..d7cf94da 100644 --- a/src/journal.h +++ b/src/journal.h @@ -134,6 +134,7 @@ public: tag_check_exprs_map tag_check_exprs; bool was_loaded; bool force_checking; + bool check_payees; enum checking_style_t { CHECK_PERMISSIVE, diff --git a/src/session.cc b/src/session.cc index cc069efe..797ee3e5 100644 --- a/src/session.cc +++ b/src/session.cc @@ -99,6 +99,8 @@ std::size_t session_t::read_data(const string& master_account) if (HANDLED(explicit)) journal->force_checking = true; + if (HANDLED(check_payees)) + journal->check_payees = true; if (HANDLED(permissive)) journal->checking_style = journal_t::CHECK_PERMISSIVE; @@ -248,6 +250,7 @@ option_t * session_t::lookup_option(const char * p) break; case 'c': OPT(cache_); + else OPT(check_payees); break; case 'd': OPT(download); // -Q diff --git a/src/session.h b/src/session.h index 5d6d4bed..680b8a0e 100644 --- a/src/session.h +++ b/src/session.h @@ -87,6 +87,7 @@ public: void report_options(std::ostream& out) { HANDLER(cache_).report(out); + HANDLER(check_payees).report(out); HANDLER(download).report(out); HANDLER(decimal_comma).report(out); HANDLER(file_).report(out); @@ -110,6 +111,7 @@ public: */ OPTION(session_t, cache_); + OPTION(session_t, check_payees); OPTION(session_t, download); // -Q OPTION_(session_t, decimal_comma, DO() { diff --git a/test/baseline/opt-check-payees.test b/test/baseline/opt-check-payees.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 3ed09fc85b60e354906f881db1e2bbfa47331273 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 11:52:27 -0600 Subject: Optimizations --- src/journal.cc | 110 ++++++++++++++++++++++++++++++--------------------------- src/xact.cc | 7 ++-- 2 files changed, 62 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/journal.cc b/src/journal.cc index 355f3ee6..4900a7f3 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -136,21 +136,23 @@ account_t * journal_t::register_account(const string& name, post_t * post, } } - if (! result->has_flags(ACCOUNT_KNOWN)) { - if (! post) { - if (force_checking) - fixed_accounts = true; - result->add_flags(ACCOUNT_KNOWN); - } - else if (! fixed_accounts && post->_state != item_t::UNCLEARED) { - result->add_flags(ACCOUNT_KNOWN); - } - else if (checking_style == CHECK_WARNING) { - warning_(_("%1Unknown account '%2'") << location - << result->fullname()); - } - else if (checking_style == CHECK_ERROR) { - throw_(parse_error, _("Unknown account '%1'") << result->fullname()); + if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { + if (! result->has_flags(ACCOUNT_KNOWN)) { + if (! post) { + if (force_checking) + fixed_accounts = true; + result->add_flags(ACCOUNT_KNOWN); + } + else if (! fixed_accounts && post->_state != item_t::UNCLEARED) { + result->add_flags(ACCOUNT_KNOWN); + } + else if (checking_style == CHECK_WARNING) { + warning_(_("%1Unknown account '%2'") << location + << result->fullname()); + } + else if (checking_style == CHECK_ERROR) { + throw_(parse_error, _("Unknown account '%1'") << result->fullname()); + } } } @@ -197,24 +199,26 @@ void journal_t::register_commodity(commodity_t& comm, variant context, const string& location) { - if (! comm.has_flags(COMMODITY_KNOWN)) { - if (context.which() == 0) { - if (force_checking) - fixed_commodities = true; - comm.add_flags(COMMODITY_KNOWN); - } - else if (! fixed_commodities && - ((context.which() == 1 && - boost::get(context)->_state != item_t::UNCLEARED) || - (context.which() == 2 && - boost::get(context)->_state != item_t::UNCLEARED))) { - comm.add_flags(COMMODITY_KNOWN); - } - else if (checking_style == CHECK_WARNING) { - warning_(_("%1Unknown commodity '%2'") << location << comm); - } - else if (checking_style == CHECK_ERROR) { - throw_(parse_error, _("Unknown commodity '%1'") << comm); + if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { + if (! comm.has_flags(COMMODITY_KNOWN)) { + if (context.which() == 0) { + if (force_checking) + fixed_commodities = true; + comm.add_flags(COMMODITY_KNOWN); + } + else if (! fixed_commodities && + ((context.which() == 1 && + boost::get(context)->_state != item_t::UNCLEARED) || + (context.which() == 2 && + boost::get(context)->_state != item_t::UNCLEARED))) { + comm.add_flags(COMMODITY_KNOWN); + } + else if (checking_style == CHECK_WARNING) { + warning_(_("%1Unknown commodity '%2'") << location << comm); + } + else if (checking_style == CHECK_ERROR) { + throw_(parse_error, _("Unknown commodity '%1'") << comm); + } } } } @@ -254,26 +258,28 @@ void journal_t::register_metadata(const string& key, const value_t& value, variant context, const string& location) { - std::set::iterator i = known_tags.find(key); + if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { + std::set::iterator i = known_tags.find(key); - if (i == known_tags.end()) { - if (context.which() == 0) { - if (force_checking) - fixed_metadata = true; - known_tags.insert(key); - } - else if (! fixed_metadata && - ((context.which() == 1 && - boost::get(context)->_state != item_t::UNCLEARED) || - (context.which() == 2 && - boost::get(context)->_state != item_t::UNCLEARED))) { - known_tags.insert(key); - } - else if (checking_style == CHECK_WARNING) { - warning_(_("%1Unknown metadata tag '%2'") << location << key); - } - else if (checking_style == CHECK_ERROR) { - throw_(parse_error, _("Unknown metadata tag '%1'") << key); + if (i == known_tags.end()) { + if (context.which() == 0) { + if (force_checking) + fixed_metadata = true; + known_tags.insert(key); + } + else if (! fixed_metadata && + ((context.which() == 1 && + boost::get(context)->_state != item_t::UNCLEARED) || + (context.which() == 2 && + boost::get(context)->_state != item_t::UNCLEARED))) { + known_tags.insert(key); + } + else if (checking_style == CHECK_WARNING) { + warning_(_("%1Unknown metadata tag '%2'") << location << key); + } + else if (checking_style == CHECK_ERROR) { + throw_(parse_error, _("Unknown metadata tag '%1'") << key); + } } } diff --git a/src/xact.cc b/src/xact.cc index ae571b62..b581db95 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -565,7 +565,6 @@ bool xact_t::valid() const } namespace { - bool post_pred(expr_t::ptr_op_t op, post_t& post) { switch (op->kind) { @@ -582,6 +581,9 @@ namespace { else break; + case expr_t::op_t::O_EQ: + return post_pred(op->left(), post) == post_pred(op->right(), post); + case expr_t::op_t::O_NOT: return ! post_pred(op->left(), post); @@ -604,8 +606,7 @@ namespace { throw_(calc_error, _("Unhandled operator")); return false; } - -} // unnamed namespace +} void auto_xact_t::extend_xact(xact_base_t& xact) { -- cgit v1.2.3 From c1ab9cad30c76449a3b5d463e7aa3b6874b83199 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:37:53 -0600 Subject: --explicit wasn't being handled correctly --- src/session.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/session.cc b/src/session.cc index 797ee3e5..31122daa 100644 --- a/src/session.cc +++ b/src/session.cc @@ -256,6 +256,9 @@ option_t * session_t::lookup_option(const char * p) OPT(download); // -Q else OPT(decimal_comma); break; + case 'e': + OPT(explicit); + break; case 'f': OPT_(file_); // -f break; @@ -264,7 +267,6 @@ option_t * session_t::lookup_option(const char * p) break; case 'l': OPT_ALT(price_exp_, leeway_); - else OPT(explicit); break; case 'm': OPT(master_account_); -- cgit v1.2.3 From 052b1f28b7c80b568d63c8bf1801aa7c439a29a4 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:38:06 -0600 Subject: Chain the current report to the current session --- src/global.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/global.cc b/src/global.cc index c0698376..34427f4b 100644 --- a/src/global.cc +++ b/src/global.cc @@ -68,7 +68,7 @@ global_scope_t::global_scope_t(char ** envp) // a GUI were calling into Ledger it would have one session object per // open document, with a separate report_t object for each report it // generated. - report_stack.push_front(new report_t(session())); + report_stack.push_front(new report_t(*session_ptr)); scope_t::default_scope = &report(); // Read the user's options, in the following order: -- cgit v1.2.3 From 196816d5aab6cde6cf6aaf11f77cfd5b507027d6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:38:14 -0600 Subject: Added a TODO comment --- src/xact.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index b581db95..6c07c862 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -746,6 +746,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact) new_post->copy_details(*post); new_post->add_flags(ITEM_GENERATED); + // jww (2012-02-27): Do account directive assertions get applied + // to postings generated from automated transactions? xact.add_post(new_post); new_post->account->add_post(new_post); -- cgit v1.2.3 From d51d33d788653383764f27a021b42916a15da509 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:38:23 -0600 Subject: Added some missing variable names --- src/journal.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/journal.cc b/src/journal.cc index 4900a7f3..ea90fa05 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -159,7 +159,8 @@ account_t * journal_t::register_account(const string& name, post_t * post, return result; } -string journal_t::register_payee(const string& name, xact_t *, const string&) +string journal_t::register_payee(const string& name, xact_t * xact, + const string& location) { string payee; -- cgit v1.2.3 From bee748cc930a18f8d92134c6eaf75a023c99fa9d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:38:39 -0600 Subject: Added a TODO comment --- src/textual.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 5b0193cb..46489778 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -915,6 +915,8 @@ void instance_t::account_directive(char * line) expr_t::EXPR_CHECK)); } else if (keyword == "eval" || keyword == "expr") { + // jww (2012-02-27): Make account into symbol scopes so that this + // can be used to override definitions within the account. bind_scope_t bound_scope(context.scope, *account); expr_t(b).calc(bound_scope); } -- cgit v1.2.3 From 268127cf11a8da96b81c751abc93196c2c26bbee Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:38:51 -0600 Subject: Added 'note' sub-directive to 'commodity' --- src/textual.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 46489778..3ca0722e 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1026,6 +1026,8 @@ void instance_t::commodity_directive(char * line) commodity_nomarket_directive(*commodity); else if (keyword == "default") commodity_default_directive(*commodity); + else if (keyword == "note") + commodity->set_note(string(b)); } } } -- cgit v1.2.3 From 440371d2968bb6dc18838791e57b0e31ec172067 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:39:00 -0600 Subject: Added a TODO comment --- src/textual.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 3ca0722e..0590bbfa 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1047,6 +1047,8 @@ void instance_t::commodity_alias_directive(commodity_t&, string) void instance_t::commodity_format_directive(commodity_t&, string format) { + // jww (2012-02-27): A format specified this way should turn off + // observational formatting. trim(format); amount_t amt; amt.parse(format); -- cgit v1.2.3 From 07137cc042fc312ae92db35167b8874a1adf2c21 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:39:12 -0600 Subject: 'end apply' doesn't need a qualifier --- src/textual.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 0590bbfa..82065f3c 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -849,16 +849,16 @@ void instance_t::apply_year_directive(char * line) void instance_t::end_apply_directive(char * kind) { char * b = next_element(kind); - string name(b ? b : "account"); + string name(b ? b : " "); if (context.apply_stack.size() <= 1) throw_(std::runtime_error, - _("'end %1' found, but no enclosing '%2' directive") + _("'end apply%1' found, but no enclosing 'apply%2' directive") << name << name); - if (name != context.apply_stack.front().label) + if (name != " " && name != context.apply_stack.front().label) throw_(std::runtime_error, - _("'end %1' directive does not match 'apply %2' directive") + _("'end apply%1' directive does not match 'apply %2' directive") << name << context.apply_stack.front().label); context.apply_stack.pop_front(); -- cgit v1.2.3 From acb3c573323cb21d5a649b16cadd0d4b50cdf730 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 22:48:02 -0600 Subject: Correctly elide amount sometimes in "print" --- src/print.cc | 54 +++++++++++++++++++++++++++++++++---------- test/baseline/opt-equity.test | 2 +- 2 files changed, 43 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/print.cc b/src/print.cc index d46a4b3a..56a2ec7b 100644 --- a/src/print.cc +++ b/src/print.cc @@ -41,6 +41,36 @@ namespace ledger { namespace { + bool post_has_simple_amount(const post_t& post) + { + // Is the amount the result of a computation, i.e., it wasn't + // explicit specified by the user? + if (post.has_flags(POST_CALCULATED)) + return false; + + // Is the amount still empty? This shouldn't be true by this point, + // but we check anyway for safety. + if (post.amount.is_null()) + return false; + + // Is the amount a complex expression. If so, the first 'if' should + // have triggered. + if (post.amount_expr) + return false; + + // Is there a balance assignment? If so, don't elide the amount as + // that can change the semantics. + if (post.assigned_amount) + return false; + + // Does it have an explicitly specified cost (i.e., one that wasn't + // calculated for the user)? If so, don't elide the amount! + if (post.cost && ! post.has_flags(POST_COST_CALCULATED)) + return false; + + return true; + } + void print_note(std::ostream& out, const string& note, const bool note_on_next_line, @@ -123,9 +153,7 @@ namespace { } } -#if 0 std::size_t count = xact.posts.size(); -#endif std::size_t index = 0; foreach (post_t * post, xact.posts) { @@ -178,16 +206,18 @@ namespace { string amt; if (post->amount_expr) { amt = post->amount_expr->text(); - } else -#if 0 - // jww (2012-02-27): Disabled for now because it's not - // outputted valid transactions in every case - if (! (count == 2 && index == 2 && - (*xact.posts.begin())->amount.commodity() == - post->amount.commodity() && - ! (*xact.posts.begin())->cost && ! post->cost)) -#endif - { + } + else if (count == 2 && index == 2 && + post_has_simple_amount(*post) && + post_has_simple_amount(*(*xact.posts.begin())) && + ((*xact.posts.begin())->amount.commodity() == + post->amount.commodity())) { + // If there are two postings and they both simple amount, and + // they are both of the same commodity, don't bother printing + // the second amount as it's always just an inverse of the + // first. + } + else { int amount_width = (report.HANDLER(amount_width_).specified ? report.HANDLER(amount_width_).value.to_int() : 12); diff --git a/test/baseline/opt-equity.test b/test/baseline/opt-equity.test index d8695759..90f4743a 100644 --- a/test/baseline/opt-equity.test +++ b/test/baseline/opt-equity.test @@ -5,5 +5,5 @@ test equity assets 2007/02/02 Opening Balances Assets:Investments:Vanguard:VMMXX 0.350 VMMXX - Equity:Opening Balances -0.350 VMMXX + Equity:Opening Balances end test -- cgit v1.2.3 From 6d04f605738fe47477fdc95342008dfe7d091c1a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 00:08:21 -0600 Subject: Fixed display problem in an error message --- src/textual.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 82065f3c..f6cbf070 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -853,12 +853,12 @@ void instance_t::end_apply_directive(char * kind) if (context.apply_stack.size() <= 1) throw_(std::runtime_error, - _("'end apply%1' found, but no enclosing 'apply%2' directive") + _("'end apply %1' found, but no enclosing 'apply %2' directive") << name << name); if (name != " " && name != context.apply_stack.front().label) throw_(std::runtime_error, - _("'end apply%1' directive does not match 'apply %2' directive") + _("'end apply %1' directive does not match 'apply %2' directive") << name << context.apply_stack.front().label); context.apply_stack.pop_front(); -- cgit v1.2.3 From fc62402c60cd3faac37f3cd60ee417d119869929 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 00:08:39 -0600 Subject: Fixed nasty problem related to interval reporting --- src/filters.cc | 4 ++-- src/filters.h | 4 ++-- src/times.h | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 331073eb..f57f37e7 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -924,7 +924,7 @@ void interval_posts::operator()(post_t& post) report_subtotal(last_interval); if (generate_empty_posts) { - for (++last_interval; interval != last_interval; ++last_interval) { + for (++last_interval; last_interval < interval; ++last_interval) { // Generate a null posting, so the intervening periods can be // seen when -E is used, or if the calculated amount ends up being // non-zero @@ -940,7 +940,7 @@ void interval_posts::operator()(post_t& post) report_subtotal(last_interval); } - assert(interval == last_interval); + assert(last_interval <= interval); } else { last_interval = interval; } diff --git a/src/filters.h b/src/filters.h index e7a2eefa..d207d842 100644 --- a/src/filters.h +++ b/src/filters.h @@ -732,8 +732,8 @@ public: virtual void flush() { if (last_post && interval.duration) { - if (interval.is_valid()) - report_subtotal(interval); + if (last_interval && interval != last_interval) + report_subtotal(last_interval); subtotal_posts::flush(); } } diff --git a/src/times.h b/src/times.h index 39945824..a2680ae3 100644 --- a/src/times.h +++ b/src/times.h @@ -553,6 +553,10 @@ public: return (start == other.start && (! start || *start == *other.start)); } + bool operator<(const date_interval_t& other) const { + return (start == other.start && + (! start || *start < *other.start)); + } operator bool() const { return is_valid(); -- cgit v1.2.3 From d3d13329d98b2b26378813a91ad0d4e49045a426 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 01:37:19 -0600 Subject: Made the --debug option's argument a regex --- src/utils.cc | 7 ++++++- src/utils.h | 26 ++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/utils.cc b/src/utils.cc index 2f64bb0a..5260fd42 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -603,7 +603,12 @@ void logger_func(log_level_t level) namespace ledger { -optional _log_category; +optional _log_category; +#if defined(HAVE_BOOST_REGEX_UNICODE) +optional _log_category_re; +#else +optional _log_category_re; +#endif struct __maybe_enable_debugging { __maybe_enable_debugging() { diff --git a/src/utils.h b/src/utils.h index e1a03d79..c7aaac52 100644 --- a/src/utils.h +++ b/src/utils.h @@ -338,10 +338,32 @@ extern uint8_t _trace_level; #if defined(DEBUG_ON) -extern optional _log_category; +extern optional _log_category; +#if defined(HAVE_BOOST_REGEX_UNICODE) + extern optional _log_category_re; +#else + extern optional _log_category_re; +#endif inline bool category_matches(const char * cat) { - return _log_category && starts_with(cat, *_log_category); + if (_log_category) { + if (! _log_category_re) { + _log_category_re = +#if defined(HAVE_BOOST_REGEX_UNICODE) + boost::make_u32regex(_log_category->c_str(), + boost::regex::perl | boost::regex::icase); +#else + boost::make_regex(_log_category->c_str(), + boost::regex::perl | boost::regex::icase); +#endif + } +#if defined(HAVE_BOOST_REGEX_UNICODE) + return boost::u32regex_search(cat, *_log_category_re); +#else + return boost::regex_search(cat, *_log_category_re); +#endif + } + return false; } #define SHOW_DEBUG(cat) \ -- cgit v1.2.3 From 887f429ae40934c145e03b03cc452e6af4457c0f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 01:37:29 -0600 Subject: Added debug code for debugging interval reports --- src/filters.cc | 22 ++++++++++++++++++---- src/filters.h | 31 +++++++++++++++++++++++++++---- src/times.cc | 11 +++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index f57f37e7..fbef1cd8 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -916,11 +916,24 @@ void interval_posts::report_subtotal(const date_interval_t& ival) void interval_posts::operator()(post_t& post) { - if (! interval.find_period(post.date())) + DEBUG("filters.interval", "Considering post with amount " << post.amount); +#if defined(DEBUG_ON) + DEBUG("filters.interval", "interval is:"); + debug_interval(interval); +#endif + if (! interval.find_period(post.date())) { + DEBUG("filters.interval", "Post does not fall within period"); return; + } if (interval.duration) { - if (last_interval && interval != last_interval) { + DEBUG("filters.interval", "There is an interval duration"); + if (interval != last_interval) { +#if defined(DEBUG_ON) + DEBUG("filters.interval", "interval != last_interval, so reporting"); + DEBUG("filters.interval", "last_interval is:"); + debug_interval(last_interval); +#endif report_subtotal(last_interval); if (generate_empty_posts) { @@ -942,13 +955,14 @@ void interval_posts::operator()(post_t& post) } assert(last_interval <= interval); } else { + DEBUG("filters.interval", "Setting last_interval = interval"); last_interval = interval; } - } else { - last_interval = interval; } + DEBUG("filters.interval", "Calling subtotal_posts::operator()"); subtotal_posts::operator()(post); } else { + DEBUG("filters.interval", "There is no interval duration"); item_handler::operator()(post); } diff --git a/src/filters.h b/src/filters.h index d207d842..2e51c91c 100644 --- a/src/filters.h +++ b/src/filters.h @@ -728,21 +728,44 @@ public: empty_account = &temps.create_account(_("")); } - void report_subtotal(const date_interval_t& interval); + void report_subtotal(const date_interval_t& ival); + +#if defined(DEBUG_ON) + void debug_interval(const date_interval_t& ival) { + if (ival.start) + DEBUG("filters.interval", "start = " << *ival.start); + else + DEBUG("filters.interval", "no start"); + + if (ival.finish) + DEBUG("filters.interval", "finish = " << *ival.finish); + else + DEBUG("filters.interval", "no finish"); + } +#endif virtual void flush() { if (last_post && interval.duration) { - if (last_interval && interval != last_interval) + DEBUG("filters.interval", "There is a last_post and an interval.duration"); + if (interval != last_interval) { +#if defined(DEBUG_ON) + DEBUG("filters.interval", "interval != last_interval, so reporting"); + DEBUG("filters.interval", "interval is:"); + debug_interval(interval); + DEBUG("filters.interval", "last_interval is:"); + debug_interval(last_interval); +#endif report_subtotal(last_interval); + } subtotal_posts::flush(); } } virtual void operator()(post_t& post); virtual void clear() { - interval = start_interval; + interval = start_interval; last_interval = date_interval_t(); - last_post = NULL; + last_post = NULL; subtotal_posts::clear(); create_accounts(); diff --git a/src/times.cc b/src/times.cc index 0384edf6..dd10a508 100644 --- a/src/times.cc +++ b/src/times.cc @@ -1397,8 +1397,17 @@ bool date_interval_t::find_period(const date_t& date) DEBUG("times.interval", "date = " << date); DEBUG("times.interval", "scan = " << scan); DEBUG("times.interval", "end_of_scan = " << end_of_scan); +#if defined(DEBUG_ON) + if (finish) + DEBUG("times.interval", "finish = " << *finish); + else + DEBUG("times.interval", "finish is not set"); +#endif while (date >= scan && (! finish || scan < *finish)) { + DEBUG("times.interval", "date = " << date); + DEBUG("times.interval", "end_of_scan = " << end_of_scan); + if (date < end_of_scan) { start = scan; end_of_duration = end_of_scan; @@ -1416,6 +1425,8 @@ bool date_interval_t::find_period(const date_t& date) end_of_scan = duration->add(scan); } + DEBUG("times.interval", "false: failed scan"); + return false; } -- cgit v1.2.3 From 6adfcc8469e3d526f4bcb0971b49efb490ad6401 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 02:34:37 -0600 Subject: Rewrite the way interval reports are generated --- src/chain.cc | 4 +- src/filters.cc | 129 ++++++---- src/filters.h | 38 +-- src/times.cc | 14 +- src/times.h | 12 +- test/baseline/opt-period.test | 2 +- test/regress/F06D5554.test | 552 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 663 insertions(+), 88 deletions(-) create mode 100644 test/regress/F06D5554.test (limited to 'src') diff --git a/src/chain.cc b/src/chain.cc index 450e3758..61388840 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -217,13 +217,11 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, // interval_posts groups posts together based on a time period, such as // weekly or monthly. - if (report.HANDLED(period_)) { + if (report.HANDLED(period_)) handler.reset(new interval_posts(handler, expr, report.HANDLER(period_).str(), report.HANDLED(exact), report.HANDLED(empty))); - handler.reset(new sort_posts(handler, "date")); - } if (report.HANDLED(date_)) handler.reset(new transfer_details(handler, transfer_details::SET_DATE, diff --git a/src/filters.cc b/src/filters.cc index fbef1cd8..fa1f6fa2 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -904,69 +904,104 @@ void subtotal_posts::operator()(post_t& post) void interval_posts::report_subtotal(const date_interval_t& ival) { - if (last_post && ival) { - if (exact_periods) - subtotal_posts::report_subtotal(); - else - subtotal_posts::report_subtotal(NULL, ival); - } + if (exact_periods) + subtotal_posts::report_subtotal(); + else + subtotal_posts::report_subtotal(NULL, ival); +} - last_post = NULL; +namespace { + struct sort_posts_by_date { + bool operator()(post_t * left, post_t * right) const { + return left->date() < right->date(); + } + }; } void interval_posts::operator()(post_t& post) { - DEBUG("filters.interval", "Considering post with amount " << post.amount); -#if defined(DEBUG_ON) - DEBUG("filters.interval", "interval is:"); - debug_interval(interval); -#endif - if (! interval.find_period(post.date())) { - DEBUG("filters.interval", "Post does not fall within period"); + // If there is a duration (such as weekly), we must generate the + // report in two passes. Otherwise, we only have to check whether the + // post falls within the reporting period. + + if (interval.duration) { + all_posts.push_back(&post); + } + else if (interval.find_period(post.date())) + item_handler::operator()(post); +} + +void interval_posts::flush() +{ + if (! interval.duration) { + item_handler::flush(); return; } - if (interval.duration) { - DEBUG("filters.interval", "There is an interval duration"); - if (interval != last_interval) { -#if defined(DEBUG_ON) - DEBUG("filters.interval", "interval != last_interval, so reporting"); - DEBUG("filters.interval", "last_interval is:"); - debug_interval(last_interval); -#endif - report_subtotal(last_interval); + // Sort all the postings we saw by date ascending + std::stable_sort(all_posts.begin(), all_posts.end(), + sort_posts_by_date()); - if (generate_empty_posts) { - for (++last_interval; last_interval < interval; ++last_interval) { - // Generate a null posting, so the intervening periods can be - // seen when -E is used, or if the calculated amount ends up being - // non-zero - xact_t& null_xact = temps.create_xact(); - null_xact._date = last_interval.inclusive_end(); + // Determine the beginning interval by using the earliest post + if (! interval.find_period(all_posts.front()->date())) + throw_(std::logic_error, _("Failed to find period for interval report")); - post_t& null_post = temps.create_post(null_xact, empty_account); - null_post.add_flags(POST_CALCULATED); - null_post.amount = 0L; + // Walk the interval forward reporting all posts within each one + // before moving on, until we reach the end of all_posts + bool saw_posts = false; + for (std::deque::iterator i = all_posts.begin(); + i != all_posts.end(); ) { + post_t * post(*i); - last_post = &null_post; - subtotal_posts::operator()(null_post); + DEBUG("filters.interval", + "Considering post " << post->date() << " = " << post->amount); +#if defined(DEBUG_ON) + DEBUG("filters.interval", "interval is:"); + debug_interval(interval); +#endif + assert(! interval.finish || post->date() < *interval.finish); - report_subtotal(last_interval); - } - assert(last_interval <= interval); - } else { - DEBUG("filters.interval", "Setting last_interval = interval"); - last_interval = interval; + if (interval.within_period(post->date())) { + DEBUG("filters.interval", "Calling subtotal_posts::operator()"); + subtotal_posts::operator()(*post); + ++i; + saw_posts = true; + } else { + if (saw_posts) { + DEBUG("filters.interval", + "Calling subtotal_posts::report_subtotal()"); + report_subtotal(interval); + saw_posts = false; } + else if (generate_empty_posts) { + // Generate a null posting, so the intervening periods can be + // seen when -E is used, or if the calculated amount ends up + // being non-zero + xact_t& null_xact = temps.create_xact(); + null_xact._date = interval.inclusive_end(); + + post_t& null_post = temps.create_post(null_xact, empty_account); + null_post.add_flags(POST_CALCULATED); + null_post.amount = 0L; + + subtotal_posts::operator()(null_post); + report_subtotal(interval); + } + + DEBUG("filters.interval", "Advancing interval"); + ++interval; } - DEBUG("filters.interval", "Calling subtotal_posts::operator()"); - subtotal_posts::operator()(post); - } else { - DEBUG("filters.interval", "There is no interval duration"); - item_handler::operator()(post); } - last_post = &post; + // If the last postings weren't reported, do so now. + if (saw_posts) { + DEBUG("filters.interval", + "Calling subtotal_posts::report_subtotal() at end"); + report_subtotal(interval); + } + + // Tell our parent class to flush + subtotal_posts::flush(); } void posts_as_equity::report_subtotal() diff --git a/src/filters.h b/src/filters.h index 2e51c91c..c972de82 100644 --- a/src/filters.h +++ b/src/filters.h @@ -655,11 +655,11 @@ protected: typedef std::pair values_pair; protected: - expr_t& amount_expr; - values_map values; - optional date_format; - temporaries_t temps; - std::list component_posts; + expr_t& amount_expr; + values_map values; + optional date_format; + temporaries_t temps; + std::deque component_posts; public: subtotal_posts(post_handler_ptr handler, expr_t& _amount_expr, @@ -697,12 +697,12 @@ class interval_posts : public subtotal_posts { date_interval_t start_interval; date_interval_t interval; - date_interval_t last_interval; - post_t * last_post; account_t * empty_account; bool exact_periods; bool generate_empty_posts; + std::deque all_posts; + interval_posts(); public: @@ -713,8 +713,7 @@ public: bool _exact_periods = false, bool _generate_empty_posts = false) : subtotal_posts(_handler, amount_expr), start_interval(_interval), - interval(start_interval), last_post(NULL), - exact_periods(_exact_periods), + interval(start_interval), exact_periods(_exact_periods), generate_empty_posts(_generate_empty_posts) { TRACE_CTOR(interval_posts, "post_handler_ptr, expr_t&, date_interval_t, bool, bool"); @@ -744,28 +743,11 @@ public: } #endif - virtual void flush() { - if (last_post && interval.duration) { - DEBUG("filters.interval", "There is a last_post and an interval.duration"); - if (interval != last_interval) { -#if defined(DEBUG_ON) - DEBUG("filters.interval", "interval != last_interval, so reporting"); - DEBUG("filters.interval", "interval is:"); - debug_interval(interval); - DEBUG("filters.interval", "last_interval is:"); - debug_interval(last_interval); -#endif - report_subtotal(last_interval); - } - subtotal_posts::flush(); - } - } virtual void operator()(post_t& post); + virtual void flush(); virtual void clear() { - interval = start_interval; - last_interval = date_interval_t(); - last_post = NULL; + interval = start_interval; subtotal_posts::clear(); create_accounts(); diff --git a/src/times.cc b/src/times.cc index dd10a508..9712c2ee 100644 --- a/src/times.cc +++ b/src/times.cc @@ -1305,7 +1305,7 @@ void date_interval_t::stabilize(const optional& date) date_interval_t next_interval(*this); ++next_interval; - if (next_interval.start && *next_interval.start < *date) { + if (next_interval.start && *next_interval.start <= *date) { *this = next_interval; } else { end_of_duration = none; @@ -1355,7 +1355,8 @@ void date_interval_t::stabilize(const optional& date) } } -bool date_interval_t::find_period(const date_t& date) +bool date_interval_t::find_period(const date_t& date, + const bool allow_shift) { stabilize(date); @@ -1405,9 +1406,6 @@ bool date_interval_t::find_period(const date_t& date) #endif while (date >= scan && (! finish || scan < *finish)) { - DEBUG("times.interval", "date = " << date); - DEBUG("times.interval", "end_of_scan = " << end_of_scan); - if (date < end_of_scan) { start = scan; end_of_duration = end_of_scan; @@ -1420,9 +1418,15 @@ bool date_interval_t::find_period(const date_t& date) return true; } + else if (! allow_shift) { + break; + } scan = duration->add(scan); end_of_scan = duration->add(scan); + + DEBUG("times.interval", "scan = " << scan); + DEBUG("times.interval", "end_of_scan = " << end_of_scan); } DEBUG("times.interval", "false: failed scan"); diff --git a/src/times.h b/src/times.h index a2680ae3..bc462efa 100644 --- a/src/times.h +++ b/src/times.h @@ -578,10 +578,14 @@ public: return start; } - /** Find the current or next period containing date. Returns true if the - date_interval_t object has been altered to reflect the interval - containing date, or false if no such period can be found. */ - bool find_period(const date_t& date = CURRENT_DATE()); + /** Find the current or next period containing date. Returns false if + no such period can be found. If allow_shift is true, the default, + then the interval may be shifted in time to find the period. */ + bool find_period(const date_t& date = CURRENT_DATE(), + const bool allow_shift = true); + bool within_period(const date_t& date = CURRENT_DATE()) { + return find_period(date, false); + } optional inclusive_end() const { if (end_of_duration) diff --git a/test/baseline/opt-period.test b/test/baseline/opt-period.test index 7268bcce..f370b404 100644 --- a/test/baseline/opt-period.test +++ b/test/baseline/opt-period.test @@ -257,7 +257,7 @@ test reg -p "weekly january 2008" 08-Jan-01 - 08-Jan-05 Assets:Cash $-20.00 $-20.00 Expenses:Books $10.00 $-10.00 Liabilities:Cards $10.00 0 -08-Jan-29 - 08-Jan-31 Assets:Cash $-20.00 $-20.00 +08-Jan-27 - 08-Jan-31 Assets:Cash $-20.00 $-20.00 Expenses:Books $10.00 $-10.00 Liabilities:Cards $10.00 0 end test diff --git a/test/regress/F06D5554.test b/test/regress/F06D5554.test new file mode 100644 index 00000000..4541b791 --- /dev/null +++ b/test/regress/F06D5554.test @@ -0,0 +1,552 @@ +2011/04/01 serveraxis.com + Expenses:Computer:Internet $15.00 + Expenses:Computer:Internet $1.10 + Liabilities:MasterCard + +2011/04/05 Pennsylvania toll booth + Expenses:Auto:Fees $13.00 + Expenses:Cash + +2011/04/05 iTunes + Expenses:Music $1.29 + Expenses:Taxes:Sales $0.09 + Liabilities:MasterCard $-1.38 + +2011/04/19 iTunes + Expenses:Computer:Software $4.99 + Expenses:Taxes:Sales $0.35 + Liabilities:MasterCard $-5.34 + +2011/04/24 iTunes + Expenses:Movies $1.99 + Expenses:Movies $2.99 + Expenses:Taxes:Sales $0.35 + Liabilities:MasterCard $-5.33 + +2011/04/29 iTunes + Expenses:Computer:Movies $0.99 + Expenses:Taxes:Sales $0.07 + Liabilities:MasterCard $-1.06 + +2011/05/01 serveraxis.com + Expenses:Computer:Internet $15.00 + Expenses:Computer:Internet $1.10 + Liabilities:MasterCard + +2011/05/18 iTunes + Expenses:Computer:Software $6.99 + Expenses:Taxes:Sales $0.49 + Liabilities:MasterCard $-7.48 + +2011/05/20 DynDNS.com + Expenses:Computer:Internet $15.00 + Liabilities:MasterCard + +2011/05/20 DynDNS.com + Expenses:Computer:Internet $15.00 + Liabilities:MasterCard + +2011/05/27 iTunes + Expenses:Movies $1.99 + Expenses:Movies $1.99 + Expenses:Movies $1.99 + Expenses:Taxes:Sales $0.42 + Liabilities:MasterCard $-6.39 + +2011/05/26 Valero + Expenses:Auto:Gas $26.79 + Liabilities:MasterCard + +2011/05/26 Starbucks + Expenses:Food $2.20 + Expenses:Taxes:Sales $0.15 + Liabilities:MasterCard $-2.35 + +2011/05/26 La Mex + Expenses:Food $17.70 + Expenses:Taxes:Sales $1.11 + Expenses:Tips $3.00 + Liabilities:MasterCard $-21.81 + +2011/05/27 Leaves N Beans + Expenses:Food:Dining $20.98 + Expenses:Taxes:Sales $1.63 + Expenses:Tips $2.00 + Liabilities:MasterCard $-24.61 + +2011/05/27 Wal*Mart + Expenses:Home:Supplies $7.97 + Expenses:Food:Grocery $3.25 + Expenses:Food:Grocery $3.18 + Expenses:Food:Grocery $3.18 + Expenses:Food:Grocery $2.98 + Expenses:Food:Grocery $1.98 + Expenses:Food:Grocery $3.98 + Expenses:Food:Grocery $3.58 + Expenses:Food:Grocery $3.58 + Expenses:Food:Grocery $1.58 + Expenses:Food:Grocery $1.88 + Expenses:Food:Grocery $2.50 + Expenses:Food:Grocery $1.26 + Expenses:Food:Grocery $2.62 + Expenses:Food:Grocery $3.48 + Expenses:Home:Supplies $1.37 + Expenses:Home:Supplies $2.92 + Expenses:Beauty $3.38 + Expenses:Beauty $0.97 + Expenses:Beauty $4.64 + Expenses:Beauty $1.97 + Expenses:Beauty $1.97 + Expenses:Beauty $5.98 + Expenses:Home:Supplies $9.98 + Expenses:Bedding $4.00 + Expenses:Bedding $4.00 + Expenses:Home:Supplies $2.88 + Expenses:Home:Supplies $2.88 + Expenses:Home:Supplies $2.88 + Expenses:Home:Supplies $2.88 + Expenses:Clothing $2.96 + Expenses:Supplies $0.84 + Expenses:Food:Grocery $1.38 + Expenses:Food:Grocery $1.38 + Expenses:Food:Grocery $2.32 + Expenses:Food:Grocery $2.00 + Expenses:Food:Grocery $2.98 + Expenses:Food:Grocery $3.00 + Expenses:Food:Grocery $2.14 + Expenses:Food:Grocery $2.14 + Expenses:Food:Grocery $2.50 + Expenses:Food:Grocery $2.50 + Expenses:Food:Grocery $3.48 + Expenses:Home:Supplies $1.17 + Expenses:Supplies $3.00 + Expenses:Bedding $34.88 + Expenses:Home $6.00 + Expenses:Home $6.00 + Expenses:Home:Supplies $3.97 + Expenses:Food:Grocery $0.78 + Expenses:Food:Grocery $0.78 + Expenses:Food:Grocery $0.78 + Expenses:Food:Grocery $0.78 + Expenses:Home $4.00 + Expenses:Home $4.00 + Expenses:Home $10.87 + Expenses:Home $4.00 + Expenses:Bedding $65.96 + Expenses:Taxes:Sales $16.89 + Expenses:Taxes:Sales $0.65 + Liabilities:MasterCard $-293.83 + +2011/05/27 Asia Grill + Expenses:Food:Dining $28.63 + Expenses:Tips $4.00 + Liabilities:MasterCard $-32.63 + +2011/05/28 Shell + Expenses:Auto:Gas $43.41 + Liabilities:MasterCard + +2011/05/28 Sears + Expenses:Home $1,728.96 + Expenses:Taxes:Sales $136.87 + Liabilities:MasterCard $-1,865.83 + +2011/05/28 Sears + Expenses:Home $99.61 + Expenses:Taxes:Sales $8.22 + Liabilities:MasterCard $-107.83 + +2011/05/28 Buffalo Wild Wings + Expenses:Food:Dining $22.98 + Expenses:Tips $2.35 + Expenses:Taxes:Sales $3.50 + Liabilities:MasterCard $-28.83 + +2011/05/28 Cold Stone Creamery + Expenses:Food:Dining $5.73 + Expenses:Tips $0.50 + Liabilities:MasterCard $-6.23 + +2011/05/29 Hy Vee + Expenses:Supplies $2.00 + Expenses:Supplies $7.99 + Expenses:Supplies $7.99 + Expenses:Food:Grocery $157.64 + Expenses:Taxes:Sales $5.74 + Liabilities:MasterCard $-181.36 + +2011/05/30 Allied movers, Fidel & Manny + Expenses:Tips $97.00 + Expenses:Cash + +2011/05/30 Starbucks + Expenses:Food:Dining $6.90 + Expenses:Taxes:Sales $0.71 + Liabilities:MasterCard $-7.61 + +2011/05/31 Wal*Mart + Expenses:Home $108.13 + Expenses:Taxes:Sales $8.65 + Liabilities:MasterCard $-116.78 + +test reg -p "apr 2011" Expenses +11-Apr-01 serveraxis.com Expe:Computer:Internet $15.00 $15.00 + Expe:Computer:Internet $1.10 $16.10 +11-Apr-05 Pennsylvania toll b.. Expenses:Auto:Fees $13.00 $29.10 + Expenses:Cash $-13.00 $16.10 +11-Apr-05 iTunes Expenses:Music $1.29 $17.39 + Expenses:Taxes:Sales $0.09 $17.48 +11-Apr-19 iTunes Expe:Computer:Software $4.99 $22.47 + Expenses:Taxes:Sales $0.35 $22.82 +11-Apr-24 iTunes Expenses:Movies $1.99 $24.81 + Expenses:Movies $2.99 $27.80 + Expenses:Taxes:Sales $0.35 $28.15 +11-Apr-29 iTunes Expens:Computer:Movies $0.99 $29.14 + Expenses:Taxes:Sales $0.07 $29.21 +end test + +test reg -p "apr 2011" Expenses --monthly +11-Apr-01 - 11-Apr-30 Expenses:Auto:Fees $13.00 $13.00 + Expenses:Cash $-13.00 0 + Expe:Computer:Internet $16.10 $16.10 + Expens:Computer:Movies $0.99 $17.09 + Expe:Computer:Software $4.99 $22.08 + Expenses:Movies $4.98 $27.06 + Expenses:Music $1.29 $28.35 + Expenses:Taxes:Sales $0.86 $29.21 +end test + +test reg -p "apr 2011" Expenses --monthly --exact +11-Apr-01 - 11-Apr-29 Expenses:Auto:Fees $13.00 $13.00 + Expenses:Cash $-13.00 0 + Expe:Computer:Internet $16.10 $16.10 + Expens:Computer:Movies $0.99 $17.09 + Expe:Computer:Software $4.99 $22.08 + Expenses:Movies $4.98 $27.06 + Expenses:Music $1.29 $28.35 + Expenses:Taxes:Sales $0.86 $29.21 +end test + +test reg -p "apr 2011" Expenses --weekly +11-Apr-01 - 11-Apr-02 Expe:Computer:Internet $16.10 $16.10 +11-Apr-03 - 11-Apr-09 Expenses:Auto:Fees $13.00 $29.10 + Expenses:Cash $-13.00 $16.10 + Expenses:Music $1.29 $17.39 + Expenses:Taxes:Sales $0.09 $17.48 +11-Apr-17 - 11-Apr-23 Expe:Computer:Software $4.99 $22.47 + Expenses:Taxes:Sales $0.35 $22.82 +11-Apr-24 - 11-Apr-30 Expens:Computer:Movies $0.99 $23.81 + Expenses:Movies $4.98 $28.79 + Expenses:Taxes:Sales $0.42 $29.21 +end test + +test reg -p "apr 2011" Expenses --weekly --exact +11-Apr-01 - 11-Apr-01 Expe:Computer:Internet $16.10 $16.10 +11-Apr-05 - 11-Apr-05 Expenses:Auto:Fees $13.00 $29.10 + Expenses:Cash $-13.00 $16.10 + Expenses:Music $1.29 $17.39 + Expenses:Taxes:Sales $0.09 $17.48 +11-Apr-19 - 11-Apr-19 Expe:Computer:Software $4.99 $22.47 + Expenses:Taxes:Sales $0.35 $22.82 +11-Apr-24 - 11-Apr-29 Expens:Computer:Movies $0.99 $23.81 + Expenses:Movies $4.98 $28.79 + Expenses:Taxes:Sales $0.42 $29.21 +end test + +test reg -p "apr 2011" Expenses --weekly --empty +11-Apr-01 - 11-Apr-02 Expe:Computer:Internet $16.10 $16.10 +11-Apr-03 - 11-Apr-09 Expenses:Auto:Fees $13.00 $29.10 + Expenses:Cash $-13.00 $16.10 + Expenses:Music $1.29 $17.39 + Expenses:Taxes:Sales $0.09 $17.48 +11-Apr-10 - 11-Apr-16 0 $17.48 +11-Apr-17 - 11-Apr-23 Expe:Computer:Software $4.99 $22.47 + Expenses:Taxes:Sales $0.35 $22.82 +11-Apr-24 - 11-Apr-30 Expens:Computer:Movies $0.99 $23.81 + Expenses:Movies $4.98 $28.79 + Expenses:Taxes:Sales $0.42 $29.21 +end test + +test reg -p "apr 2011" Expenses --weekly --empty --exact +11-Apr-01 - 11-Apr-01 Expe:Computer:Internet $16.10 $16.10 +11-Apr-05 - 11-Apr-05 Expenses:Auto:Fees $13.00 $29.10 + Expenses:Cash $-13.00 $16.10 + Expenses:Music $1.29 $17.39 + Expenses:Taxes:Sales $0.09 $17.48 +11-Apr-16 - 11-Apr-16 0 $17.48 +11-Apr-19 - 11-Apr-19 Expe:Computer:Software $4.99 $22.47 + Expenses:Taxes:Sales $0.35 $22.82 +11-Apr-24 - 11-Apr-29 Expens:Computer:Movies $0.99 $23.81 + Expenses:Movies $4.98 $28.79 + Expenses:Taxes:Sales $0.42 $29.21 +end test + +test reg -p "may 2011" +11-May-01 serveraxis.com Expe:Computer:Internet $15.00 $15.00 + Expe:Computer:Internet $1.10 $16.10 + Liabilities:MasterCard $-16.10 0 +11-May-18 iTunes Expe:Computer:Software $6.99 $6.99 + Expenses:Taxes:Sales $0.49 $7.48 + Liabilities:MasterCard $-7.48 0 +11-May-20 DynDNS.com Expe:Computer:Internet $15.00 $15.00 + Liabilities:MasterCard $-15.00 0 +11-May-20 DynDNS.com Expe:Computer:Internet $15.00 $15.00 + Liabilities:MasterCard $-15.00 0 +11-May-27 iTunes Expenses:Movies $1.99 $1.99 + Expenses:Movies $1.99 $3.98 + Expenses:Movies $1.99 $5.97 + Expenses:Taxes:Sales $0.42 $6.39 + Liabilities:MasterCard $-6.39 0 +11-May-26 Valero Expenses:Auto:Gas $26.79 $26.79 + Liabilities:MasterCard $-26.79 0 +11-May-26 Starbucks Expenses:Food $2.20 $2.20 + Expenses:Taxes:Sales $0.15 $2.35 + Liabilities:MasterCard $-2.35 0 +11-May-26 La Mex Expenses:Food $17.70 $17.70 + Expenses:Taxes:Sales $1.11 $18.81 + Expenses:Tips $3.00 $21.81 + Liabilities:MasterCard $-21.81 0 +11-May-27 Leaves N Beans Expenses:Food:Dining $20.98 $20.98 + Expenses:Taxes:Sales $1.63 $22.61 + Expenses:Tips $2.00 $24.61 + Liabilities:MasterCard $-24.61 0 +11-May-27 Wal*Mart Expenses:Home:Supplies $7.97 $7.97 + Expenses:Food:Grocery $3.25 $11.22 + Expenses:Food:Grocery $3.18 $14.40 + Expenses:Food:Grocery $3.18 $17.58 + Expenses:Food:Grocery $2.98 $20.56 + Expenses:Food:Grocery $1.98 $22.54 + Expenses:Food:Grocery $3.98 $26.52 + Expenses:Food:Grocery $3.58 $30.10 + Expenses:Food:Grocery $3.58 $33.68 + Expenses:Food:Grocery $1.58 $35.26 + Expenses:Food:Grocery $1.88 $37.14 + Expenses:Food:Grocery $2.50 $39.64 + Expenses:Food:Grocery $1.26 $40.90 + Expenses:Food:Grocery $2.62 $43.52 + Expenses:Food:Grocery $3.48 $47.00 + Expenses:Home:Supplies $1.37 $48.37 + Expenses:Home:Supplies $2.92 $51.29 + Expenses:Beauty $3.38 $54.67 + Expenses:Beauty $0.97 $55.64 + Expenses:Beauty $4.64 $60.28 + Expenses:Beauty $1.97 $62.25 + Expenses:Beauty $1.97 $64.22 + Expenses:Beauty $5.98 $70.20 + Expenses:Home:Supplies $9.98 $80.18 + Expenses:Bedding $4.00 $84.18 + Expenses:Bedding $4.00 $88.18 + Expenses:Home:Supplies $2.88 $91.06 + Expenses:Home:Supplies $2.88 $93.94 + Expenses:Home:Supplies $2.88 $96.82 + Expenses:Home:Supplies $2.88 $99.70 + Expenses:Clothing $2.96 $102.66 + Expenses:Supplies $0.84 $103.50 + Expenses:Food:Grocery $1.38 $104.88 + Expenses:Food:Grocery $1.38 $106.26 + Expenses:Food:Grocery $2.32 $108.58 + Expenses:Food:Grocery $2.00 $110.58 + Expenses:Food:Grocery $2.98 $113.56 + Expenses:Food:Grocery $3.00 $116.56 + Expenses:Food:Grocery $2.14 $118.70 + Expenses:Food:Grocery $2.14 $120.84 + Expenses:Food:Grocery $2.50 $123.34 + Expenses:Food:Grocery $2.50 $125.84 + Expenses:Food:Grocery $3.48 $129.32 + Expenses:Home:Supplies $1.17 $130.49 + Expenses:Supplies $3.00 $133.49 + Expenses:Bedding $34.88 $168.37 + Expenses:Home $6.00 $174.37 + Expenses:Home $6.00 $180.37 + Expenses:Home:Supplies $3.97 $184.34 + Expenses:Food:Grocery $0.78 $185.12 + Expenses:Food:Grocery $0.78 $185.90 + Expenses:Food:Grocery $0.78 $186.68 + Expenses:Food:Grocery $0.78 $187.46 + Expenses:Home $4.00 $191.46 + Expenses:Home $4.00 $195.46 + Expenses:Home $10.87 $206.33 + Expenses:Home $4.00 $210.33 + Expenses:Bedding $65.96 $276.29 + Expenses:Taxes:Sales $16.89 $293.18 + Expenses:Taxes:Sales $0.65 $293.83 + Liabilities:MasterCard $-293.83 0 +11-May-27 Asia Grill Expenses:Food:Dining $28.63 $28.63 + Expenses:Tips $4.00 $32.63 + Liabilities:MasterCard $-32.63 0 +11-May-28 Shell Expenses:Auto:Gas $43.41 $43.41 + Liabilities:MasterCard $-43.41 0 +11-May-28 Sears Expenses:Home $1,728.96 $1,728.96 + Expenses:Taxes:Sales $136.87 $1,865.83 + Liabilities:MasterCard $-1,865.83 0 +11-May-28 Sears Expenses:Home $99.61 $99.61 + Expenses:Taxes:Sales $8.22 $107.83 + Liabilities:MasterCard $-107.83 0 +11-May-28 Buffalo Wild Wings Expenses:Food:Dining $22.98 $22.98 + Expenses:Tips $2.35 $25.33 + Expenses:Taxes:Sales $3.50 $28.83 + Liabilities:MasterCard $-28.83 0 +11-May-28 Cold Stone Creamery Expenses:Food:Dining $5.73 $5.73 + Expenses:Tips $0.50 $6.23 + Liabilities:MasterCard $-6.23 0 +11-May-29 Hy Vee Expenses:Supplies $2.00 $2.00 + Expenses:Supplies $7.99 $9.99 + Expenses:Supplies $7.99 $17.98 + Expenses:Food:Grocery $157.64 $175.62 + Expenses:Taxes:Sales $5.74 $181.36 + Liabilities:MasterCard $-181.36 0 +11-May-30 Allied movers, Fide.. Expenses:Tips $97.00 $97.00 + Expenses:Cash $-97.00 0 +11-May-30 Starbucks Expenses:Food:Dining $6.90 $6.90 + Expenses:Taxes:Sales $0.71 $7.61 + Liabilities:MasterCard $-7.61 0 +11-May-31 Wal*Mart Expenses:Home $108.13 $108.13 + Expenses:Taxes:Sales $8.65 $116.78 + Liabilities:MasterCard $-116.78 0 +end test + +test reg -p "may 2011" --monthly +11-May-01 - 11-May-31 Expenses:Auto:Gas $70.20 $70.20 + Expenses:Beauty $18.91 $89.11 + Expenses:Bedding $108.84 $197.95 + Expenses:Cash $-97.00 $100.95 + Expenses:Clothing $2.96 $103.91 + Expe:Computer:Internet $46.10 $150.01 + Expe:Computer:Software $6.99 $157.00 + Expenses:Food $19.90 $176.90 + Expenses:Food:Dining $85.22 $262.12 + Expenses:Food:Grocery $225.61 $487.73 + Expenses:Home $1,971.57 $2,459.30 + Expenses:Home:Supplies $38.90 $2,498.20 + Expenses:Movies $5.97 $2,504.17 + Expenses:Supplies $21.82 $2,525.99 + Expenses:Taxes:Sales $185.03 $2,711.02 + Expenses:Tips $108.85 $2,819.87 + Liabilities:MasterCard $-2,819.87 0 +end test + +test reg -p "may 2011" --weekly +11-May-01 - 11-May-07 Expe:Computer:Internet $16.10 $16.10 + Liabilities:MasterCard $-16.10 0 +11-May-15 - 11-May-21 Expe:Computer:Internet $30.00 $30.00 + Expe:Computer:Software $6.99 $36.99 + Expenses:Taxes:Sales $0.49 $37.48 + Liabilities:MasterCard $-37.48 0 +11-May-22 - 11-May-28 Expenses:Auto:Gas $70.20 $70.20 + Expenses:Beauty $18.91 $89.11 + Expenses:Bedding $108.84 $197.95 + Expenses:Clothing $2.96 $200.91 + Expenses:Food $19.90 $220.81 + Expenses:Food:Dining $78.32 $299.13 + Expenses:Food:Grocery $67.97 $367.10 + Expenses:Home $1,863.44 $2,230.54 + Expenses:Home:Supplies $38.90 $2,269.44 + Expenses:Movies $5.97 $2,275.41 + Expenses:Supplies $3.84 $2,279.25 + Expenses:Taxes:Sales $169.44 $2,448.69 + Expenses:Tips $11.85 $2,460.54 + Liabilities:MasterCard $-2,460.54 0 +11-May-29 - 11-May-31 Expenses:Cash $-97.00 $-97.00 + Expenses:Food:Dining $6.90 $-90.10 + Expenses:Food:Grocery $157.64 $67.54 + Expenses:Home $108.13 $175.67 + Expenses:Supplies $17.98 $193.65 + Expenses:Taxes:Sales $15.10 $208.75 + Expenses:Tips $97.00 $305.75 + Liabilities:MasterCard $-305.75 0 +end test + +test reg -p "may 2011" --weekly --exact +11-May-01 - 11-May-01 Expe:Computer:Internet $16.10 $16.10 + Liabilities:MasterCard $-16.10 0 +11-May-18 - 11-May-20 Expe:Computer:Internet $30.00 $30.00 + Expe:Computer:Software $6.99 $36.99 + Expenses:Taxes:Sales $0.49 $37.48 + Liabilities:MasterCard $-37.48 0 +11-May-26 - 11-May-28 Expenses:Auto:Gas $70.20 $70.20 + Expenses:Beauty $18.91 $89.11 + Expenses:Bedding $108.84 $197.95 + Expenses:Clothing $2.96 $200.91 + Expenses:Food $19.90 $220.81 + Expenses:Food:Dining $78.32 $299.13 + Expenses:Food:Grocery $67.97 $367.10 + Expenses:Home $1,863.44 $2,230.54 + Expenses:Home:Supplies $38.90 $2,269.44 + Expenses:Movies $5.97 $2,275.41 + Expenses:Supplies $3.84 $2,279.25 + Expenses:Taxes:Sales $169.44 $2,448.69 + Expenses:Tips $11.85 $2,460.54 + Liabilities:MasterCard $-2,460.54 0 +11-May-29 - 11-May-31 Expenses:Cash $-97.00 $-97.00 + Expenses:Food:Dining $6.90 $-90.10 + Expenses:Food:Grocery $157.64 $67.54 + Expenses:Home $108.13 $175.67 + Expenses:Supplies $17.98 $193.65 + Expenses:Taxes:Sales $15.10 $208.75 + Expenses:Tips $97.00 $305.75 + Liabilities:MasterCard $-305.75 0 +end test + +test reg -p "may 2011" --weekly --empty +11-May-01 - 11-May-07 Expe:Computer:Internet $16.10 $16.10 + Liabilities:MasterCard $-16.10 0 +11-May-08 - 11-May-14 0 0 +11-May-15 - 11-May-21 Expe:Computer:Internet $30.00 $30.00 + Expe:Computer:Software $6.99 $36.99 + Expenses:Taxes:Sales $0.49 $37.48 + Liabilities:MasterCard $-37.48 0 +11-May-22 - 11-May-28 Expenses:Auto:Gas $70.20 $70.20 + Expenses:Beauty $18.91 $89.11 + Expenses:Bedding $108.84 $197.95 + Expenses:Clothing $2.96 $200.91 + Expenses:Food $19.90 $220.81 + Expenses:Food:Dining $78.32 $299.13 + Expenses:Food:Grocery $67.97 $367.10 + Expenses:Home $1,863.44 $2,230.54 + Expenses:Home:Supplies $38.90 $2,269.44 + Expenses:Movies $5.97 $2,275.41 + Expenses:Supplies $3.84 $2,279.25 + Expenses:Taxes:Sales $169.44 $2,448.69 + Expenses:Tips $11.85 $2,460.54 + Liabilities:MasterCard $-2,460.54 0 +11-May-29 - 11-May-31 Expenses:Cash $-97.00 $-97.00 + Expenses:Food:Dining $6.90 $-90.10 + Expenses:Food:Grocery $157.64 $67.54 + Expenses:Home $108.13 $175.67 + Expenses:Supplies $17.98 $193.65 + Expenses:Taxes:Sales $15.10 $208.75 + Expenses:Tips $97.00 $305.75 + Liabilities:MasterCard $-305.75 0 +end test + +test reg -p "may 2011" --weekly --empty --exact +11-May-01 - 11-May-01 Expe:Computer:Internet $16.10 $16.10 + Liabilities:MasterCard $-16.10 0 +11-May-14 - 11-May-14 0 0 +11-May-18 - 11-May-20 Expe:Computer:Internet $30.00 $30.00 + Expe:Computer:Software $6.99 $36.99 + Expenses:Taxes:Sales $0.49 $37.48 + Liabilities:MasterCard $-37.48 0 +11-May-26 - 11-May-28 Expenses:Auto:Gas $70.20 $70.20 + Expenses:Beauty $18.91 $89.11 + Expenses:Bedding $108.84 $197.95 + Expenses:Clothing $2.96 $200.91 + Expenses:Food $19.90 $220.81 + Expenses:Food:Dining $78.32 $299.13 + Expenses:Food:Grocery $67.97 $367.10 + Expenses:Home $1,863.44 $2,230.54 + Expenses:Home:Supplies $38.90 $2,269.44 + Expenses:Movies $5.97 $2,275.41 + Expenses:Supplies $3.84 $2,279.25 + Expenses:Taxes:Sales $169.44 $2,448.69 + Expenses:Tips $11.85 $2,460.54 + Liabilities:MasterCard $-2,460.54 0 +11-May-29 - 11-May-31 Expenses:Cash $-97.00 $-97.00 + Expenses:Food:Dining $6.90 $-90.10 + Expenses:Food:Grocery $157.64 $67.54 + Expenses:Home $108.13 $175.67 + Expenses:Supplies $17.98 $193.65 + Expenses:Taxes:Sales $15.10 $208.75 + Expenses:Tips $97.00 $305.75 + Liabilities:MasterCard $-305.75 0 +end test -- cgit v1.2.3 From 7a55c7ffc1e3565c82c746ea66dbb6af2e3d8509 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 02:47:22 -0600 Subject: Allow xact notes on periodic transactions Fixes #448 --- src/textual.cc | 9 ++++++--- test/regress/CFE5D8AA.test | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 test/regress/CFE5D8AA.test (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index f6cbf070..739b63f0 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1565,9 +1565,12 @@ bool instance_t::parse_posts(account_t * account, std::streamsize len = read_line(line); assert(len > 0); - if (post_t * post = parse_post(line, len, account, NULL, defer_expr)) { - xact.add_post(post); - added = true; + char * p = skip_ws(line); + if (*p != ';') { + if (post_t * post = parse_post(line, len, account, NULL, defer_expr)) { + xact.add_post(post); + added = true; + } } } diff --git a/test/regress/CFE5D8AA.test b/test/regress/CFE5D8AA.test new file mode 100644 index 00000000..857dad13 --- /dev/null +++ b/test/regress/CFE5D8AA.test @@ -0,0 +1,20 @@ +~ monthly + assets:checking $1,000.00 + income:work:salary $-1,000.00 + +~ monthly + ; note + assets:checking $1,000.00 + income:work:salary $-1,000.00 + +~ monthly + assets:checking $1,000.00 + income:work:salary + +~ monthly + ; note + assets:checking $1,000.00 + income:work:salary + +test reg +end test -- cgit v1.2.3 From 656e46e1823ce00285f08f72d8473a58f9bd561a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 03:02:16 -0600 Subject: Renamed actual/effective dates to primary/auxiliary --- doc/ledger.1 | 6 +++--- src/csv.cc | 8 ++++---- src/csv.h | 6 +++--- src/generate.cc | 10 +++++----- src/generate.h | 2 +- src/item.cc | 22 +++++++++++++--------- src/item.h | 20 ++++++++++---------- src/post.cc | 24 ++++++++++++------------ src/post.h | 6 +++--- src/print.cc | 8 ++++---- src/py_item.cc | 10 +++++----- src/py_post.cc | 2 +- src/report.cc | 9 ++++----- src/report.h | 10 +++++----- src/textual.cc | 2 +- src/xact.cc | 6 +++--- test/baseline/opt-actual-dates.test | 0 test/baseline/opt-aux-date.test | 20 ++++++++++++++++++++ test/baseline/opt-effective.test | 20 -------------------- test/baseline/opt-primary-date.test | 0 20 files changed, 97 insertions(+), 94 deletions(-) delete mode 100644 test/baseline/opt-actual-dates.test create mode 100644 test/baseline/opt-aux-date.test delete mode 100644 test/baseline/opt-effective.test create mode 100644 test/baseline/opt-primary-date.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 656a3866..4829034f 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd February 27, 2012 +.Dd February 28, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -263,7 +263,6 @@ transactions they are contained in. See the manual for more information. .It Fl \-account Ar STR .It Fl \-account-width Ar INT .It Fl \-actual Pq Fl L -.It Fl \-actual-dates .It Fl \-add-budget .It Fl \-amount Ar EXPR Pq Fl t .It Fl \-amount-data Pq Fl j @@ -271,6 +270,7 @@ transactions they are contained in. See the manual for more information. .It Fl \-anon .It Fl \-args-only .It Fl \-auto-match +.It Fl \-aux-date .It Fl \-average Pq Fl A .It Fl \-balance-format Ar FMT .It Fl \-base @@ -308,7 +308,6 @@ See .It Fl \-display-total Ar EXPR .It Fl \-dow .It Fl \-download -.It Fl \-effective .It Fl \-empty Pq Fl E .It Fl \-end Pq Fl e .It Fl \-equity @@ -387,6 +386,7 @@ See .Fl \-leeway . .It Fl \-prices-format Ar FMT .It Fl \-pricedb-format Ar FMT +.It Fl \-primary-date .It Fl \-quantity Pq Fl O .It Fl \-quarterly .It Fl \-raw diff --git a/src/csv.cc b/src/csv.cc index c253f246..82c28ff3 100644 --- a/src/csv.cc +++ b/src/csv.cc @@ -109,8 +109,8 @@ void csv_reader::read_index(std::istream& sin) if (date_mask.match(field)) index.push_back(FIELD_DATE); - else if (date_eff_mask.match(field)) - index.push_back(FIELD_DATE_EFF); + else if (date_aux_mask.match(field)) + index.push_back(FIELD_DATE_AUX); else if (code_mask.match(field)) index.push_back(FIELD_CODE); else if (payee_mask.match(field)) @@ -175,8 +175,8 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket, xact->_date = parse_date(field); break; - case FIELD_DATE_EFF: - xact->_date_eff = parse_date(field); + case FIELD_DATE_AUX: + xact->_date_aux = parse_date(field); break; case FIELD_CODE: diff --git a/src/csv.h b/src/csv.h index cf350e9d..909439ff 100644 --- a/src/csv.h +++ b/src/csv.h @@ -62,7 +62,7 @@ class csv_reader enum headers_t { FIELD_DATE = 0, - FIELD_DATE_EFF, + FIELD_DATE_AUX, FIELD_CODE, FIELD_PAYEE, FIELD_AMOUNT, @@ -74,7 +74,7 @@ class csv_reader }; mask_t date_mask; - mask_t date_eff_mask; + mask_t date_aux_mask; mask_t code_mask; mask_t payee_mask; mask_t amount_mask; @@ -90,7 +90,7 @@ public: : in(_in), pathname(_pathname), linenum(0), sequence(0), date_mask("date"), - date_eff_mask("posted( ?date)?"), + date_aux_mask("posted( ?date)?"), code_mask("code"), payee_mask("(payee|desc(ription)?|title)"), amount_mask("amount"), diff --git a/src/generate.cc b/src/generate.cc index 185e23e7..963cd845 100644 --- a/src/generate.cc +++ b/src/generate.cc @@ -69,9 +69,9 @@ generate_posts_iterator::generate_posts_iterator generate_date(next_date_buf); next_date = parse_date(next_date_buf.str()); - std::ostringstream next_eff_date_buf; - generate_date(next_eff_date_buf); - next_eff_date = parse_date(next_eff_date_buf.str()); + std::ostringstream next_aux_date_buf; + generate_date(next_aux_date_buf); + next_aux_date = parse_date(next_aux_date_buf.str()); } @@ -326,8 +326,8 @@ void generate_posts_iterator::generate_xact(std::ostream& out) next_date += gregorian::days(six_gen()); if (truth_gen()) { out << '='; - out << format_date(next_eff_date, FMT_WRITTEN); - next_eff_date += gregorian::days(six_gen()); + out << format_date(next_aux_date, FMT_WRITTEN); + next_aux_date += gregorian::days(six_gen()); } out << ' '; diff --git a/src/generate.h b/src/generate.h index 47abcd94..abf719d4 100644 --- a/src/generate.h +++ b/src/generate.h @@ -57,7 +57,7 @@ class generate_posts_iterator std::size_t quantity; bool allow_invalid; date_t next_date; - date_t next_eff_date; + date_t next_aux_date; mt19937 rnd_gen; diff --git a/src/item.cc b/src/item.cc index 97411512..653013ea 100644 --- a/src/item.cc +++ b/src/item.cc @@ -35,7 +35,7 @@ namespace ledger { -bool item_t::use_effective_date = false; +bool item_t::use_aux_date = false; bool item_t::has_tag(const string& tag, bool) const { @@ -150,7 +150,7 @@ void item_t::parse_tags(const char * p, if (char * pp = std::strchr(buf, '=')) { *pp++ = '\0'; - _date_eff = parse_date(pp); + _date_aux = parse_date(pp); } if (buf[0]) _date = parse_date(buf); @@ -239,12 +239,12 @@ namespace { value_t get_date(item_t& item) { return item.date(); } - value_t get_actual_date(item_t& item) { - return item.actual_date(); + value_t get_primary_date(item_t& item) { + return item.primary_date(); } - value_t get_effective_date(item_t& item) { - if (optional effective = item.effective_date()) - return *effective; + value_t get_aux_date(item_t& item) { + if (optional aux_date = item.aux_date()) + return *aux_date; return NULL_VALUE; } value_t get_note(item_t& item) { @@ -403,9 +403,11 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, if (name == "actual") return WRAP_FUNCTOR(get_wrapper<&get_actual>); else if (name == "actual_date") - return WRAP_FUNCTOR(get_wrapper<&get_actual_date>); + return WRAP_FUNCTOR(get_wrapper<&get_primary_date>); else if (name == "addr") return WRAP_FUNCTOR(get_wrapper<&get_addr>); + else if (name == "auxiliary_date") + return WRAP_FUNCTOR(get_wrapper<&get_aux_date>); break; case 'b': @@ -435,7 +437,7 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, else if (name == "end_pos") return WRAP_FUNCTOR(get_wrapper<&get_end_pos>); else if (name == "effective_date") - return WRAP_FUNCTOR(get_wrapper<&get_effective_date>); + return WRAP_FUNCTOR(get_wrapper<&get_aux_date>); break; case 'f': @@ -472,6 +474,8 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_pending>); else if (name == "parent") return WRAP_FUNCTOR(get_wrapper<&ignore>); + else if (name == "primary_date") + return WRAP_FUNCTOR(get_wrapper<&get_primary_date>); break; case 's': diff --git a/src/item.h b/src/item.h index 908dd1b8..62575d8b 100644 --- a/src/item.h +++ b/src/item.h @@ -112,7 +112,7 @@ public: state_t _state; optional _date; - optional _date_eff; + optional _date_aux; optional note; optional pos; optional metadata; @@ -138,7 +138,7 @@ public: set_state(item.state()); _date = item._date; - _date_eff = item._date_eff; + _date_aux = item._date_aux; note = item.note; pos = item.pos; metadata = item.metadata; @@ -175,7 +175,7 @@ public: scope_t& scope, bool overwrite_existing = true); - static bool use_effective_date; + static bool use_aux_date; virtual bool has_date() const { return _date; @@ -183,17 +183,17 @@ public: virtual date_t date() const { assert(_date); - if (use_effective_date) - if (optional effective = effective_date()) - return *effective; + if (use_aux_date) + if (optional aux = aux_date()) + return *aux; return *_date; } - virtual date_t actual_date() const { + virtual date_t primary_date() const { assert(_date); return *_date; } - virtual optional effective_date() const { - return _date_eff; + virtual optional aux_date() const { + return _date_aux; } void set_state(state_t new_state) { @@ -220,7 +220,7 @@ private: ar & boost::serialization::base_object(*this); ar & _state; ar & _date; - ar & _date_eff; + ar & _date_aux; ar & note; ar & pos; ar & metadata; diff --git a/src/post.cc b/src/post.cc index d88dd869..125947e4 100644 --- a/src/post.cc +++ b/src/post.cc @@ -91,11 +91,11 @@ date_t post_t::date() const if (xdata_ && is_valid(xdata_->date)) return xdata_->date; - if (item_t::use_effective_date) { - if (_date_eff) - return *_date_eff; - else if (xact && xact->_date_eff) - return *xact->_date_eff; + if (item_t::use_aux_date) { + if (_date_aux) + return *_date_aux; + else if (xact && xact->_date_aux) + return *xact->_date_aux; } if (! _date) { @@ -105,7 +105,7 @@ date_t post_t::date() const return *_date; } -date_t post_t::actual_date() const +date_t post_t::primary_date() const { if (xdata_ && is_valid(xdata_->date)) return xdata_->date; @@ -117,11 +117,11 @@ date_t post_t::actual_date() const return *_date; } -optional post_t::effective_date() const +optional post_t::aux_date() const { - optional date = item_t::effective_date(); + optional date = item_t::aux_date(); if (! date && xact) - return xact->effective_date(); + return xact->aux_date(); return date; } @@ -657,9 +657,9 @@ void to_xml(std::ostream& out, const post_t& post) push_xml y(out, "date"); to_xml(out, *post._date, false); } - if (post._date_eff) { - push_xml y(out, "effective-date"); - to_xml(out, *post._date_eff, false); + if (post._date_aux) { + push_xml y(out, "aux-date"); + to_xml(out, *post._date_aux, false); } if (post.account) { diff --git a/src/post.h b/src/post.h index e626bca1..0cfd3e90 100644 --- a/src/post.h +++ b/src/post.h @@ -123,8 +123,8 @@ public: virtual date_t value_date() const; virtual date_t date() const; - virtual date_t actual_date() const; - virtual optional effective_date() const; + virtual date_t primary_date() const; + virtual optional aux_date() const; string payee() const; @@ -230,7 +230,7 @@ public: { bool operator()(const post_t * left, const post_t * right) const { gregorian::date_duration duration = - left->actual_date() - right->actual_date(); + left->primary_date() - right->primary_date(); if (duration.days() == 0) { return ((left->pos ? left->pos->sequence : 0) < (right->pos ? right->pos->sequence : 0)); diff --git a/src/print.cc b/src/print.cc index 56a2ec7b..63f38d80 100644 --- a/src/print.cc +++ b/src/print.cc @@ -112,11 +112,11 @@ namespace { std::ostringstream buf; - buf << format_date(item_t::use_effective_date ? - xact.date() : xact.actual_date(), + buf << format_date(item_t::use_aux_date ? + xact.date() : xact.primary_date(), format_type, format); - if (! item_t::use_effective_date && xact.effective_date()) - buf << '=' << format_date(*xact.effective_date(), + if (! item_t::use_aux_date && xact.aux_date()) + buf << '=' << format_date(*xact.aux_date(), format_type, format); buf << ' '; diff --git a/src/py_item.cc b/src/py_item.cc index 51d9e50c..361de914 100644 --- a/src/py_item.cc +++ b/src/py_item.cc @@ -149,13 +149,13 @@ void export_item() .def("parse_tags", &item_t::parse_tags) .def("append_note", &item_t::append_note) - .add_static_property("use_effective_date", - make_getter(&item_t::use_effective_date), - make_setter(&item_t::use_effective_date)) + .add_static_property("use_auxiliary_date", + make_getter(&item_t::use_aux_date), + make_setter(&item_t::use_aux_date)) .add_property("date", &item_t::date, make_setter(&item_t::_date)) - .add_property("effective_date", &item_t::effective_date, - make_setter(&item_t::_date_eff)) + .add_property("auxiliary_date", &item_t::aux_date, + make_setter(&item_t::_date_aux)) .add_property("state", &item_t::state, &item_t::set_state) diff --git a/src/py_post.cc b/src/py_post.cc index 62323eb1..cb6ebce7 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -160,7 +160,7 @@ void export_post() .def("get_tag", py_get_tag_2m) .def("date", &post_t::date) - .def("effective_date", &post_t::effective_date) + .def("auxiliary_date", &post_t::aux_date) .def("must_balance", &post_t::must_balance) diff --git a/src/report.cc b/src/report.cc index c562ab38..530e7727 100644 --- a/src/report.cc +++ b/src/report.cc @@ -76,8 +76,7 @@ void report_t::normalize_options(const string& verb) HANDLER(pager_).off(); } - item_t::use_effective_date = (HANDLED(effective) && - ! HANDLED(actual_dates)); + item_t::use_aux_date = (HANDLED(aux_date) && ! HANDLED(primary_date)); commodity_pool_t::current_pool->keep_base = HANDLED(base); commodity_pool_t::current_pool->get_quotes = session.HANDLED(download); @@ -960,10 +959,10 @@ option_t * report_t::lookup_option(const char * p) OPT(abbrev_len_); else OPT_(account_); else OPT(actual); - else OPT(actual_dates); else OPT(add_budget); else OPT(amount_); else OPT(amount_data); + else OPT_ALT(primary_date, actual_dates); else OPT(anon); else OPT_ALT(color, ansi); else OPT(auto_match); @@ -1005,12 +1004,12 @@ option_t * report_t::lookup_option(const char * p) else OPT(date_width_); break; case 'e': - OPT(effective); - else OPT(empty); + OPT(empty); else OPT_(end_); else OPT(equity); else OPT(exact); else OPT(exchange_); + else OPT_ALT(aux_date, effective); break; case 'f': OPT(flat); diff --git a/src/report.h b/src/report.h index 565728df..2b521aae 100644 --- a/src/report.h +++ b/src/report.h @@ -224,12 +224,12 @@ public: HANDLER(abbrev_len_).report(out); HANDLER(account_).report(out); HANDLER(actual).report(out); - HANDLER(actual_dates).report(out); HANDLER(add_budget).report(out); HANDLER(amount_).report(out); HANDLER(amount_data).report(out); HANDLER(anon).report(out); HANDLER(auto_match).report(out); + HANDLER(aux_date).report(out); HANDLER(average).report(out); HANDLER(balance_format_).report(out); HANDLER(base).report(out); @@ -256,7 +256,6 @@ public: HANDLER(display_amount_).report(out); HANDLER(display_total_).report(out); HANDLER(dow).report(out); - HANDLER(effective).report(out); HANDLER(empty).report(out); HANDLER(end_).report(out); HANDLER(equity).report(out); @@ -303,6 +302,7 @@ public: HANDLER(price).report(out); HANDLER(prices_format_).report(out); HANDLER(pricedb_format_).report(out); + HANDLER(primary_date).report(out); HANDLER(quantity).report(out); HANDLER(quarterly).report(out); HANDLER(raw).report(out); @@ -361,8 +361,6 @@ public: parent->HANDLER(limit_).on(string("--actual"), "actual"); }); - OPTION(report_t, actual_dates); - OPTION_(report_t, add_budget, DO() { parent->budget_flags |= BUDGET_BUDGETED | BUDGET_UNBUDGETED; }); @@ -571,7 +569,7 @@ public: }); OPTION(report_t, dow); - OPTION(report_t, effective); + OPTION(report_t, aux_date); OPTION(report_t, empty); // -E OPTION_(report_t, end_, DO_(args) { // -e @@ -822,6 +820,8 @@ public: "P %(datetime) %(display_account) %(scrub(display_amount))\n"); }); + OPTION(report_t, primary_date); + OPTION_(report_t, quantity, DO() { // -O parent->HANDLER(revalued).off(); parent->HANDLER(amount_).set_expr(string("--quantity"), "amount"); diff --git a/src/textual.cc b/src/textual.cc index 739b63f0..4de8d461 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1603,7 +1603,7 @@ xact_t * instance_t::parse_xact(char * line, if (char * p = std::strchr(line, '=')) { *p++ = '\0'; - xact->_date_eff = parse_date(p); + xact->_date_aux = parse_date(p); } xact->_date = parse_date(line); diff --git a/src/xact.cc b/src/xact.cc index 6c07c862..8e1951a5 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -794,9 +794,9 @@ void to_xml(std::ostream& out, const xact_t& xact) push_xml y(out, "date"); to_xml(out, *xact._date, false); } - if (xact._date_eff) { - push_xml y(out, "effective-date"); - to_xml(out, *xact._date_eff, false); + if (xact._date_aux) { + push_xml y(out, "aux-date"); + to_xml(out, *xact._date_aux, false); } if (xact.code) { diff --git a/test/baseline/opt-actual-dates.test b/test/baseline/opt-actual-dates.test deleted file mode 100644 index e69de29b..00000000 diff --git a/test/baseline/opt-aux-date.test b/test/baseline/opt-aux-date.test new file mode 100644 index 00000000..9d1e73d0 --- /dev/null +++ b/test/baseline/opt-aux-date.test @@ -0,0 +1,20 @@ +2008/01/01 January + Expenses:Books $10.00 + Assets:Cash + +2008/01/31=2008/01/01 End of January + Expenses:Books $10.00 ; [=2008/02/01] + Assets:Cash + +2008/02/01 February + Expenses:Books $20.00 + Assets:Cash + +test reg --effective +08-Jan-01 January Expenses:Books $10.00 $10.00 + Assets:Cash $-10.00 0 +08-Feb-01 End of January Expenses:Books $10.00 $10.00 +08-Jan-01 End of January Assets:Cash $-10.00 0 +08-Feb-01 February Expenses:Books $20.00 $20.00 + Assets:Cash $-20.00 0 +end test diff --git a/test/baseline/opt-effective.test b/test/baseline/opt-effective.test deleted file mode 100644 index 9d1e73d0..00000000 --- a/test/baseline/opt-effective.test +++ /dev/null @@ -1,20 +0,0 @@ -2008/01/01 January - Expenses:Books $10.00 - Assets:Cash - -2008/01/31=2008/01/01 End of January - Expenses:Books $10.00 ; [=2008/02/01] - Assets:Cash - -2008/02/01 February - Expenses:Books $20.00 - Assets:Cash - -test reg --effective -08-Jan-01 January Expenses:Books $10.00 $10.00 - Assets:Cash $-10.00 0 -08-Feb-01 End of January Expenses:Books $10.00 $10.00 -08-Jan-01 End of January Assets:Cash $-10.00 0 -08-Feb-01 February Expenses:Books $20.00 $20.00 - Assets:Cash $-20.00 0 -end test diff --git a/test/baseline/opt-primary-date.test b/test/baseline/opt-primary-date.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 52bca153dd7594915501a8a70de67369ac7788a5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 03:05:35 -0600 Subject: Change occurrences of auxiliary_date to just aux_date --- src/item.cc | 2 +- src/py_item.cc | 4 ++-- src/py_post.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 653013ea..a1b9b6bb 100644 --- a/src/item.cc +++ b/src/item.cc @@ -406,7 +406,7 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_primary_date>); else if (name == "addr") return WRAP_FUNCTOR(get_wrapper<&get_addr>); - else if (name == "auxiliary_date") + else if (name == "aux_date") return WRAP_FUNCTOR(get_wrapper<&get_aux_date>); break; diff --git a/src/py_item.cc b/src/py_item.cc index 361de914..a12784ff 100644 --- a/src/py_item.cc +++ b/src/py_item.cc @@ -149,12 +149,12 @@ void export_item() .def("parse_tags", &item_t::parse_tags) .def("append_note", &item_t::append_note) - .add_static_property("use_auxiliary_date", + .add_static_property("use_aux_date", make_getter(&item_t::use_aux_date), make_setter(&item_t::use_aux_date)) .add_property("date", &item_t::date, make_setter(&item_t::_date)) - .add_property("auxiliary_date", &item_t::aux_date, + .add_property("aux_date", &item_t::aux_date, make_setter(&item_t::_date_aux)) .add_property("state", &item_t::state, &item_t::set_state) diff --git a/src/py_post.cc b/src/py_post.cc index cb6ebce7..1c187ff1 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -160,7 +160,7 @@ void export_post() .def("get_tag", py_get_tag_2m) .def("date", &post_t::date) - .def("auxiliary_date", &post_t::aux_date) + .def("aux_date", &post_t::aux_date) .def("must_balance", &post_t::must_balance) -- cgit v1.2.3 From 6cbceb89286a4d0e4043b877cdf9f4f7471289c7 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 03:18:58 -0600 Subject: Apply timelog time to the check-in day Fixes #546 --- src/timelog.cc | 2 +- test/regress/ACE05ECE.test | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 test/regress/ACE05ECE.test (limited to 'src') diff --git a/src/timelog.cc b/src/timelog.cc index ee9a0b6c..5ab6a25c 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -89,7 +89,7 @@ namespace { event.note = out_event.note; std::auto_ptr curr(new xact_t); - curr->_date = out_event.checkin.date(); + curr->_date = event.checkin.date(); curr->code = out_event.desc; // if it wasn't used above curr->payee = event.desc; curr->pos = event.position; diff --git a/test/regress/ACE05ECE.test b/test/regress/ACE05ECE.test new file mode 100644 index 00000000..72ea562b --- /dev/null +++ b/test/regress/ACE05ECE.test @@ -0,0 +1,6 @@ +i 2011/07/20 17:00:00 Hello Work project +o 2011/07/21 01:00:00 Hello + +test reg Hello +11-Jul-20 Work project (Hello) 8.00h 8.00h +end test -- cgit v1.2.3 From f4a8f6b2484637efc22c02c20d79ad5034647abc Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 03:38:42 -0600 Subject: XML account output now generates a tag --- src/xml.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/xml.cc b/src/xml.cc index 15710d44..90efd4b3 100644 --- a/src/xml.cc +++ b/src/xml.cc @@ -52,6 +52,7 @@ namespace { out << "\">\n"; out << "" << acct->name << "\n"; + out << "" << acct->fullname() << "\n"; value_t total = acct->amount(); if (! total.is_null()) { out << "\n"; -- cgit v1.2.3 From 9376b0f7b6a930baa063f0681157c93ba68ed47a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 04:02:24 -0600 Subject: Corrected behavior of >= and <= --- src/value.cc | 4 ++-- test/regress/854150DF.test | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 test/regress/854150DF.test (limited to 'src') diff --git a/src/value.cc b/src/value.cc index f54c06a5..61066f03 100644 --- a/src/value.cc +++ b/src/value.cc @@ -869,7 +869,7 @@ bool value_t::is_less_than(const value_t& val) const case INTEGER: return as_long() < val.as_long(); case AMOUNT: - return val.as_amount() >= as_long(); + return val.as_amount() > as_long(); default: break; } @@ -990,7 +990,7 @@ bool value_t::is_greater_than(const value_t& val) const case INTEGER: return as_long() > val.as_long(); case AMOUNT: - return val.as_amount() > as_long(); + return val.as_amount() < as_long(); default: break; } diff --git a/test/regress/854150DF.test b/test/regress/854150DF.test new file mode 100644 index 00000000..7133e183 --- /dev/null +++ b/test/regress/854150DF.test @@ -0,0 +1,25 @@ +2011-11-10 * test + A:B:C 12.50 GBP + A:C + +test bal --flat -d "depth>=2" + 12.50 GBP A:B:C + -12.50 GBP A:C +-------------------- + 0 +end test + +test bal --flat -d "depth>1" + 12.50 GBP A:B:C + -12.50 GBP A:C +-------------------- + 0 +end test + +test bal --flat -d "depth>2" + 12.50 GBP A:B:C +end test + +test bal --flat -d "depth==2" + -12.50 GBP A:C +end test -- cgit v1.2.3 From 5517871144e61ec1c7bad8f4dd96e0672e63f9d1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 28 Feb 2012 21:19:36 -0600 Subject: Corrected problem with parsing of leap days --- src/times.cc | 1 + test/regress/65FECA4D.test | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 test/regress/65FECA4D.test (limited to 'src') diff --git a/src/times.cc b/src/times.cc index 9712c2ee..8ea90892 100644 --- a/src/times.cc +++ b/src/times.cc @@ -173,6 +173,7 @@ namespace { #else // USE_BOOST_FACETS std::tm data; std::memset(&data, 0, sizeof(std::tm)); + data.tm_year = CURRENT_DATE().year() - 1900; data.tm_mday = 1; // some formats have no day if (strptime(str, fmt_str, &data)) return gregorian::date_from_tm(data); diff --git a/test/regress/65FECA4D.test b/test/regress/65FECA4D.test new file mode 100644 index 00000000..3e575961 --- /dev/null +++ b/test/regress/65FECA4D.test @@ -0,0 +1,12 @@ +--now=2012-02-28 + +Y 2012 + +2/29 E-trade Bank + Expenses:Food $20 + Assets:Cash + +test reg +12-Feb-29 E-trade Bank Expenses:Food $20 $20 + Assets:Cash $-20 0 +end test -- cgit v1.2.3 From 520ece21644c4f3a97f722e351fce382c1661183 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 29 Feb 2012 14:43:16 -0600 Subject: Fixed problem with the Python build --- src/item.cc | 7 ++----- src/item.h | 13 +++++++++++++ src/py_post.cc | 3 +++ src/py_xact.cc | 5 +++-- 4 files changed, 21 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index a1b9b6bb..d123ee5a 100644 --- a/src/item.cc +++ b/src/item.cc @@ -338,13 +338,10 @@ namespace { } value_t get_seq(item_t& item) { - return item.pos ? long(item.pos->sequence) : 0L; + return long(item.seq()); } value_t get_id(item_t& item) { - if (optional ref = item.get_tag(_("UUID"))) - return *ref; - else - return item.pos ? long(item.pos->sequence) : 0L; + return string_value(item.id()); } value_t get_addr(item_t& item) { diff --git a/src/item.h b/src/item.h index 62575d8b..af3992c0 100644 --- a/src/item.h +++ b/src/item.h @@ -151,6 +151,19 @@ public: return ! (*this == xact); } + string id() const { + if (optional ref = get_tag(_("UUID"))) { + return ref->to_string(); + } else { + std::ostringstream buf; + buf << seq(); + return buf.str(); + } + } + std::size_t seq() const { + return pos ? pos->sequence : 0L; + } + virtual bool has_tag(const string& tag, bool inherit = true) const; virtual bool has_tag(const mask_t& tag_mask, diff --git a/src/py_post.cc b/src/py_post.cc index 1c187ff1..cace419f 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -132,6 +132,9 @@ void export_post() class_< post_t, bases > ("Posting") //.def(init()) + .def("id", &post_t::id) + .def("seq", &post_t::seq) + .add_property("xact", make_getter(&post_t::xact, return_internal_reference<>()), diff --git a/src/py_xact.cc b/src/py_xact.cc index 604d8d59..af1fcdd5 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -107,6 +107,9 @@ void export_xact() ; class_< xact_t, bases > ("Transaction") + .def("id", &xact_t::id) + .def("seq", &xact_t::seq) + .add_property("code", make_getter(&xact_t::code), make_setter(&xact_t::code)) @@ -117,8 +120,6 @@ void export_xact() .def("add_post", &xact_t::add_post, with_custodian_and_ward<1, 2>()) .def("magnitude", &xact_t::magnitude) - .def("idstring", &xact_t::idstring) - .def("id", &xact_t::id) .def("lookup", &xact_t::lookup) -- cgit v1.2.3 From eac84f5e2184cabad709fbb165d58d0b17f791d0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 29 Feb 2012 22:11:40 -0600 Subject: Removed comment that no longer made sense --- src/journal.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/journal.h b/src/journal.h index d7cf94da..70820cbd 100644 --- a/src/journal.h +++ b/src/journal.h @@ -156,8 +156,6 @@ public: return sources.end(); } - // These four methods are delegated to the current session, since all - // accounts processed are gathered together at the session level. void add_account(account_t * acct); bool remove_account(account_t * acct); account_t * find_account(const string& name, bool auto_create = true); -- cgit v1.2.3 From 8013e091292628b3e631fea07342c31f1c36966b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 29 Feb 2012 22:11:55 -0600 Subject: Added STR() utility function --- src/accum.cc | 3 +++ src/accum.h | 8 ++++++++ src/draft.cc | 4 +--- 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/accum.cc b/src/accum.cc index 8f3d5185..0187995e 100644 --- a/src/accum.cc +++ b/src/accum.cc @@ -35,6 +35,9 @@ namespace ledger { +straccstream _accum; +std::ostringstream _accum_buffer; + std::streamsize straccbuf::xsputn(const char * s, std::streamsize num) { if (index == 0) { diff --git a/src/accum.h b/src/accum.h index 236a7714..411bcbe6 100644 --- a/src/accum.h +++ b/src/accum.h @@ -83,6 +83,14 @@ public: #define ACCUM(obj) (static_cast(obj).str()) +extern straccstream _accum; +extern std::ostringstream _accum_buffer; + +#define STR(msg) \ + ((_accum_buffer << ACCUM(_accum << msg)), \ + _accum.clear(), \ + _accum_buffer.str()) + } // namespace ledger #endif // _ACCUM_H diff --git a/src/draft.cc b/src/draft.cc index 2b8b45f7..9f9ec6e8 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -67,10 +67,8 @@ void draft_t::xact_template_t::dump(std::ostream& out) const << std::endl; } else { foreach (const post_template_t& post, posts) { - straccstream accum; out << std::endl - << ACCUM(accum << _("[Posting \"%1\"]") - << (post.from ? _("from") : _("to"))) + << STR(_("[Posting \"%1\"]") << (post.from ? _("from") : _("to"))) << std::endl; if (post.account_mask) -- cgit v1.2.3 From 6989b0748bb8afae80f7f3271ec014dbb4ad07d3 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 29 Feb 2012 22:12:07 -0600 Subject: Fixed Python initialization problem with --import --- src/pyinterp.cc | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/pyinterp.cc b/src/pyinterp.cc index e0fd2d59..e48f16c6 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -191,32 +191,34 @@ object python_interpreter_t::import_into_main(const string& str) object python_interpreter_t::import_option(const string& str) { + if (! is_initialized) + initialize(); + path file(str); + string name(str); python::object sys_module = python::import("sys"); python::object sys_dict = sys_module.attr("__dict__"); python::list paths(sys_dict["path"]); + if (contains(str, ".py")) { #if BOOST_VERSION >= 103700 - paths.insert(0, file.parent_path().string()); - sys_dict["path"] = paths; + path& cwd(get_parsing_context().current_directory); + paths.insert(0, filesystem::absolute(file, cwd).parent_path().string()); + sys_dict["path"] = paths; #if BOOST_VERSION >= 104600 - string name = file.filename().string(); - if (contains(name, ".py")) name = file.stem().string(); #else - string name = file.filename(); - if (contains(name, ".py")) name = file.stem(); #endif #else // BOOST_VERSION >= 103700 - paths.insert(0, file.branch_path().string()); - sys_dict["path"] = paths; - - string name = file.leaf(); + paths.insert(0, file.branch_path().string()); + sys_dict["path"] = paths; + name = file.leaf(); #endif // BOOST_VERSION >= 103700 + } return python::import(python::str(name.c_str())); } -- cgit v1.2.3 From 8021955292d22ed0df5d7b018bf4238966d9c1e7 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 29 Feb 2012 22:12:24 -0600 Subject: Removed incorrect session.h include in textual.cc --- src/textual.cc | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 4de8d461..4977852c 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -39,7 +39,6 @@ #include "query.h" #include "pstream.h" #include "pool.h" -#include "session.h" #define TIMELOG_SUPPORT 1 #if defined(TIMELOG_SUPPORT) -- cgit v1.2.3 From e2afc783db0dff1927b00dc506390353d9e3bbd2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 29 Feb 2012 22:32:23 -0600 Subject: Increased file copyrights to 2012 --- src/account.cc | 2 +- src/account.h | 2 +- src/accum.cc | 2 +- src/accum.h | 2 +- src/amount.cc | 2 +- src/amount.h | 2 +- src/annotate.cc | 2 +- src/annotate.h | 2 +- src/archive.cc | 2 +- src/archive.h | 2 +- src/balance.cc | 2 +- src/balance.h | 2 +- src/chain.cc | 2 +- src/chain.h | 2 +- src/commodity.cc | 2 +- src/commodity.h | 2 +- src/compare.cc | 2 +- src/compare.h | 2 +- src/convert.cc | 2 +- src/convert.h | 2 +- src/csv.cc | 2 +- src/csv.h | 2 +- src/draft.cc | 2 +- src/draft.h | 2 +- src/emacs.cc | 2 +- src/emacs.h | 2 +- src/error.cc | 2 +- src/error.h | 2 +- src/expr.cc | 2 +- src/expr.h | 2 +- src/exprbase.h | 2 +- src/filters.cc | 2 +- src/filters.h | 2 +- src/flags.h | 2 +- src/format.cc | 2 +- src/format.h | 2 +- src/generate.cc | 2 +- src/generate.h | 2 +- src/global.cc | 2 +- src/global.h | 4 ++-- src/item.cc | 2 +- src/item.h | 2 +- src/iterators.cc | 2 +- src/iterators.h | 2 +- src/journal.cc | 2 +- src/journal.h | 2 +- src/lookup.cc | 2 +- src/lookup.h | 2 +- src/main.cc | 2 +- src/mask.cc | 2 +- src/mask.h | 2 +- src/op.cc | 2 +- src/op.h | 2 +- src/option.cc | 2 +- src/option.h | 2 +- src/org.cc | 2 +- src/org.h | 2 +- src/output.cc | 2 +- src/output.h | 2 +- src/parser.cc | 2 +- src/parser.h | 2 +- src/pool.cc | 2 +- src/pool.h | 2 +- src/post.cc | 2 +- src/post.h | 2 +- src/precmd.cc | 2 +- src/precmd.h | 2 +- src/predicate.cc | 2 +- src/predicate.h | 2 +- src/print.cc | 2 +- src/print.h | 2 +- src/pstream.h | 2 +- src/py_account.cc | 2 +- src/py_amount.cc | 2 +- src/py_balance.cc | 2 +- src/py_commodity.cc | 2 +- src/py_expr.cc | 2 +- src/py_format.cc | 2 +- src/py_item.cc | 2 +- src/py_journal.cc | 2 +- src/py_post.cc | 2 +- src/py_times.cc | 2 +- src/py_utils.cc | 2 +- src/py_value.cc | 2 +- src/py_xact.cc | 2 +- src/pyfstream.h | 2 +- src/pyinterp.cc | 2 +- src/pyinterp.h | 2 +- src/pyledger.cc | 2 +- src/pyutils.h | 2 +- src/query.cc | 2 +- src/query.h | 2 +- src/quotes.cc | 2 +- src/quotes.h | 2 +- src/report.cc | 2 +- src/report.h | 2 +- src/scope.cc | 2 +- src/scope.h | 2 +- src/series.h | 2 +- src/session.cc | 2 +- src/session.h | 2 +- src/stats.cc | 2 +- src/stats.h | 2 +- src/stream.cc | 2 +- src/stream.h | 2 +- src/system.hh.in | 2 +- src/temps.cc | 2 +- src/temps.h | 2 +- src/textual.cc | 2 +- src/timelog.cc | 2 +- src/timelog.h | 2 +- src/times.cc | 2 +- src/times.h | 2 +- src/token.cc | 2 +- src/token.h | 2 +- src/unistring.h | 2 +- src/utils.cc | 2 +- src/utils.h | 2 +- src/value.cc | 2 +- src/value.h | 2 +- src/xact.cc | 2 +- src/xact.h | 2 +- src/xml.cc | 2 +- src/xml.h | 2 +- 124 files changed, 125 insertions(+), 125 deletions(-) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index 42c10839..40ddf70b 100644 --- a/src/account.cc +++ b/src/account.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/account.h b/src/account.h index 7a632b35..8f0f915f 100644 --- a/src/account.h +++ b/src/account.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/accum.cc b/src/accum.cc index 0187995e..3add051b 100644 --- a/src/accum.cc +++ b/src/accum.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/accum.h b/src/accum.h index 411bcbe6..349aeba9 100644 --- a/src/accum.h +++ b/src/accum.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/amount.cc b/src/amount.cc index 85afc3d8..4d26a688 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/amount.h b/src/amount.h index f7e877a7..3a8e06b9 100644 --- a/src/amount.h +++ b/src/amount.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/annotate.cc b/src/annotate.cc index 8ba46f4f..cd1733ca 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/annotate.h b/src/annotate.h index b590ca45..3c6db8e8 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/archive.cc b/src/archive.cc index 28760512..72ec0419 100644 --- a/src/archive.cc +++ b/src/archive.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/archive.h b/src/archive.h index 1ebf3496..4ce5e0e7 100644 --- a/src/archive.h +++ b/src/archive.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/balance.cc b/src/balance.cc index 7ce9d994..4fba7344 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/balance.h b/src/balance.h index ac22f3e7..57e6ace4 100644 --- a/src/balance.h +++ b/src/balance.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/chain.cc b/src/chain.cc index 61388840..fc1be5bd 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/chain.h b/src/chain.h index 7bd76712..080c4231 100644 --- a/src/chain.h +++ b/src/chain.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/commodity.cc b/src/commodity.cc index 5fd54d11..643d0d1e 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/commodity.h b/src/commodity.h index d7747b2a..68f788e3 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/compare.cc b/src/compare.cc index cdc96a86..e2a298c2 100644 --- a/src/compare.cc +++ b/src/compare.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/compare.h b/src/compare.h index 0e7bf5e5..e1abbca1 100644 --- a/src/compare.h +++ b/src/compare.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/convert.cc b/src/convert.cc index 15995d05..1b1bf814 100644 --- a/src/convert.cc +++ b/src/convert.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/convert.h b/src/convert.h index 6d02f24a..de958108 100644 --- a/src/convert.h +++ b/src/convert.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/csv.cc b/src/csv.cc index 82c28ff3..823238c7 100644 --- a/src/csv.cc +++ b/src/csv.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/csv.h b/src/csv.h index 909439ff..4d6e1253 100644 --- a/src/csv.h +++ b/src/csv.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/draft.cc b/src/draft.cc index 9f9ec6e8..7c95caf7 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/draft.h b/src/draft.h index 59039f77..41485731 100644 --- a/src/draft.h +++ b/src/draft.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/emacs.cc b/src/emacs.cc index 5048a348..41c67cc6 100644 --- a/src/emacs.cc +++ b/src/emacs.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/emacs.h b/src/emacs.h index 97292728..a018ce68 100644 --- a/src/emacs.h +++ b/src/emacs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/error.cc b/src/error.cc index 88adfbdb..4a16f4e3 100644 --- a/src/error.cc +++ b/src/error.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/error.h b/src/error.h index b9960b03..7630f017 100644 --- a/src/error.h +++ b/src/error.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/expr.cc b/src/expr.cc index b3d4abcd..74d16ecc 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/expr.h b/src/expr.h index 79ae2864..e082efa5 100644 --- a/src/expr.h +++ b/src/expr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/exprbase.h b/src/exprbase.h index e0e2824f..0b1ef243 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/filters.cc b/src/filters.cc index fa1f6fa2..72ce9c32 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/filters.h b/src/filters.h index c972de82..22f2d2cb 100644 --- a/src/filters.h +++ b/src/filters.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/flags.h b/src/flags.h index 09b7eec4..e2046c08 100644 --- a/src/flags.h +++ b/src/flags.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/format.cc b/src/format.cc index 65c06488..a391fdf1 100644 --- a/src/format.cc +++ b/src/format.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/format.h b/src/format.h index f30b8184..74d77768 100644 --- a/src/format.h +++ b/src/format.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/generate.cc b/src/generate.cc index 963cd845..bf9a8036 100644 --- a/src/generate.cc +++ b/src/generate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/generate.h b/src/generate.h index abf719d4..1b22004b 100644 --- a/src/generate.h +++ b/src/generate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/global.cc b/src/global.cc index 34427f4b..ee921fc5 100644 --- a/src/global.cc +++ b/src/global.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/global.h b/src/global.h index 6504230d..28bffc3a 100644 --- a/src/global.h +++ b/src/global.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -113,7 +113,7 @@ public: out << "Ledger " << ledger::version << _(", the command-line accounting tool"); out << - _("\n\nCopyright (c) 2003-2010, John Wiegley. All rights reserved.\n\n\ + _("\n\nCopyright (c) 2003-2012, John Wiegley. All rights reserved.\n\n\ This program is made available under the terms of the BSD Public License.\n\ See LICENSE file included with the distribution for details and disclaimer."); out << std::endl; diff --git a/src/item.cc b/src/item.cc index d123ee5a..3a2b0b60 100644 --- a/src/item.cc +++ b/src/item.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/item.h b/src/item.h index af3992c0..3a9c55bb 100644 --- a/src/item.h +++ b/src/item.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/iterators.cc b/src/iterators.cc index b398646e..72e0481c 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/iterators.h b/src/iterators.h index 93782400..6d490259 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/journal.cc b/src/journal.cc index ea90fa05..2ebe90fb 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/journal.h b/src/journal.h index 70820cbd..6d10ffda 100644 --- a/src/journal.h +++ b/src/journal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/lookup.cc b/src/lookup.cc index 452727d6..ce22529d 100644 --- a/src/lookup.cc +++ b/src/lookup.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/lookup.h b/src/lookup.h index 8e83b84e..ba64b0b5 100644 --- a/src/lookup.h +++ b/src/lookup.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/main.cc b/src/main.cc index 9031341f..2202a5de 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/mask.cc b/src/mask.cc index 52907cfe..5bc10d5f 100644 --- a/src/mask.cc +++ b/src/mask.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/mask.h b/src/mask.h index e72347ad..15929b2e 100644 --- a/src/mask.h +++ b/src/mask.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/op.cc b/src/op.cc index 6dff031c..372101f0 100644 --- a/src/op.cc +++ b/src/op.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/op.h b/src/op.h index c4d353dc..192c1f5e 100644 --- a/src/op.h +++ b/src/op.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/option.cc b/src/option.cc index 2843c775..170b94af 100644 --- a/src/option.cc +++ b/src/option.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/option.h b/src/option.h index 8f89d081..dc1099db 100644 --- a/src/option.h +++ b/src/option.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/org.cc b/src/org.cc index 7c8e8c0d..3c897f54 100644 --- a/src/org.cc +++ b/src/org.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/org.h b/src/org.h index ed023be2..0b34b610 100644 --- a/src/org.h +++ b/src/org.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/output.cc b/src/output.cc index b26881a3..aaf81f60 100644 --- a/src/output.cc +++ b/src/output.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/output.h b/src/output.h index ac3925c4..281f69b6 100644 --- a/src/output.h +++ b/src/output.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/parser.cc b/src/parser.cc index 6197af6b..2c9069d7 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/parser.h b/src/parser.h index 09e12d95..75fd9a41 100644 --- a/src/parser.h +++ b/src/parser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/pool.cc b/src/pool.cc index 65edbd6a..ba408fc5 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/pool.h b/src/pool.h index 4b935f69..87b315f9 100644 --- a/src/pool.h +++ b/src/pool.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/post.cc b/src/post.cc index 125947e4..e0ca149f 100644 --- a/src/post.cc +++ b/src/post.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/post.h b/src/post.h index 0cfd3e90..ce33fefc 100644 --- a/src/post.h +++ b/src/post.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/precmd.cc b/src/precmd.cc index 663b638d..6f8becb6 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/precmd.h b/src/precmd.h index 277933c3..1c52d8a7 100644 --- a/src/precmd.h +++ b/src/precmd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/predicate.cc b/src/predicate.cc index fd301a7d..58d6c752 100644 --- a/src/predicate.cc +++ b/src/predicate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/predicate.h b/src/predicate.h index 673f1d5d..7d58dc2f 100644 --- a/src/predicate.h +++ b/src/predicate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/print.cc b/src/print.cc index 63f38d80..c544c4e0 100644 --- a/src/print.cc +++ b/src/print.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/print.h b/src/print.h index 527f1912..42bfc8b6 100644 --- a/src/print.h +++ b/src/print.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/pstream.h b/src/pstream.h index 8134495d..a894325d 100644 --- a/src/pstream.h +++ b/src/pstream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/py_account.cc b/src/py_account.cc index 5ef86871..64a7ae54 100644 --- a/src/py_account.cc +++ b/src/py_account.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_amount.cc b/src/py_amount.cc index 9ce4a02d..f10595e8 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_balance.cc b/src/py_balance.cc index 0140a625..6c9ccb24 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_commodity.cc b/src/py_commodity.cc index 6d8a29b3..11ebe844 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_expr.cc b/src/py_expr.cc index 027125e2..dd9df1f5 100644 --- a/src/py_expr.cc +++ b/src/py_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_format.cc b/src/py_format.cc index fc2103c7..482eaf5b 100644 --- a/src/py_format.cc +++ b/src/py_format.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_item.cc b/src/py_item.cc index a12784ff..e3e49457 100644 --- a/src/py_item.cc +++ b/src/py_item.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_journal.cc b/src/py_journal.cc index bd781225..4f5427f5 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_post.cc b/src/py_post.cc index cace419f..bd599604 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_times.cc b/src/py_times.cc index c2e0b8f8..17f9ec7e 100644 --- a/src/py_times.cc +++ b/src/py_times.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_utils.cc b/src/py_utils.cc index 710dca4b..45ffe545 100644 --- a/src/py_utils.cc +++ b/src/py_utils.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_value.cc b/src/py_value.cc index f8f36453..3b67c4c6 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/py_xact.cc b/src/py_xact.cc index af1fcdd5..97d5df47 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/pyfstream.h b/src/pyfstream.h index 49b072f2..972f976f 100644 --- a/src/pyfstream.h +++ b/src/pyfstream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/pyinterp.cc b/src/pyinterp.cc index e48f16c6..44bea2cd 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/pyinterp.h b/src/pyinterp.h index ea947c5a..ae8dd9c2 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/pyledger.cc b/src/pyledger.cc index 4a53532a..cf5e362e 100644 --- a/src/pyledger.cc +++ b/src/pyledger.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/pyutils.h b/src/pyutils.h index 7e016502..44bb6d90 100644 --- a/src/pyutils.h +++ b/src/pyutils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/query.cc b/src/query.cc index 812123cb..8bdabb38 100644 --- a/src/query.cc +++ b/src/query.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/query.h b/src/query.h index 8f7917b2..52168539 100644 --- a/src/query.h +++ b/src/query.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/quotes.cc b/src/quotes.cc index 0cc8d06b..b29eb8bd 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/quotes.h b/src/quotes.h index 376d8918..52092fbc 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/report.cc b/src/report.cc index 530e7727..2c0c3970 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/report.h b/src/report.h index 2b521aae..35d45437 100644 --- a/src/report.h +++ b/src/report.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/scope.cc b/src/scope.cc index 2b9851b0..b2a7b17b 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/scope.h b/src/scope.h index 2720e8fc..6fcd67e9 100644 --- a/src/scope.h +++ b/src/scope.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/series.h b/src/series.h index 40f34051..75b98194 100644 --- a/src/series.h +++ b/src/series.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/session.cc b/src/session.cc index 31122daa..9d994a9b 100644 --- a/src/session.cc +++ b/src/session.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/session.h b/src/session.h index 680b8a0e..93bee8ba 100644 --- a/src/session.h +++ b/src/session.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/stats.cc b/src/stats.cc index 524f5a87..0966fee2 100644 --- a/src/stats.cc +++ b/src/stats.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/stats.h b/src/stats.h index b7bf94c5..7b00fec8 100644 --- a/src/stats.h +++ b/src/stats.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/stream.cc b/src/stream.cc index 5d4cf5e0..ce40bfcc 100644 --- a/src/stream.cc +++ b/src/stream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/stream.h b/src/stream.h index 42c85534..c317ebdf 100644 --- a/src/stream.h +++ b/src/stream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/system.hh.in b/src/system.hh.in index 42a82e41..e14166b2 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/temps.cc b/src/temps.cc index 365c33c5..cb471d41 100644 --- a/src/temps.cc +++ b/src/temps.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/temps.h b/src/temps.h index 1e7eb69f..ad4e5672 100644 --- a/src/temps.h +++ b/src/temps.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/textual.cc b/src/textual.cc index 4977852c..15642cae 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/timelog.cc b/src/timelog.cc index 5ab6a25c..8d3d69c1 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/timelog.h b/src/timelog.h index 020ae4f2..12083302 100644 --- a/src/timelog.h +++ b/src/timelog.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/times.cc b/src/times.cc index 8ea90892..21ec1859 100644 --- a/src/times.cc +++ b/src/times.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/times.h b/src/times.h index bc462efa..6eadcad3 100644 --- a/src/times.h +++ b/src/times.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/token.cc b/src/token.cc index 77092d49..9449d9b7 100644 --- a/src/token.cc +++ b/src/token.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/token.h b/src/token.h index cbdf1258..01ff7ee9 100644 --- a/src/token.h +++ b/src/token.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/unistring.h b/src/unistring.h index 4be36b0d..a33c6e3f 100644 --- a/src/unistring.h +++ b/src/unistring.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/utils.cc b/src/utils.cc index 5260fd42..09526267 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/utils.h b/src/utils.h index c7aaac52..e37f37aa 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/value.cc b/src/value.cc index 61066f03..5fa748f6 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/value.h b/src/value.h index f8495002..1e4d0ce9 100644 --- a/src/value.h +++ b/src/value.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/xact.cc b/src/xact.cc index 8e1951a5..0fedb42a 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/xact.h b/src/xact.h index ff4b7bc2..cb7bdeb3 100644 --- a/src/xact.h +++ b/src/xact.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/xml.cc b/src/xml.cc index 90efd4b3..560db805 100644 --- a/src/xml.cc +++ b/src/xml.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 diff --git a/src/xml.h b/src/xml.h index 5d14dab3..871fd120 100644 --- a/src/xml.h +++ b/src/xml.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, 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 -- cgit v1.2.3 From 944e580825f0d9ce060b6dcdffe8990b6c2cca20 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 03:31:28 -0600 Subject: Refactored the notion of "the current parsing context" --- src/accum.h | 10 +- src/context.h | 151 ++++++++++++++ src/convert.cc | 12 +- src/csv.cc | 81 ++++---- src/csv.h | 33 +--- src/generate.cc | 10 +- src/global.cc | 7 +- src/journal.cc | 139 ++++++------- src/journal.h | 32 ++- src/precmd.cc | 10 +- src/py_commodity.cc | 2 +- src/py_journal.cc | 11 +- src/pyinterp.cc | 35 +++- src/scope.h | 2 +- src/session.cc | 31 ++- src/session.h | 3 + src/textual.cc | 448 +++++++++++++++++++----------------------- src/timelog.cc | 15 +- src/timelog.h | 11 +- src/xact.cc | 6 +- src/xact.h | 3 +- test/baseline/dir-tag.test | 2 +- test/baseline/feat-check.test | 2 +- 23 files changed, 598 insertions(+), 458 deletions(-) create mode 100644 src/context.h (limited to 'src') diff --git a/src/accum.h b/src/accum.h index 349aeba9..dde93c30 100644 --- a/src/accum.h +++ b/src/accum.h @@ -86,10 +86,16 @@ public: extern straccstream _accum; extern std::ostringstream _accum_buffer; +inline string str_helper_func() { + string buf = _accum_buffer.str(); + _accum_buffer.clear(); + _accum_buffer.str(""); + return buf; +} + #define STR(msg) \ ((_accum_buffer << ACCUM(_accum << msg)), \ - _accum.clear(), \ - _accum_buffer.str()) + _accum.clear(), str_helper_func()) } // namespace ledger diff --git a/src/context.h b/src/context.h new file mode 100644 index 00000000..0533536f --- /dev/null +++ b/src/context.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2003-2012, 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 data + */ + +/** + * @file context.h + * @author John Wiegley + * + * @ingroup data + */ +#ifndef _CONTEXT_H +#define _CONTEXT_H + +#include "utils.h" +#include "times.h" + +namespace ledger { + +class journal_t; +class account_t; +class scope_t; + +class parse_context_t +{ +public: + static const std::size_t MAX_LINE = 4096; + + shared_ptr stream; + + path pathname; + path current_directory; + journal_t * journal; + account_t * master; + scope_t * scope; + char linebuf[MAX_LINE + 1]; + istream_pos_type line_beg_pos; + istream_pos_type curr_pos; + std::size_t linenum; + std::size_t errors; + std::size_t count; + std::size_t sequence; + + explicit parse_context_t(shared_ptr _stream, + const path& cwd) + : stream(_stream), current_directory(cwd), master(NULL), + scope(NULL), linenum(0), errors(0), count(0), sequence(1) {} + + parse_context_t(const parse_context_t& context) + : stream(context.stream), + pathname(context.pathname), + current_directory(context.current_directory), + journal(context.journal), + master(context.master), + scope(context.scope), + line_beg_pos(context.line_beg_pos), + curr_pos(context.curr_pos), + linenum(context.linenum), + errors(context.errors), + count(context.count), + sequence(context.sequence) { + std::memcpy(linebuf, context.linebuf, MAX_LINE); + } + + string location() const { + return file_context(pathname, linenum); + } + + void warning(const string& what) const { + warning_func(location() + what); + } +}; + +inline parse_context_t open_for_reading(const path& pathname, + const path& cwd) +{ + path filename = resolve_path(pathname); + + if (! exists(filename)) + throw_(std::runtime_error, + _("Cannot read journal file %1") << filename); + + path parent(filesystem::absolute(pathname, cwd).parent_path()); + shared_ptr stream(new ifstream(filename)); + parse_context_t context(stream, parent); + context.pathname = filename; + return context; +} + +class parse_context_stack_t +{ + std::list parsing_context; + +public: + void push(shared_ptr stream, + const path& cwd = filesystem::current_path()) { + parsing_context.push_front(parse_context_t(stream, cwd)); + } + void push(const path& pathname, + const path& cwd = filesystem::current_path()) { + parsing_context.push_front(open_for_reading(pathname, cwd)); + } + + void push(const parse_context_t& context) { + parsing_context.push_front(context); + } + + void pop() { + assert(! parsing_context.empty()); + parsing_context.pop_front(); + } + + parse_context_t& get_current() { + assert(! parsing_context.empty()); + return parsing_context.front(); + } +}; + +} // namespace ledger + +#endif // _CONTEXT_H diff --git a/src/convert.cc b/src/convert.cc index 1b1bf814..e8ca241e 100644 --- a/src/convert.cc +++ b/src/convert.cc @@ -63,12 +63,16 @@ value_t convert_command(call_scope_t& args) print_xacts formatter(report); path csv_file_path(args.get(0)); - ifstream data(csv_file_path); - csv_reader reader(data, csv_file_path); + + report.session.parsing_context.push(csv_file_path); + parse_context_t& context(report.session.parsing_context.get_current()); + context.journal = &journal; + context.master = bucket; + + csv_reader reader(context); try { - while (xact_t * xact = reader.read_xact(journal, bucket, - report.HANDLED(rich_data))) { + while (xact_t * xact = reader.read_xact(report.HANDLED(rich_data))) { if (report.HANDLED(invert)) { foreach (post_t * post, xact->posts) post->amount.in_place_negate(); diff --git a/src/csv.cc b/src/csv.cc index 823238c7..305db992 100644 --- a/src/csv.cc +++ b/src/csv.cc @@ -40,27 +40,27 @@ namespace ledger { -string csv_reader::read_field(std::istream& sin) +string csv_reader::read_field(std::istream& in) { string field; char c; - if (sin.peek() == '"' || sin.peek() == '|') { - sin.get(c); + if (in.peek() == '"' || in.peek() == '|') { + in.get(c); char x; - while (sin.good() && ! sin.eof()) { - sin.get(x); + while (in.good() && ! in.eof()) { + in.get(x); if (x == '\\') { - sin.get(x); + in.get(x); } - else if (x == '"' && sin.peek() == '"') { - sin.get(x); + else if (x == '"' && in.peek() == '"') { + in.get(x); } else if (x == c) { if (x == '|') - sin.unget(); - else if (sin.peek() == ',') - sin.get(c); + in.unget(); + else if (in.peek() == ',') + in.get(c); break; } if (x != '\0') @@ -68,9 +68,9 @@ string csv_reader::read_field(std::istream& sin) } } else { - while (sin.good() && ! sin.eof()) { - sin.get(c); - if (sin.good()) { + while (in.good() && ! in.eof()) { + in.get(c); + if (in.good()) { if (c == ',') break; if (c != '\0') @@ -82,22 +82,22 @@ string csv_reader::read_field(std::istream& sin) return field; } -char * csv_reader::next_line(std::istream& sin) +char * csv_reader::next_line(std::istream& in) { - while (sin.good() && ! sin.eof() && sin.peek() == '#') - sin.getline(linebuf, MAX_LINE); + while (in.good() && ! in.eof() && in.peek() == '#') + in.getline(context.linebuf, parse_context_t::MAX_LINE); - if (! sin.good() || sin.eof()) + if (! in.good() || in.eof()) return NULL; - sin.getline(linebuf, MAX_LINE); + in.getline(context.linebuf, parse_context_t::MAX_LINE); - return linebuf; + return context.linebuf; } -void csv_reader::read_index(std::istream& sin) +void csv_reader::read_index(std::istream& in) { - char * line = next_line(sin); + char * line = next_line(in); if (! line) return; @@ -130,13 +130,12 @@ void csv_reader::read_index(std::istream& sin) } } -xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket, - bool rich_data) +xact_t * csv_reader::read_xact(bool rich_data) { - char * line = next_line(in); + char * line = next_line(*context.stream.get()); if (! line || index.empty()) return NULL; - linenum++; + context.linenum++; std::istringstream instr(line); @@ -146,18 +145,18 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket, xact->set_state(item_t::CLEARED); xact->pos = position_t(); - xact->pos->pathname = pathname; - xact->pos->beg_pos = in.tellg(); - xact->pos->beg_line = linenum; - xact->pos->sequence = sequence++; + xact->pos->pathname = context.pathname; + xact->pos->beg_pos = context.stream->tellg(); + xact->pos->beg_line = context.linenum; + xact->pos->sequence = context.sequence++; post->xact = xact.get(); post->pos = position_t(); - post->pos->pathname = pathname; - post->pos->beg_pos = in.tellg(); - post->pos->beg_line = linenum; - post->pos->sequence = sequence++; + post->pos->pathname = context.pathname; + post->pos->beg_pos = context.stream->tellg(); + post->pos->beg_line = context.linenum; + post->pos->sequence = context.sequence++; post->set_state(item_t::CLEARED); post->account = NULL; @@ -186,7 +185,7 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket, case FIELD_PAYEE: { bool found = false; - foreach (payee_mapping_t& value, journal.payee_mappings) { + foreach (payee_mapping_t& value, context.journal->payee_mappings) { DEBUG("csv.mappings", "Looking for payee mapping: " << value.first); if (value.first.match(field)) { xact->payee = value.second; @@ -244,7 +243,7 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket, // Translate the account name, if we have enough information to do so - foreach (account_mapping_t& value, journal.account_mappings) { + foreach (account_mapping_t& value, context.journal->account_mappings) { if (value.first.match(xact->payee)) { post->account = value.second; break; @@ -260,13 +259,13 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket, post->xact = xact.get(); post->pos = position_t(); - post->pos->pathname = pathname; - post->pos->beg_pos = in.tellg(); - post->pos->beg_line = linenum; - post->pos->sequence = sequence++; + post->pos->pathname = context.pathname; + post->pos->beg_pos = context.stream->tellg(); + post->pos->beg_line = context.linenum; + post->pos->sequence = context.sequence++; post->set_state(item_t::CLEARED); - post->account = bucket; + post->account = context.master; if (! amt.is_null()) post->amount = - amt; diff --git a/src/csv.h b/src/csv.h index 4d6e1253..24ea9121 100644 --- a/src/csv.h +++ b/src/csv.h @@ -43,6 +43,7 @@ #define _CSV_H #include "value.h" +#include "context.h" namespace ledger { @@ -52,13 +53,7 @@ class account_t; class csv_reader { - static const std::size_t MAX_LINE = 4096; - - std::istream& in; - path pathname; - char linebuf[MAX_LINE]; - std::size_t linenum; - std::size_t sequence; + parse_context_t context; enum headers_t { FIELD_DATE = 0, @@ -86,9 +81,8 @@ class csv_reader std::vector names; public: - csv_reader(std::istream& _in, const path& _pathname) - : in(_in), pathname(_pathname), - linenum(0), sequence(0), + csv_reader(parse_context_t& _context) + : context(_context), date_mask("date"), date_aux_mask("posted( ?date)?"), code_mask("code"), @@ -97,32 +91,23 @@ public: cost_mask("cost"), total_mask("total"), note_mask("note") { - read_index(in); + read_index(*context.stream.get()); } void read_index(std::istream& in); string read_field(std::istream& in); char * next_line(std::istream& in); - xact_t * read_xact(journal_t& journal, account_t * bucket, bool rich_data); + xact_t * read_xact(bool rich_data); const char * get_last_line() const { - return linebuf; + return context.linebuf; } - path get_pathname() const { - return pathname; + return context.pathname; } std::size_t get_linenum() const { - return linenum; - } - - void reset() { - pathname.clear(); - index.clear(); - names.clear(); - linenum = 0; - sequence = 0; + return context.linenum; } }; diff --git a/src/generate.cc b/src/generate.cc index bf9a8036..edd58632 100644 --- a/src/generate.cc +++ b/src/generate.cc @@ -360,9 +360,15 @@ void generate_posts_iterator::increment() DEBUG("generate.post", "The post we intend to parse:\n" << buf.str()); - std::istringstream in(buf.str()); try { - if (session.journal->parse(in, session) != 0) { + shared_ptr in(new std::istringstream(buf.str())); + + parse_context_stack_t parsing_context; + parsing_context.push(in); + parsing_context.get_current().journal = session.journal.get(); + parsing_context.get_current().scope = &session; + + if (session.journal->read(parsing_context) != 0) { VERIFY(session.journal->xacts.back()->valid()); posts.reset(*session.journal->xacts.back()); post = *posts++; diff --git a/src/global.cc b/src/global.cc index ee921fc5..5b7bb1c1 100644 --- a/src/global.cc +++ b/src/global.cc @@ -112,9 +112,12 @@ void global_scope_t::read_init() if (exists(init_file)) { TRACE_START(init, 1, "Read initialization file"); - ifstream init(init_file); + parse_context_stack_t parsing_context; + parsing_context.push(init_file); + parsing_context.get_current().journal = session().journal.get(); + parsing_context.get_current().scope = &report(); - if (session().journal->read(init_file, NULL, &report()) > 0 || + if (session().journal->read(parsing_context) > 0 || session().journal->auto_xacts.size() > 0 || session().journal->period_xacts.size() > 0) { throw_(parse_error, _("Transactions found in initialization file '%1'") diff --git a/src/journal.cc b/src/journal.cc index 2ebe90fb..55c89dd9 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -32,6 +32,7 @@ #include #include "journal.h" +#include "context.h" #include "amount.h" #include "commodity.h" #include "pool.h" @@ -47,6 +48,7 @@ journal_t::journal_t() initialize(); } +#if 0 journal_t::journal_t(const path& pathname) { TRACE_CTOR(journal_t, "path"); @@ -60,6 +62,7 @@ journal_t::journal_t(const string& str) initialize(); read(str); } +#endif journal_t::~journal_t() { @@ -87,6 +90,7 @@ void journal_t::initialize() fixed_payees = false; fixed_commodities = false; fixed_metadata = false; + current_context = NULL; was_loaded = false; force_checking = false; check_payees = false; @@ -114,7 +118,6 @@ account_t * journal_t::find_account_re(const string& regexp) } account_t * journal_t::register_account(const string& name, post_t * post, - const string& location, account_t * master_account) { account_t * result = NULL; @@ -147,8 +150,8 @@ account_t * journal_t::register_account(const string& name, post_t * post, result->add_flags(ACCOUNT_KNOWN); } else if (checking_style == CHECK_WARNING) { - warning_(_("%1Unknown account '%2'") << location - << result->fullname()); + current_context->warning(STR(_("Unknown account '%1'") + << result->fullname())); } else if (checking_style == CHECK_ERROR) { throw_(parse_error, _("Unknown account '%1'") << result->fullname()); @@ -159,8 +162,7 @@ account_t * journal_t::register_account(const string& name, post_t * post, return result; } -string journal_t::register_payee(const string& name, xact_t * xact, - const string& location) +string journal_t::register_payee(const string& name, xact_t * xact) { string payee; @@ -178,7 +180,7 @@ string journal_t::register_payee(const string& name, xact_t * xact, known_payees.insert(name); } else if (checking_style == CHECK_WARNING) { - warning_(_("%1Unknown payee '%2'") << location << name); + current_context->warning(STR(_("Unknown payee '%1'") << name)); } else if (checking_style == CHECK_ERROR) { throw_(parse_error, _("Unknown payee '%1'") << name); @@ -197,8 +199,7 @@ string journal_t::register_payee(const string& name, xact_t * xact, } void journal_t::register_commodity(commodity_t& comm, - variant context, - const string& location) + variant context) { if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { if (! comm.has_flags(COMMODITY_KNOWN)) { @@ -215,7 +216,7 @@ void journal_t::register_commodity(commodity_t& comm, comm.add_flags(COMMODITY_KNOWN); } else if (checking_style == CHECK_WARNING) { - warning_(_("%1Unknown commodity '%2'") << location << comm); + current_context->warning(STR(_("Unknown commodity '%1'") << comm)); } else if (checking_style == CHECK_ERROR) { throw_(parse_error, _("Unknown commodity '%1'") << comm); @@ -224,40 +225,8 @@ void journal_t::register_commodity(commodity_t& comm, } } -namespace { - void check_metadata(journal_t& journal, - const string& key, const value_t& value, - variant context, - const string& location) - { - std::pair range = - journal.tag_check_exprs.equal_range(key); - - for (tag_check_exprs_map::iterator i = range.first; - i != range.second; - ++i) { - value_scope_t val_scope - (context.which() == 1 ? - static_cast(*boost::get(context)) : - static_cast(*boost::get(context)), value); - - if (! (*i).second.first.calc(val_scope).to_boolean()) { - if ((*i).second.second == expr_t::EXPR_ASSERTION) - throw_(parse_error, - _("Metadata assertion failed for (%1: %2): %3") - << key << value << (*i).second.first); - else - warning_(_("%1Metadata check failed for (%2: %3): %4") - << location << key << value << (*i).second.first); - } - } - } -} - void journal_t::register_metadata(const string& key, const value_t& value, - variant context, - const string& location) + variant context) { if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { std::set::iterator i = known_tags.find(key); @@ -276,7 +245,7 @@ void journal_t::register_metadata(const string& key, const value_t& value, known_tags.insert(key); } else if (checking_style == CHECK_WARNING) { - warning_(_("%1Unknown metadata tag '%2'") << location << key); + current_context->warning(STR(_("Unknown metadata tag '%1'") << key)); } else if (checking_style == CHECK_ERROR) { throw_(parse_error, _("Unknown metadata tag '%1'") << key); @@ -284,8 +253,33 @@ void journal_t::register_metadata(const string& key, const value_t& value, } } - if (! value.is_null()) - check_metadata(*this, key, value, context, location); + if (! value.is_null()) { + std::pair range = + tag_check_exprs.equal_range(key); + + for (tag_check_exprs_map::iterator i = range.first; + i != range.second; + ++i) { + bind_scope_t bound_scope + (*current_context->scope, + context.which() == 1 ? + static_cast(*boost::get(context)) : + static_cast(*boost::get(context))); + value_scope_t val_scope(bound_scope, value); + + if (! (*i).second.first.calc(val_scope).to_boolean()) { + if ((*i).second.second == expr_t::EXPR_ASSERTION) + throw_(parse_error, + _("Metadata assertion failed for (%1: %2): %3") + << key << value << (*i).second.first); + else + current_context->warning + (STR(_("Metadata check failed for (%1: %2): %3") + << key << value << (*i).second.first)); + } + } + } } namespace { @@ -300,13 +294,10 @@ namespace { xact ? *xact->metadata : *post->metadata) { const string& key(pair.first); - // jww (2012-02-27): We really need to know the parsing context, - // both here and for the call to warning_ in - // xact_t::extend_xact. if (optional value = pair.second.first) - journal.register_metadata(key, *value, context, ""); + journal.register_metadata(key, *value, context); else - journal.register_metadata(key, NULL_VALUE, context, ""); + journal.register_metadata(key, NULL_VALUE, context); } } } @@ -351,7 +342,7 @@ bool journal_t::add_xact(xact_t * xact) void journal_t::extend_xact(xact_base_t * xact) { foreach (auto_xact_t * auto_xact, auto_xacts) - auto_xact->extend_xact(*xact); + auto_xact->extend_xact(*xact, *current_context); } bool journal_t::remove_xact(xact_t * xact) @@ -372,25 +363,36 @@ bool journal_t::remove_xact(xact_t * xact) return true; } -std::size_t journal_t::read(std::istream& in, - const path& pathname, - account_t * master_alt, - scope_t * scope) +std::size_t journal_t::read(parse_context_stack_t& context) { std::size_t count = 0; try { - if (! scope) - scope = scope_t::default_scope; + parse_context_t& current(context.get_current()); + current_context = ¤t; + + current.count = 0; + if (! current.scope) + current.scope = scope_t::default_scope; - if (! scope) + if (! current.scope) throw_(std::runtime_error, _("No default scope in which to read journal file '%1'") - << pathname); + << current.pathname); - count = parse(in, *scope, master_alt ? master_alt : master, &pathname); + if (! current.master) + current.master = master; + + count = read_textual(context); + if (count > 0) { + if (! current.pathname.empty()) + sources.push_back(fileinfo_t(current.pathname)); + else + sources.push_back(fileinfo_t()); + } } catch (...) { clear_xdata(); + current_context = NULL; throw; } @@ -402,23 +404,6 @@ std::size_t journal_t::read(std::istream& in, return count; } -std::size_t journal_t::read(const path& pathname, - account_t * master_account, - scope_t * scope) -{ - path filename = resolve_path(pathname); - - if (! exists(filename)) - throw_(std::runtime_error, - _("Cannot read journal file %1") << filename); - - ifstream stream(filename); - std::size_t count = read(stream, filename, master_account, scope); - if (count > 0) - sources.push_back(fileinfo_t(filename)); - return count; -} - bool journal_t::has_xdata() { foreach (xact_t * xact, xacts) diff --git a/src/journal.h b/src/journal.h index 6d10ffda..8b750993 100644 --- a/src/journal.h +++ b/src/journal.h @@ -55,7 +55,8 @@ class auto_xact_t; class period_xact_t; class post_t; class account_t; -class scope_t; +class parse_context_t; +class parse_context_stack_t; typedef std::list xacts_list; typedef std::list auto_xacts_list; @@ -132,6 +133,7 @@ public: account_mappings_t payees_for_unknown_accounts; checksum_map_t checksum_map; tag_check_exprs_map tag_check_exprs; + parse_context_t * current_context; bool was_loaded; bool force_checking; bool check_payees; @@ -143,8 +145,10 @@ public: } checking_style; journal_t(); +#if 0 journal_t(const path& pathname); journal_t(const string& str); +#endif ~journal_t(); void initialize(); @@ -162,16 +166,12 @@ public: account_t * find_account_re(const string& regexp); account_t * register_account(const string& name, post_t * post, - const string& location, account_t * master = NULL); - string register_payee(const string& name, xact_t * xact, - const string& location); + string register_payee(const string& name, xact_t * xact); void register_commodity(commodity_t& comm, - variant context, - const string& location); + variant context); void register_metadata(const string& key, const value_t& value, - variant context, - const string& location); + variant context); bool add_xact(xact_t * xact); void extend_xact(xact_base_t * xact); @@ -196,24 +196,16 @@ public: return period_xacts.end(); } - std::size_t read(std::istream& in, - const path& pathname, - account_t * master = NULL, - scope_t * scope = NULL); - std::size_t read(const path& pathname, - account_t * master = NULL, - scope_t * scope = NULL); - - std::size_t parse(std::istream& in, - scope_t& session_scope, - account_t * master = NULL, - const path * original_file = NULL); + std::size_t read(parse_context_stack_t& context); bool has_xdata(); void clear_xdata(); bool valid() const; +private: + std::size_t read_textual(parse_context_stack_t& context); + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ diff --git a/src/precmd.cc b/src/precmd.cc index 6f8becb6..6b106a8b 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -83,8 +83,14 @@ namespace { out << _("--- Context is first posting of the following transaction ---") << std::endl << str << std::endl; { - std::istringstream in(str); - report.session.journal->parse(in, report.session); + shared_ptr in(new std::istringstream(str)); + + parse_context_stack_t parsing_context; + parsing_context.push(in); + parsing_context.get_current().journal = report.session.journal.get(); + parsing_context.get_current().scope = &report.session; + + report.session.journal->read(parsing_context); report.session.journal->clear_xdata(); } } diff --git a/src/py_commodity.cc b/src/py_commodity.cc index 11ebe844..ffa903f4 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -113,7 +113,7 @@ namespace { if (i == pool.commodities.end()) { PyErr_SetString(PyExc_ValueError, (string("Could not find commodity ") + symbol).c_str()); - throw boost::python::error_already_set(); + throw_error_already_set(); } return (*i).second; } diff --git a/src/py_journal.cc b/src/py_journal.cc index 4f5427f5..a72b8528 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -135,10 +135,12 @@ namespace { return journal.find_account(name, auto_create); } +#if 0 std::size_t py_read(journal_t& journal, const string& pathname) { - return journal.read(pathname); + return journal.read(context_stack); } +#endif struct collector_wrapper { @@ -264,9 +266,10 @@ void export_journal() ; class_< journal_t, boost::noncopyable > ("Journal") +#if 0 .def(init()) .def(init()) - +#endif .add_property("master", make_getter(&journal_t::master, return_internal_reference<1, @@ -311,9 +314,9 @@ void export_journal() (&journal_t::period_xacts_begin, &journal_t::period_xacts_end)) .def("sources", python::range > (&journal_t::sources_begin, &journal_t::sources_end)) - +#if 0 .def("read", py_read) - +#endif .def("has_xdata", &journal_t::has_xdata) .def("clear_xdata", &journal_t::clear_xdata) diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 44bea2cd..de9c94cb 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -194,18 +194,19 @@ object python_interpreter_t::import_option(const string& str) if (! is_initialized) initialize(); - path file(str); - string name(str); - python::object sys_module = python::import("sys"); python::object sys_dict = sys_module.attr("__dict__"); + path file(str); + string name(str); python::list paths(sys_dict["path"]); if (contains(str, ".py")) { #if BOOST_VERSION >= 103700 - path& cwd(get_parsing_context().current_directory); - paths.insert(0, filesystem::absolute(file, cwd).parent_path().string()); + path& cwd(parsing_context.get_current().current_directory); + path parent(filesystem::absolute(file, cwd).parent_path()); + DEBUG("python.interp", "Adding " << parent << " to PYTHONPATH"); + paths.insert(0, parent.string()); sys_dict["path"] = paths; #if BOOST_VERSION >= 104600 @@ -220,7 +221,24 @@ object python_interpreter_t::import_option(const string& str) #endif // BOOST_VERSION >= 103700 } - return python::import(python::str(name.c_str())); + try { + if (contains(str, ".py")) { + import_into_main(name); + } else { + object obj = python::import(python::str(name.c_str())); + main_nspace[name.c_str()] = obj; + return obj; + } + } + catch (const error_already_set&) { + PyErr_Print(); + throw_(std::runtime_error, _("Python failed to import: %1") << str); + } + catch (...) { + throw; + } + + return object(); } object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode) @@ -348,13 +366,13 @@ value_t python_interpreter_t::server_command(call_scope_t& args) functor_t func(main_function, "main"); try { func(args); + return true; } catch (const error_already_set&) { PyErr_Print(); throw_(std::runtime_error, _("Error while invoking ledger.server's main() function")); } - return true; } else { throw_(std::runtime_error, _("The ledger.server module is missing its main() function!")); @@ -455,6 +473,7 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args) if (! PyCallable_Check(func.ptr())) { extract val(func); + DEBUG("python.interp", "Value of Python '" << name << "': " << val); std::signal(SIGINT, sigint_handler); if (val.check()) return val(); @@ -476,6 +495,8 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args) value_t result; if (xval.check()) { result = xval(); + DEBUG("python.interp", + "Return from Python '" << name << "': " << result); Py_DECREF(val); } else { Py_DECREF(val); diff --git a/src/scope.h b/src/scope.h index 6fcd67e9..c42ec66b 100644 --- a/src/scope.h +++ b/src/scope.h @@ -664,7 +664,7 @@ public: if (name == "value") return MAKE_FUNCTOR(value_scope_t::get_value); - return NULL; + return child_scope_t::lookup(kind, name); } }; diff --git a/src/session.cc b/src/session.cc index 9d994a9b..e07026b6 100644 --- a/src/session.cc +++ b/src/session.cc @@ -120,8 +120,17 @@ std::size_t session_t::read_data(const string& master_account) #endif // HAVE_BOOST_SERIALIZATION if (price_db_path) { if (exists(*price_db_path)) { - if (journal->read(*price_db_path) > 0) - throw_(parse_error, _("Transactions not allowed in price history file")); + parsing_context.push(*price_db_path); + parsing_context.get_current().journal = journal.get(); + try { + if (journal->read(parsing_context) > 0) + throw_(parse_error, _("Transactions not allowed in price history file")); + } + catch (...) { + parsing_context.pop(); + throw; + } + parsing_context.pop(); } } @@ -140,12 +149,22 @@ std::size_t session_t::read_data(const string& master_account) } buffer.flush(); - std::istringstream buf_in(buffer.str()); - xact_count += journal->read(buf_in, "/dev/stdin", acct); - journal->sources.push_back(journal_t::fileinfo_t()); + shared_ptr stream(new std::istringstream(buffer.str())); + parsing_context.push(stream); } else { - xact_count += journal->read(pathname, acct); + parsing_context.push(pathname); + } + + parsing_context.get_current().journal = journal.get(); + parsing_context.get_current().master = acct; + try { + xact_count += journal->read(parsing_context); + } + catch (...) { + parsing_context.pop(); + throw; } + parsing_context.pop(); } DEBUG("ledger.read", "xact_count [" << xact_count diff --git a/src/session.h b/src/session.h index 93bee8ba..54b9912a 100644 --- a/src/session.h +++ b/src/session.h @@ -44,6 +44,7 @@ #include "account.h" #include "journal.h" +#include "context.h" #include "option.h" #include "commodity.h" @@ -57,7 +58,9 @@ class session_t : public symbol_scope_t public: bool flush_on_next_data_file; + std::auto_ptr journal; + parse_context_stack_t parsing_context; explicit session_t(); virtual ~session_t() { diff --git a/src/textual.cc b/src/textual.cc index 15642cae..97c80e4f 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -32,6 +32,7 @@ #include #include "journal.h" +#include "context.h" #include "xact.h" #include "post.h" #include "account.h" @@ -53,8 +54,10 @@ namespace { struct application_t { string label; - variant value; + variant, account_t *, string, fixed_rate_t> value; + application_t(string _label, optional epoch) + : label(_label), value(epoch) {} application_t(string _label, account_t * acct) : label(_label), value(acct) {} application_t(string _label, string tag) @@ -63,24 +66,27 @@ namespace { : label(_label), value(rate) {} }; - class parse_context_t : public noncopyable + class instance_t : public noncopyable, public scope_t { + public: - std::list apply_stack; + parse_context_stack_t& context_stack; + parse_context_t& context; + std::istream& in; + instance_t * parent; - journal_t& journal; - scope_t& scope; + std::list apply_stack; #if defined(TIMELOG_SUPPORT) - time_log_t timelog; + time_log_t timelog; #endif - std::size_t count; - std::size_t errors; - std::size_t sequence; - - parse_context_t(journal_t& _journal, scope_t& _scope) - : journal(_journal), scope(_scope), timelog(journal, scope), - count(0), errors(0), sequence(1) { - timelog.context_count = &count; + + instance_t(parse_context_stack_t& _context_stack, + parse_context_t& _context, instance_t * _parent = NULL) + : context_stack(_context_stack), context(_context), + in(*context.stream.get()), parent(_parent), timelog(context) {} + + virtual string description() { + return _("textual parser"); } account_t * top_account() { @@ -90,40 +96,10 @@ namespace { return NULL; } - void close() { - timelog.close(); - } - }; - - class instance_t : public noncopyable, public scope_t - { - static const std::size_t MAX_LINE = 1024; - - public: - parse_context_t& context; - instance_t * parent; - const path * original_file; - path pathname; - std::istream& in; - char linebuf[MAX_LINE + 1]; - std::size_t linenum; - istream_pos_type line_beg_pos; - istream_pos_type curr_pos; - optional prev_epoch; - - instance_t(parse_context_t& _context, - std::istream& _in, - const path * _original_file = NULL, - instance_t * _parent = NULL); - - ~instance_t(); - - virtual string description() { - return _("textual parser"); - } - void parse(); + std::streamsize read_line(char *& line); + bool peek_whitespace_line() { return (in.good() && ! in.eof() && (in.peek() == ' ' || in.peek() == '\t')); @@ -229,37 +205,17 @@ namespace { } } -instance_t::instance_t(parse_context_t& _context, - std::istream& _in, - const path * _original_file, - instance_t * _parent) - : context(_context), parent(_parent), original_file(_original_file), - pathname(original_file ? *original_file : "/dev/stdin"), in(_in) -{ - TRACE_CTOR(instance_t, "..."); - DEBUG("times.epoch", "Saving epoch " << epoch); - prev_epoch = epoch; // declared in times.h -} - -instance_t::~instance_t() -{ - TRACE_DTOR(instance_t); - epoch = prev_epoch; - DEBUG("times.epoch", "Restored epoch to " << epoch); -} - void instance_t::parse() { - INFO("Parsing file '" << pathname.string() << "'"); + INFO("Parsing file " << context.pathname); - TRACE_START(instance_parse, 1, - "Done parsing file '" << pathname.string() << "'"); + TRACE_START(instance_parse, 1, "Done parsing file " << context.pathname); if (! in.good() || in.eof()) return; - linenum = 0; - curr_pos = in.tellg(); + context.linenum = 0; + context.curr_pos = in.tellg(); while (in.good() && ! in.eof()) { try { @@ -278,11 +234,9 @@ void instance_t::parse() foreach (instance_t * instance, instances) add_error_context(_("In file included from %1") - << file_context(instance->pathname, - instance->linenum)); + << instance->context.location()); } - add_error_context(_("While parsing file %1") - << file_context(pathname, linenum)); + add_error_context(_("While parsing file %1") << context.location()); if (caught_signal != NONE_CAUGHT) throw; @@ -307,26 +261,26 @@ std::streamsize instance_t::read_line(char *& line) assert(in.good()); assert(! in.eof()); // no one should call us in that case - line_beg_pos = curr_pos; + context.line_beg_pos = context.curr_pos; check_for_signal(); - in.getline(linebuf, MAX_LINE); + in.getline(context.linebuf, parse_context_t::MAX_LINE); std::streamsize len = in.gcount(); if (len > 0) { - if (linenum == 0 && utf8::is_bom(linebuf)) - line = &linebuf[3]; + if (context.linenum == 0 && utf8::is_bom(context.linebuf)) + line = &context.linebuf[3]; else - line = linebuf; + line = context.linebuf; if (line[len - 1] == '\r') // strip Windows CRLF down to LF line[--len] = '\0'; - linenum++; + context.linenum++; - curr_pos = line_beg_pos; - curr_pos += len; + context.curr_pos = context.line_beg_pos; + context.curr_pos += len; return len - 1; // LF is being silently dropped } @@ -446,19 +400,19 @@ void instance_t::clock_in_directive(char * line, bool /*capitalized*/) end = NULL; position_t position; - position.pathname = pathname; - position.beg_pos = line_beg_pos; - position.beg_line = linenum; - position.end_pos = curr_pos; - position.end_line = linenum; + position.pathname = context.pathname; + position.beg_pos = context.line_beg_pos; + position.beg_line = context.linenum; + position.end_pos = context.curr_pos; + position.end_line = context.linenum; position.sequence = context.sequence++; time_xact_t event(position, parse_datetime(datetime), - p ? context.top_account()->find_account(p) : NULL, + p ? top_account()->find_account(p) : NULL, n ? n : "", end ? end : ""); - context.timelog.clock_in(event); + timelog.clock_in(event); } void instance_t::clock_out_directive(char * line, bool /*capitalized*/) @@ -475,19 +429,19 @@ void instance_t::clock_out_directive(char * line, bool /*capitalized*/) end = NULL; position_t position; - position.pathname = pathname; - position.beg_pos = line_beg_pos; - position.beg_line = linenum; - position.end_pos = curr_pos; - position.end_line = linenum; + position.pathname = context.pathname; + position.beg_pos = context.line_beg_pos; + position.beg_line = context.linenum; + position.end_pos = context.curr_pos; + position.end_line = context.linenum; position.sequence = context.sequence++; time_xact_t event(position, parse_datetime(datetime), - p ? context.top_account()->find_account(p) : NULL, + p ? top_account()->find_account(p) : NULL, n ? n : "", end ? end : ""); - context.timelog.clock_out(event); + timelog.clock_out(event); context.count++; } @@ -503,8 +457,8 @@ void instance_t::default_commodity_directive(char * line) void instance_t::default_account_directive(char * line) { - context.journal.bucket = context.top_account()->find_account(skip_ws(line + 1)); - context.journal.bucket->add_flags(ACCOUNT_KNOWN); + context.journal->bucket = top_account()->find_account(skip_ws(line + 1)); + context.journal->bucket->add_flags(ACCOUNT_KNOWN); } void instance_t::price_conversion_directive(char * line) @@ -543,13 +497,14 @@ void instance_t::option_directive(char * line) *p++ = '\0'; } - if (! process_option(pathname.string(), line + 2, context.scope, p, line)) + if (! process_option(context.pathname.string(), line + 2, + *context.scope, p, line)) throw_(option_error, _("Illegal option --%1") << line + 2); } void instance_t::automated_xact_directive(char * line) { - istream_pos_type pos= line_beg_pos; + istream_pos_type pos = context.line_beg_pos; bool reveal_context = true; @@ -562,9 +517,9 @@ void instance_t::automated_xact_directive(char * line) std::auto_ptr ae(new auto_xact_t(predicate_t(expr, keeper))); ae->pos = position_t(); - ae->pos->pathname = pathname; - ae->pos->beg_pos = line_beg_pos; - ae->pos->beg_line = linenum; + ae->pos->pathname = context.pathname; + ae->pos->beg_pos = context.line_beg_pos; + ae->pos->beg_line = context.linenum; ae->pos->sequence = context.sequence++; post_t * last_post = NULL; @@ -586,9 +541,9 @@ void instance_t::automated_xact_directive(char * line) item = ae.get(); // This is a trailing note, and possibly a metadata info tag - item->append_note(p + 1, context.scope, true); + item->append_note(p + 1, *context.scope, true); item->add_flags(ITEM_NOTE_ON_NEXT_LINE); - item->pos->end_pos = curr_pos; + item->pos->end_pos = context.curr_pos; item->pos->end_line++; // If there was no last_post yet, then deferred notes get applied to @@ -619,8 +574,7 @@ void instance_t::automated_xact_directive(char * line) reveal_context = false; if (post_t * post = - parse_post(p, len - (p - line), context.top_account(), - NULL, true)) { + parse_post(p, len - (p - line), top_account(), NULL, true)) { reveal_context = true; ae->add_post(post); last_post = post; @@ -629,18 +583,19 @@ void instance_t::automated_xact_directive(char * line) } } - context.journal.auto_xacts.push_back(ae.get()); + context.journal->auto_xacts.push_back(ae.get()); - ae->journal = &context.journal; - ae->pos->end_pos = curr_pos; - ae->pos->end_line = linenum; + ae->journal = context.journal; + ae->pos->end_pos = context.curr_pos; + ae->pos->end_line = context.linenum; ae.release(); } catch (const std::exception&) { if (reveal_context) { add_error_context(_("While parsing automated transaction:")); - add_error_context(source_context(pathname, pos, curr_pos, "> ")); + add_error_context(source_context(context.pathname, pos, + context.curr_pos, "> ")); } throw; } @@ -648,7 +603,7 @@ void instance_t::automated_xact_directive(char * line) void instance_t::period_xact_directive(char * line) { - istream_pos_type pos = line_beg_pos; + istream_pos_type pos = context.line_beg_pos; bool reveal_context = true; @@ -656,23 +611,23 @@ void instance_t::period_xact_directive(char * line) std::auto_ptr pe(new period_xact_t(skip_ws(line + 1))); pe->pos = position_t(); - pe->pos->pathname = pathname; - pe->pos->beg_pos = line_beg_pos; - pe->pos->beg_line = linenum; + pe->pos->pathname = context.pathname; + pe->pos->beg_pos = context.line_beg_pos; + pe->pos->beg_line = context.linenum; pe->pos->sequence = context.sequence++; reveal_context = false; - if (parse_posts(context.top_account(), *pe.get())) { + if (parse_posts(top_account(), *pe.get())) { reveal_context = true; - pe->journal = &context.journal; + pe->journal = context.journal; if (pe->finalize()) { - context.journal.extend_xact(pe.get()); - context.journal.period_xacts.push_back(pe.get()); + context.journal->extend_xact(pe.get()); + context.journal->period_xacts.push_back(pe.get()); - pe->pos->end_pos = curr_pos; - pe->pos->end_line = linenum; + pe->pos->end_pos = context.curr_pos; + pe->pos->end_line = context.linenum; pe.release(); } else { @@ -686,7 +641,8 @@ void instance_t::period_xact_directive(char * line) catch (const std::exception&) { if (reveal_context) { add_error_context(_("While parsing periodic transaction:")); - add_error_context(source_context(pathname, pos, curr_pos, "> ")); + add_error_context(source_context(context.pathname, pos, + context.curr_pos, "> ")); } throw; } @@ -696,10 +652,10 @@ void instance_t::xact_directive(char * line, std::streamsize len) { TRACE_START(xacts, 1, "Time spent handling transactions:"); - if (xact_t * xact = parse_xact(line, len, context.top_account())) { + if (xact_t * xact = parse_xact(line, len, top_account())) { std::auto_ptr manager(xact); - if (context.journal.add_xact(xact)) { + if (context.journal->add_xact(xact)) { manager.release(); // it's owned by the journal now context.count++; } @@ -721,12 +677,13 @@ void instance_t::include_directive(char * line) if (line[0] != '/' && line[0] != '\\' && line[0] != '~') { DEBUG("textual.include", "received a relative path"); - DEBUG("textual.include", "parent file path: " << pathname.string()); - string::size_type pos = pathname.string().rfind('/'); + DEBUG("textual.include", "parent file path: " << context.pathname); + string pathstr(context.pathname.string()); + string::size_type pos = pathstr.rfind('/'); if (pos == string::npos) - pos = pathname.string().rfind('\\'); + pos = pathstr.rfind('\\'); if (pos != string::npos) { - filename = path(string(pathname.string(), 0, pos + 1)) / line; + filename = path(string(pathstr, 0, pos + 1)) / line; DEBUG("textual.include", "normalized path: " << filename.string()); } else { filename = path(string(".")) / line; @@ -773,10 +730,24 @@ void instance_t::include_directive(char * line) string base = (*iter).leaf(); #endif // BOOST_VERSION >= 103700 if (glob.match(base)) { - path inner_file(*iter); - ifstream stream(inner_file); - instance_t instance(context, stream, &inner_file, this); - instance.parse(); + journal_t * journal = context.journal; + account_t * master = context.master; + + context_stack.push(*iter); + + context_stack.get_current().journal = journal; + context_stack.get_current().master = master; + try { + instance_t instance(context_stack, + context_stack.get_current(), this); + instance.parse(); + } + catch (...) { + context_stack.pop(); + throw; + } + context_stack.pop(); + files_found = true; } } @@ -805,8 +776,8 @@ void instance_t::apply_directive(char * line) void instance_t::apply_account_directive(char * line) { - if (account_t * acct = context.top_account()->find_account(line)) - context.apply_stack.push_front(application_t("account", acct)); + if (account_t * acct = top_account()->find_account(line)) + apply_stack.push_front(application_t("account", acct)); #if !defined(NO_ASSERTS) else assert("Failed to create account" == NULL); @@ -820,14 +791,14 @@ void instance_t::apply_tag_directive(char * line) if (tag.find(':') == string::npos) tag = string(":") + tag + ":"; - context.apply_stack.push_front(application_t("tag", tag)); + apply_stack.push_front(application_t("tag", tag)); } void instance_t::apply_rate_directive(char * line) { if (optional > price_point = commodity_pool_t::current_pool->parse_price_directive(trim_ws(line), true)) { - context.apply_stack.push_front + apply_stack.push_front (application_t("fixed", fixed_rate_t(price_point->first, price_point->second.price))); } else { @@ -837,11 +808,13 @@ void instance_t::apply_rate_directive(char * line) void instance_t::apply_year_directive(char * line) { - unsigned short year(lexical_cast(skip_ws(line + 1))); - DEBUG("times.epoch", "Setting current year to " << year); + apply_stack.push_front(application_t("year", epoch)); + // This must be set to the last day of the year, otherwise partial // dates like "11/01" will refer to last year's november, not the // current year. + unsigned short year(lexical_cast(skip_ws(line + 1))); + DEBUG("times.epoch", "Setting current year to " << year); epoch = datetime_t(date_t(year, 12, 31)); } @@ -850,28 +823,30 @@ void instance_t::end_apply_directive(char * kind) char * b = next_element(kind); string name(b ? b : " "); - if (context.apply_stack.size() <= 1) + if (apply_stack.size() <= 1) throw_(std::runtime_error, _("'end apply %1' found, but no enclosing 'apply %2' directive") << name << name); - if (name != " " && name != context.apply_stack.front().label) + if (name != " " && name != apply_stack.front().label) throw_(std::runtime_error, _("'end apply %1' directive does not match 'apply %2' directive") - << name << context.apply_stack.front().label); + << name << apply_stack.front().label); + + if (apply_stack.front().value.type() == typeid(optional)) + epoch = boost::get >(apply_stack.front().value); - context.apply_stack.pop_front(); + apply_stack.pop_front(); } void instance_t::account_directive(char * line) { - istream_pos_type beg_pos = line_beg_pos; - std::size_t beg_linenum = linenum; + istream_pos_type beg_pos = context.line_beg_pos; + std::size_t beg_linenum = context.linenum; char * p = skip_ws(line); account_t * account = - context.journal.register_account(p, NULL, file_context(pathname, linenum), - context.top_account()); + context.journal->register_account(p, NULL, top_account()); std::auto_ptr ae; while (peek_whitespace_line()) { @@ -900,7 +875,7 @@ void instance_t::account_directive(char * line) ae.reset(new auto_xact_t(pred)); ae->pos = position_t(); - ae->pos->pathname = pathname; + ae->pos->pathname = context.pathname; ae->pos->beg_pos = beg_pos; ae->pos->beg_line = beg_linenum; ae->pos->sequence = context.sequence++; @@ -916,7 +891,7 @@ void instance_t::account_directive(char * line) else if (keyword == "eval" || keyword == "expr") { // jww (2012-02-27): Make account into symbol scopes so that this // can be used to override definitions within the account. - bind_scope_t bound_scope(context.scope, *account); + bind_scope_t bound_scope(*context.scope, *account); expr_t(b).calc(bound_scope); } else if (keyword == "note") { @@ -925,11 +900,11 @@ void instance_t::account_directive(char * line) } if (ae.get()) { - context.journal.auto_xacts.push_back(ae.get()); + context.journal->auto_xacts.push_back(ae.get()); - ae->journal = &context.journal; + ae->journal = context.journal; ae->pos->end_pos = in.tellg(); - ae->pos->end_line = linenum; + ae->pos->end_line = context.linenum; ae.release(); } @@ -943,7 +918,7 @@ void instance_t::account_alias_directive(account_t * account, string alias) trim(alias); std::pair result = context.journal - .account_aliases.insert(accounts_map::value_type(alias, account)); + ->account_aliases.insert(accounts_map::value_type(alias, account)); assert(result.second); } @@ -957,26 +932,25 @@ void instance_t::alias_directive(char * line) *e++ = '\0'; e = skip_ws(e); - account_alias_directive(context.top_account()->find_account(e), b); + account_alias_directive(top_account()->find_account(e), b); } } void instance_t::account_payee_directive(account_t * account, string payee) { trim(payee); - context.journal.payees_for_unknown_accounts + context.journal->payees_for_unknown_accounts .push_back(account_mapping_t(mask_t(payee), account)); } void instance_t::account_default_directive(account_t * account) { - context.journal.bucket = account; + context.journal->bucket = account; } void instance_t::payee_directive(char * line) { - string payee = context.journal - .register_payee(line, NULL, file_context(pathname, linenum)); + string payee = context.journal->register_payee(line, NULL); while (peek_whitespace_line()) { read_line(line); @@ -994,7 +968,7 @@ void instance_t::payee_directive(char * line) void instance_t::payee_alias_directive(const string& payee, string alias) { trim(alias); - context.journal.payee_mappings + context.journal->payee_mappings .push_back(payee_mapping_t(mask_t(alias), payee)); } @@ -1006,8 +980,7 @@ void instance_t::commodity_directive(char * line) if (commodity_t * commodity = commodity_pool_t::current_pool->find_or_create(symbol)) { - context.journal.register_commodity(*commodity, 0, - file_context(pathname, linenum)); + context.journal->register_commodity(*commodity, 0); while (peek_whitespace_line()) { read_line(line); @@ -1067,8 +1040,7 @@ void instance_t::commodity_default_directive(commodity_t& comm) void instance_t::tag_directive(char * line) { char * p = skip_ws(line); - context.journal.register_metadata(p, NULL_VALUE, 0, - file_context(pathname, linenum)); + context.journal->register_metadata(p, NULL_VALUE, 0); while (peek_whitespace_line()) { read_line(line); @@ -1079,7 +1051,7 @@ void instance_t::tag_directive(char * line) char * b = next_element(q); string keyword(q); if (keyword == "assert" || keyword == "check") { - context.journal.tag_check_exprs.insert + context.journal->tag_check_exprs.insert (tag_check_exprs_map::value_type (string(p), expr_t::check_expr_pair(expr_t(b), @@ -1093,22 +1065,21 @@ void instance_t::tag_directive(char * line) void instance_t::eval_directive(char * line) { expr_t expr(line); - expr.calc(context.scope); + expr.calc(*context.scope); } void instance_t::assert_directive(char * line) { expr_t expr(line); - if (! expr.calc(context.scope).to_boolean()) + if (! expr.calc(*context.scope).to_boolean()) throw_(parse_error, _("Assertion failed: %1") << line); } void instance_t::check_directive(char * line) { expr_t expr(line); - if (! expr.calc(context.scope).to_boolean()) - warning_(_("%1Check failed: %2") - << file_context(pathname, linenum) << line); + if (! expr.calc(*context.scope).to_boolean()) + context.warning(STR(_("Check failed: %1") << line)); } void instance_t::comment_directive(char * line) @@ -1242,12 +1213,12 @@ post_t * instance_t::parse_post(char * line, post->xact = xact; // this could be NULL post->pos = position_t(); - post->pos->pathname = pathname; - post->pos->beg_pos = line_beg_pos; - post->pos->beg_line = linenum; + post->pos->pathname = context.pathname; + post->pos->beg_pos = context.line_beg_pos; + post->pos->beg_line = context.linenum; post->pos->sequence = context.sequence++; - char buf[MAX_LINE + 1]; + char buf[parse_context_t::MAX_LINE + 1]; std::strcpy(buf, line); std::streamsize beg = 0; @@ -1264,14 +1235,14 @@ post_t * instance_t::parse_post(char * line, case '*': post->set_state(item_t::CLEARED); p = skip_ws(p + 1); - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed the CLEARED flag"); break; case '!': post->set_state(item_t::PENDING); p = skip_ws(p + 1); - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed the PENDING flag"); break; } @@ -1294,25 +1265,23 @@ post_t * instance_t::parse_post(char * line, if ((*p == '[' && *(e - 1) == ']') || (*p == '(' && *(e - 1) == ')')) { post->add_flags(POST_VIRTUAL); - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed a virtual account name"); if (*p == '[') { post->add_flags(POST_MUST_BALANCE); - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Posting must balance"); } p++; e--; } string name(p, static_cast(e - p)); - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed account name " << name); post->account = - context.journal.register_account(name, post.get(), - file_context(pathname, linenum), - account); + context.journal->register_account(name, post.get(), account); // Parse the optional amount @@ -1323,16 +1292,15 @@ post_t * instance_t::parse_post(char * line, if (*next != '(') // indicates a value expression post->amount.parse(stream, PARSE_NO_REDUCE); else - parse_amount_expr(stream, context.scope, *post.get(), post->amount, + parse_amount_expr(stream, *context.scope, *post.get(), post->amount, PARSE_NO_REDUCE | PARSE_SINGLE | PARSE_NO_ASSIGN, defer_expr, &post->amount_expr); if (! post->amount.is_null() && post->amount.has_commodity()) { - context.journal.register_commodity(post->amount.commodity(), post.get(), - file_context(pathname, linenum)); + context.journal->register_commodity(post->amount.commodity(), post.get()); if (! post->amount.has_annotation()) { - foreach (application_t& state, context.apply_stack) { + foreach (application_t& state, apply_stack) { if (state.value.type() == typeid(fixed_rate_t)) { fixed_rate_t& rate(boost::get(state.value)); if (*rate.first == post->amount.commodity()) { @@ -1346,7 +1314,7 @@ post_t * instance_t::parse_post(char * line, } } - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "post amount = " << post->amount); if (stream.eof()) { @@ -1357,7 +1325,7 @@ post_t * instance_t::parse_post(char * line, // Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST) if (*next == '@') { - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Found a price indicator"); bool per_unit = true; @@ -1365,7 +1333,7 @@ post_t * instance_t::parse_post(char * line, if (*++next == '@') { per_unit = false; post->add_flags(POST_COST_IN_FULL); - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "And it's for a total price"); } @@ -1389,7 +1357,7 @@ post_t * instance_t::parse_post(char * line, if (*p != '(') // indicates a value expression post->cost->parse(cstream, PARSE_NO_MIGRATE); else - parse_amount_expr(cstream, context.scope, *post.get(), *post->cost, + parse_amount_expr(cstream, *context.scope, *post.get(), *post->cost, PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_ASSIGN); if (post->cost->sign() < 0) @@ -1412,9 +1380,9 @@ post_t * instance_t::parse_post(char * line, if (fixed_cost) post->add_flags(POST_COST_FIXATED); - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Total cost is " << *post->cost); - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Annotated amount is " << post->amount); if (cstream.eof()) @@ -1431,7 +1399,7 @@ post_t * instance_t::parse_post(char * line, // Parse the optional balance assignment if (xact && next && *next == '=') { - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Found a balance assignment indicator"); beg = static_cast(++next - line); @@ -1446,7 +1414,7 @@ post_t * instance_t::parse_post(char * line, if (*p != '(') // indicates a value expression post->assigned_amount->parse(stream, PARSE_NO_MIGRATE); else - parse_amount_expr(stream, context.scope, *post.get(), + parse_amount_expr(stream, *context.scope, *post.get(), *post->assigned_amount, PARSE_SINGLE | PARSE_NO_MIGRATE); @@ -1457,17 +1425,17 @@ post_t * instance_t::parse_post(char * line, throw parse_error(_("Balance assertion must evaluate to a constant")); } - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "POST assign: parsed amt = " << *post->assigned_amount); amount_t& amt(*post->assigned_amount); value_t account_total (post->account->amount().strip_annotations(keep_details_t())); + DEBUG("post.assign", "line " << context.linenum << ": " + << "account balance = " << account_total); DEBUG("post.assign", - "line " << linenum << ": " "account balance = " << account_total); - DEBUG("post.assign", - "line " << linenum << ": " "post amount = " << amt); + "line " << context.linenum << ": " << "post amount = " << amt); amount_t diff = amt; @@ -1487,9 +1455,9 @@ post_t * instance_t::parse_post(char * line, } DEBUG("post.assign", - "line " << linenum << ": " << "diff = " << diff); - DEBUG("textual.parse", - "line " << linenum << ": " << "POST assign: diff = " << diff); + "line " << context.linenum << ": " << "diff = " << diff); + DEBUG("textual.parse", "line " << context.linenum << ": " + << "POST assign: diff = " << diff); if (! diff.is_zero()) { if (! post->amount.is_null()) { @@ -1498,7 +1466,7 @@ post_t * instance_t::parse_post(char * line, throw_(parse_error, _("Balance assertion off by %1") << diff); } else { post->amount = diff; - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Overwrite null posting"); } } @@ -1515,9 +1483,9 @@ post_t * instance_t::parse_post(char * line, // Parse the optional note if (next && *next == ';') { - post->append_note(++next, context.scope, true); + post->append_note(++next, *context.scope, true); next = line + len; - DEBUG("textual.parse", "line " << linenum << ": " + DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed a posting note"); } @@ -1528,14 +1496,14 @@ post_t * instance_t::parse_post(char * line, _("Unexpected char '%1' (Note: inline math requires parentheses)") << *next); - post->pos->end_pos = curr_pos; - post->pos->end_line = linenum; + post->pos->end_pos = context.curr_pos; + post->pos->end_line = context.linenum; - if (! context.apply_stack.empty()) { - foreach (const application_t& state, context.apply_stack) + if (! apply_stack.empty()) { + foreach (const application_t& state, apply_stack) if (state.value.type() == typeid(string)) post->parse_tags(boost::get(state.value).c_str(), - context.scope, true); + *context.scope, true); } TRACE_STOP(post_details, 1); @@ -1587,9 +1555,9 @@ xact_t * instance_t::parse_xact(char * line, unique_ptr xact(new xact_t); xact->pos = position_t(); - xact->pos->pathname = pathname; - xact->pos->beg_pos = line_beg_pos; - xact->pos->beg_line = linenum; + xact->pos->pathname = context.pathname; + xact->pos->beg_pos = context.line_beg_pos; + xact->pos->beg_line = context.linenum; xact->pos->sequence = context.sequence++; bool reveal_context = true; @@ -1635,9 +1603,7 @@ xact_t * instance_t::parse_xact(char * line, if (next && *next) { char * p = next_element(next, true); - xact->payee = - context.journal.register_payee(next, xact.get(), - file_context(pathname, linenum)); + xact->payee = context.journal->register_payee(next, xact.get()); next = p; } else { xact->payee = _(""); @@ -1646,7 +1612,7 @@ xact_t * instance_t::parse_xact(char * line, // Parse the xact note if (next && *next == ';') - xact->append_note(++next, context.scope, false); + xact->append_note(++next, *context.scope, false); TRACE_STOP(xact_text, 1); @@ -1673,9 +1639,9 @@ xact_t * instance_t::parse_xact(char * line, if (*p == ';') { // This is a trailing note, and possibly a metadata info tag - item->append_note(p + 1, context.scope, true); + item->append_note(p + 1, *context.scope, true); item->add_flags(ITEM_NOTE_ON_NEXT_LINE); - item->pos->end_pos = curr_pos; + item->pos->end_pos = context.curr_pos; item->pos->end_line++; } else if ((remlen > 7 && *p == 'a' && @@ -1687,7 +1653,7 @@ xact_t * instance_t::parse_xact(char * line, const char c = *p; p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]); expr_t expr(p); - bind_scope_t bound_scope(context.scope, *item); + bind_scope_t bound_scope(*context.scope, *item); if (c == 'e') { expr.calc(bound_scope); } @@ -1695,8 +1661,7 @@ xact_t * instance_t::parse_xact(char * line, if (c == 'a') { throw_(parse_error, _("Transaction assertion failed: %1") << p); } else { - warning_(_("%1Transaction check failed: %2") - << file_context(pathname, linenum) << p); + context.warning(STR(_("Transaction check failed: %1") << p)); } } } @@ -1729,14 +1694,14 @@ xact_t * instance_t::parse_xact(char * line, } #endif - xact->pos->end_pos = curr_pos; - xact->pos->end_line = linenum; + xact->pos->end_pos = context.curr_pos; + xact->pos->end_line = context.linenum; - if (! context.apply_stack.empty()) { - foreach (const application_t& state, context.apply_stack) + if (! apply_stack.empty()) { + foreach (const application_t& state, apply_stack) if (state.value.type() == typeid(string)) xact->parse_tags(boost::get(state.value).c_str(), - context.scope, false); + *context.scope, false); } TRACE_STOP(xact_details, 1); @@ -1748,7 +1713,8 @@ xact_t * instance_t::parse_xact(char * line, if (reveal_context) { add_error_context(_("While parsing transaction:")); add_error_context(source_context(xact->pos->pathname, - xact->pos->beg_pos, curr_pos, "> ")); + xact->pos->beg_pos, + context.curr_pos, "> ")); } throw; } @@ -1757,26 +1723,18 @@ xact_t * instance_t::parse_xact(char * line, expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind, const string& name) { - return context.scope.lookup(kind, name); + return context.scope->lookup(kind, name); } -std::size_t journal_t::parse(std::istream& in, - scope_t& scope, - account_t * master_account, - const path * original_file) +std::size_t journal_t::read_textual(parse_context_stack_t& context_stack) { TRACE_START(parsing_total, 1, "Total time spent parsing text:"); - - parse_context_t context(*this, scope); - if (master_account || this->master) - context.apply_stack.push_front(application_t("account", - master_account ? - master_account : this->master)); - - instance_t instance(context, in, original_file); - instance.parse(); - context.close(); - + { + instance_t instance(context_stack, context_stack.get_current()); + instance.apply_stack.push_front + (application_t("account", context_stack.get_current().master)); + instance.parse(); + } TRACE_STOP(parsing_total, 1); // These tracers were started in textual.cc @@ -1787,10 +1745,10 @@ std::size_t journal_t::parse(std::istream& in, TRACE_FINISH(instance_parse, 1); // report per-instance timers TRACE_FINISH(parsing_total, 1); - if (context.errors > 0) - throw static_cast(context.errors); + if (context_stack.get_current().errors > 0) + throw static_cast(context_stack.get_current().errors); - return context.count; + return context_stack.get_current().count; } } // namespace ledger diff --git a/src/timelog.cc b/src/timelog.cc index 8d3d69c1..67ea1015 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -36,14 +36,14 @@ #include "post.h" #include "account.h" #include "journal.h" +#include "context.h" namespace ledger { namespace { void clock_out_from_timelog(std::list& time_xacts, time_xact_t out_event, - journal_t& journal, - scope_t& scope) + parse_context_t& context) { time_xact_t event; @@ -95,7 +95,7 @@ namespace { curr->pos = event.position; if (! event.note.empty()) - curr->append_note(event.note.c_str(), scope); + curr->append_note(event.note.c_str(), *context.scope); char buf[32]; std::sprintf(buf, "%lds", long((out_event.checkin - event.checkin) @@ -110,7 +110,7 @@ namespace { curr->add_post(post); event.account->add_post(post); - if (! journal.add_xact(curr.get())) + if (! context.journal->add_xact(curr.get())) throw parse_error(_("Failed to record 'out' timelog transaction")); else curr.release(); @@ -129,9 +129,8 @@ void time_log_t::close() DEBUG("timelog", "Clocking out from account " << account->fullname()); clock_out_from_timelog(time_xacts, time_xact_t(none, CURRENT_TIME(), account), - journal, scope); - if (context_count) - (*context_count)++; + context); + context.count++; } assert(time_xacts.empty()); } @@ -154,7 +153,7 @@ void time_log_t::clock_out(time_xact_t event) if (time_xacts.empty()) throw std::logic_error(_("Timelog check-out event without a check-in")); - clock_out_from_timelog(time_xacts, event, journal, scope); + clock_out_from_timelog(time_xacts, event, context); } } // namespace ledger diff --git a/src/timelog.h b/src/timelog.h index 12083302..ed5a2d36 100644 --- a/src/timelog.h +++ b/src/timelog.h @@ -50,6 +50,7 @@ namespace ledger { class account_t; class journal_t; +class parse_context_t; class time_xact_t { @@ -86,15 +87,11 @@ public: class time_log_t : public boost::noncopyable { std::list time_xacts; - journal_t& journal; - scope_t& scope; + parse_context_t& context; public: - std::size_t * context_count; - - time_log_t(journal_t& _journal, scope_t& _scope) - : journal(_journal), scope(_scope), context_count(NULL) { - TRACE_CTOR(time_log_t, "journal_t&, scope_t&, std::size&"); + time_log_t(parse_context_t& _context) : context(_context) { + TRACE_CTOR(time_log_t, "parse_context_t&"); } ~time_log_t() { TRACE_DTOR(time_log_t); diff --git a/src/xact.cc b/src/xact.cc index 0fedb42a..0251edde 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -35,6 +35,7 @@ #include "post.h" #include "account.h" #include "journal.h" +#include "context.h" #include "pool.h" namespace ledger { @@ -608,7 +609,7 @@ namespace { } } -void auto_xact_t::extend_xact(xact_base_t& xact) +void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) { posts_list initial_posts(xact.posts.begin(), xact.posts.end()); @@ -674,7 +675,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact) throw_(parse_error, _("Transaction assertion failed: %1") << pair.first); else - warning_(_("Transaction check failed: %1") << pair.first); + context.warning(STR(_("Transaction check failed: %1") + << pair.first)); } } } diff --git a/src/xact.h b/src/xact.h index cb7bdeb3..7d7fb826 100644 --- a/src/xact.h +++ b/src/xact.h @@ -49,6 +49,7 @@ namespace ledger { class post_t; class journal_t; +class parse_context_t; typedef std::list posts_list; @@ -209,7 +210,7 @@ public: deferred_notes->push_back(deferred_tag_data_t(p, overwrite_existing)); } - virtual void extend_xact(xact_base_t& xact); + virtual void extend_xact(xact_base_t& xact, parse_context_t& context); #if defined(HAVE_BOOST_SERIALIZATION) private: diff --git a/test/baseline/dir-tag.test b/test/baseline/dir-tag.test index b1858146..496334a0 100644 --- a/test/baseline/dir-tag.test +++ b/test/baseline/dir-tag.test @@ -17,5 +17,5 @@ test reg 12-Feb-28 KFC food $20.00 $20.00 Assets:Cash $-20.00 0 __ERROR__ -Warning: Metadata check failed for (Happy: Summer): (value == "Valley") +Warning: "/Users/johnw/Projects/ledger/test/baseline/dir-tag.test", line 8: Metadata check failed for (Happy: Summer): (value == "Valley") end test diff --git a/test/baseline/feat-check.test b/test/baseline/feat-check.test index 9a2e72df..a9db1ec4 100644 --- a/test/baseline/feat-check.test +++ b/test/baseline/feat-check.test @@ -13,6 +13,6 @@ test bal -------------------- 0 __ERROR__ -Warning: Transaction check failed: (account =~ /Foo/) +Warning: "$sourcepath/test/baseline/feat-check.test", line 6: Transaction check failed: (account =~ /Foo/) Warning: "$sourcepath/test/baseline/feat-check.test", line 8: Check failed: account("Assets:Checking").all(account =~ /Expense/) end test -- cgit v1.2.3 From 9ec9cdf41e5176f7fcf06da5f75593d9ba3d4028 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 05:50:07 -0600 Subject: Started writing Python unit tests --- src/error.h | 6 +++ src/global.h | 2 +- src/main.cc | 7 ++-- src/py_amount.cc | 2 +- src/py_commodity.cc | 40 +++++++++----------- src/py_journal.cc | 93 ++++++++++++++++++++++------------------------ src/py_post.cc | 2 +- src/py_session.cc | 76 +++++++++++++++++++++++++++++++++++++ src/py_xact.cc | 8 ++++ src/pyinterp.cc | 2 + src/session.cc | 39 +++++++++++++++++-- src/session.h | 4 +- src/system.hh.in | 2 + src/textual.cc | 2 +- src/utils.cc | 3 +- test/python/JournalTest.py | 25 ++++++++----- tools/Makefile.am | 1 + 17 files changed, 220 insertions(+), 94 deletions(-) create mode 100644 src/py_session.cc (limited to 'src') diff --git a/src/error.h b/src/error.h index 7630f017..86d9de76 100644 --- a/src/error.h +++ b/src/error.h @@ -101,6 +101,12 @@ string source_context(const path& file, virtual ~name() throw() {} \ } +struct error_count { + std::size_t count; + explicit error_count(std::size_t _count) : count(_count) {} + const char * what() const { return ""; } +}; + } // namespace ledger #endif // _ERROR_H diff --git a/src/global.h b/src/global.h index 28bffc3a..2cb7842e 100644 --- a/src/global.h +++ b/src/global.h @@ -155,7 +155,7 @@ See LICENSE file included with the distribution for details and disclaimer."); OPTION_(global_scope_t, version, DO() { // -v parent->show_version_info(std::cout); - throw int(0); // exit immediately + throw error_count(0); // exit immediately }); }; diff --git a/src/main.cc b/src/main.cc index 2202a5de..aafbdbcb 100644 --- a/src/main.cc +++ b/src/main.cc @@ -191,9 +191,10 @@ int main(int argc, char * argv[], char * envp[]) std::cerr << "Exception during initialization: " << err.what() << std::endl; } - catch (int _status) { - status = _status; // used for a "quick" exit, and is used only - // if help text (such as --help) was displayed + catch (const error_count& errors) { + // used for a "quick" exit, and is used only if help text (such as + // --help) was displayed + status = static_cast(errors.count); } // If memory verification is being performed (which can be very slow), clean diff --git a/src/py_amount.cc b/src/py_amount.cc index f10595e8..25ec8e26 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -263,7 +263,7 @@ internal precision.")) .add_property("commodity", make_function(&amount_t::commodity, - return_value_policy()), + return_internal_reference<>()), make_function(&amount_t::set_commodity, with_custodian_and_ward<1, 2>())) .def("has_commodity", &amount_t::has_commodity) diff --git a/src/py_commodity.cc b/src/py_commodity.cc index ffa903f4..b5230850 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -269,18 +269,14 @@ void export_commodity() .def("make_qualified_name", &commodity_pool_t::make_qualified_name) - .def("create", py_create_1, - return_value_policy()) - .def("create", py_create_2, - return_value_policy()) + .def("create", py_create_1, return_internal_reference<>()) + .def("create", py_create_2, return_internal_reference<>()) - .def("find_or_create", py_find_or_create_1, - return_value_policy()) - .def("find_or_create", py_find_or_create_2, - return_value_policy()) + .def("find_or_create", py_find_or_create_1, return_internal_reference<>()) + .def("find_or_create", py_find_or_create_2, return_internal_reference<>()) - .def("find", py_find_1, return_value_policy()) - .def("find", py_find_2, return_value_policy()) + .def("find", py_find_1, return_internal_reference<>()) + .def("find", py_find_2, return_internal_reference<>()) .def("exchange", py_exchange_2, with_custodian_and_ward<1, 2>()) .def("exchange", py_exchange_3, with_custodian_and_ward<1, 2>()) @@ -288,23 +284,23 @@ void export_commodity() .def("parse_price_directive", &commodity_pool_t::parse_price_directive) .def("parse_price_expression", &commodity_pool_t::parse_price_expression, - return_value_policy()) + return_internal_reference<>()) .def("__getitem__", py_pool_getitem, - return_value_policy()) + return_internal_reference<>()) .def("keys", py_pool_keys) .def("has_key", py_pool_contains) .def("__contains__", py_pool_contains) .def("__iter__", - python::range > + python::range > (py_pool_commodities_begin, py_pool_commodities_end)) .def("iteritems", - python::range > + python::range > (py_pool_commodities_begin, py_pool_commodities_end)) .def("iterkeys", python::range<>(py_pool_commodities_keys_begin, py_pool_commodities_keys_end)) .def("itervalues", - python::range > + python::range > (py_pool_commodities_values_begin, py_pool_commodities_values_end)) ; @@ -349,17 +345,17 @@ void export_commodity() .add_property("referent", make_function(py_commodity_referent, - return_value_policy())) + return_internal_reference<>())) .def("has_annotation", &commodity_t::has_annotation) .def("strip_annotations", py_strip_annotations_0, - return_value_policy()) + return_internal_reference<>()) .def("strip_annotations", py_strip_annotations_1, - return_value_policy()) + return_internal_reference<>()) .def("write_annotations", &commodity_t::write_annotations) .def("pool", &commodity_t::pool, - return_value_policy()) + return_internal_reference<>()) .add_property("base_symbol", &commodity_t::base_symbol) .add_property("symbol", &commodity_t::symbol) @@ -441,12 +437,12 @@ void export_commodity() .add_property("referent", make_function(py_annotated_commodity_referent, - return_value_policy())) + return_internal_reference<>())) .def("strip_annotations", py_strip_ann_annotations_0, - return_value_policy()) + return_internal_reference<>()) .def("strip_annotations", py_strip_ann_annotations_1, - return_value_policy()) + return_internal_reference<>()) .def("write_annotations", &annotated_commodity_t::write_annotations) ; } diff --git a/src/py_journal.cc b/src/py_journal.cc index a72b8528..550fb14e 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -144,10 +144,10 @@ namespace { struct collector_wrapper { - journal_t& journal; - report_t report; - collect_posts * posts_collector; - post_handler_ptr chain; + journal_t& journal; + report_t report; + + post_handler_ptr posts_collector; collector_wrapper(journal_t& _journal, report_t& base) : journal(_journal), report(base), @@ -157,31 +157,32 @@ namespace { } std::size_t length() const { - return posts_collector->length(); + return dynamic_cast(posts_collector.get())->length(); } std::vector::iterator begin() { - return posts_collector->begin(); + return dynamic_cast(posts_collector.get())->begin(); } std::vector::iterator end() { - return posts_collector->end(); + return dynamic_cast(posts_collector.get())->end(); } }; - shared_ptr - py_collect(journal_t& journal, const string& query) + shared_ptr py_query(journal_t& journal, + const string& query) { if (journal.has_xdata()) { PyErr_SetString(PyExc_RuntimeError, - _("Cannot have multiple journal collections open at once")); + _("Cannot have more than one active journal query")); throw_error_already_set(); } report_t& current_report(downcast(*scope_t::default_scope)); - shared_ptr coll(new collector_wrapper(journal, - current_report)); - unique_ptr save_journal(current_report.session.journal.release()); - current_report.session.journal.reset(&journal); + shared_ptr + coll(new collector_wrapper(journal, current_report)); + + unique_ptr save_journal(coll->report.session.journal.release()); + coll->report.session.journal.reset(&coll->journal); try { strings_list remaining = @@ -191,60 +192,48 @@ namespace { value_t args; foreach (const string& arg, remaining) args.push_back(string_value(arg)); - coll->report.parse_query_args(args, "@Journal.collect"); + coll->report.parse_query_args(args, "@Journal.query"); - journal_posts_iterator walker(coll->journal); - coll->chain = - chain_post_handlers(post_handler_ptr(coll->posts_collector), - coll->report); - pass_down_posts(coll->chain, walker); + coll->report.posts_report(coll->posts_collector); } catch (...) { - current_report.session.journal.release(); - current_report.session.journal.reset(save_journal.release()); + coll->report.session.journal.release(); + coll->report.session.journal.reset(save_journal.release()); throw; } - current_report.session.journal.release(); - current_report.session.journal.reset(save_journal.release()); + coll->report.session.journal.release(); + coll->report.session.journal.reset(save_journal.release()); return coll; } post_t * posts_getitem(collector_wrapper& collector, long i) { - post_t * post = - collector.posts_collector->posts[static_cast(i)]; - std::cerr << typeid(post).name() << std::endl; - std::cerr << typeid(*post).name() << std::endl; - std::cerr << typeid(post->account).name() << std::endl; - std::cerr << typeid(*post->account).name() << std::endl; - return post; + return dynamic_cast(collector.posts_collector.get()) + ->posts[static_cast(i)]; } } // unnamed namespace +#define EXC_TRANSLATOR(type) \ + void exc_translate_ ## type(const type& err) { \ + PyErr_SetString(PyExc_RuntimeError, err.what()); \ + } + +EXC_TRANSLATOR(parse_error) +EXC_TRANSLATOR(error_count) + void export_journal() { class_< item_handler, shared_ptr >, boost::noncopyable >("PostHandler") ; - class_< collect_posts, bases >, - shared_ptr, boost::noncopyable >("PostCollector") - .def("__len__", &collect_posts::length) - .def("__iter__", python::range > > - (&collect_posts::begin, &collect_posts::end)) - ; - class_< collector_wrapper, shared_ptr, boost::noncopyable >("PostCollectorWrapper", no_init) .def("__len__", &collector_wrapper::length) - .def("__getitem__", posts_getitem, return_internal_reference<1, - with_custodian_and_ward_postcall<0, 1> >()) - .def("__iter__", - python::range > > + .def("__getitem__", posts_getitem, return_internal_reference<>()) + .def("__iter__", python::range > (&collector_wrapper::begin, &collector_wrapper::end)) ; @@ -286,13 +275,13 @@ void export_journal() .def("find_account", py_find_account_1, return_internal_reference<1, - with_custodian_and_ward_postcall<0, 1> >()) + with_custodian_and_ward_postcall<1, 0> >()) .def("find_account", py_find_account_2, return_internal_reference<1, - with_custodian_and_ward_postcall<0, 1> >()) + with_custodian_and_ward_postcall<1, 0> >()) .def("find_account_re", &journal_t::find_account_re, return_internal_reference<1, - with_custodian_and_ward_postcall<0, 1> >()) + with_custodian_and_ward_postcall<1, 0> >()) .def("add_xact", &journal_t::add_xact) .def("remove_xact", &journal_t::remove_xact) @@ -301,7 +290,7 @@ void export_journal() #if 0 .def("__getitem__", xacts_getitem, return_internal_reference<1, - with_custodian_and_ward_postcall<0, 1> >()) + with_custodian_and_ward_postcall<1, 0> >()) #endif .def("__iter__", python::range > @@ -320,10 +309,16 @@ void export_journal() .def("has_xdata", &journal_t::has_xdata) .def("clear_xdata", &journal_t::clear_xdata) - .def("collect", py_collect, with_custodian_and_ward_postcall<0, 1>()) + .def("query", py_query) .def("valid", &journal_t::valid) ; + +#define EXC_TRANSLATE(type) \ + register_exception_translator(&exc_translate_ ## type); + + EXC_TRANSLATE(parse_error); + EXC_TRANSLATE(error_count); } } // namespace ledger diff --git a/src/py_post.cc b/src/py_post.cc index bd599604..2789082e 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -116,7 +116,7 @@ void export_post() make_setter(&post_t::xdata_t::datetime)) .add_property("account", make_getter(&post_t::xdata_t::account, - return_value_policy()), + return_internal_reference<>()), make_setter(&post_t::xdata_t::account, with_custodian_and_ward<1, 2>())) .add_property("sort_values", diff --git a/src/py_session.cc b/src/py_session.cc new file mode 100644 index 00000000..f411d5e1 --- /dev/null +++ b/src/py_session.cc @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2003-2012, 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. + */ + +#include + +#include "pyinterp.h" +#include "pyutils.h" +#include "session.h" + +namespace ledger { + +using namespace boost::python; + +namespace { + journal_t * py_read_journal(const string& pathname) + { + return python_session->read_journal(path(pathname)); + } + + journal_t * py_read_journal_from_string(const string& data) + { + return python_session->read_journal_from_string(data); + } +} + +void export_session() +{ + class_< session_t, boost::noncopyable > ("Session") + .def("read_journal", &session_t::read_journal, + return_internal_reference<>()) + .def("read_journal_from_string", &session_t::read_journal_from_string, + return_internal_reference<>()) + .def("read_journal_files", &session_t::read_journal_files, + return_internal_reference<>()) + .def("close_journal_files", &session_t::close_journal_files) + ; + + scope().attr("session") = + object(ptr(static_cast(python_session.get()))); + scope().attr("read_journal") = + python::make_function(&py_read_journal, + return_internal_reference<>()); + scope().attr("read_journal_from_string") = + python::make_function(&py_read_journal_from_string, + return_internal_reference<>()); +} + +} // namespace ledger diff --git a/src/py_xact.cc b/src/py_xact.cc index 97d5df47..3d792c7b 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -76,6 +76,12 @@ namespace { return **elem; } + string py_xact_to_string(xact_t&) + { + // jww (2012-03-01): TODO + return empty_string; + } + } // unnamed namespace using namespace boost::python; @@ -110,6 +116,8 @@ void export_xact() .def("id", &xact_t::id) .def("seq", &xact_t::seq) + .def("__str__", py_xact_to_string) + .add_property("code", make_getter(&xact_t::code), make_setter(&xact_t::code)) diff --git a/src/pyinterp.cc b/src/pyinterp.cc index de9c94cb..4dbb7134 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -51,6 +51,7 @@ void export_commodity(); void export_expr(); void export_format(); void export_item(); +void export_session(); void export_journal(); void export_post(); void export_times(); @@ -72,6 +73,7 @@ void initialize_for_python() export_item(); export_post(); export_xact(); + export_session(); export_journal(); } diff --git a/src/session.cc b/src/session.cc index e07026b6..db01fbf6 100644 --- a/src/session.cc +++ b/src/session.cc @@ -89,8 +89,10 @@ std::size_t session_t::read_data(const string& master_account) std::size_t xact_count = 0; - account_t * acct = journal->master; - if (! master_account.empty()) + account_t * acct; + if (master_account.empty()) + acct = journal->master; + else acct = journal->find_account(master_account); optional price_db_path; @@ -185,7 +187,7 @@ std::size_t session_t::read_data(const string& master_account) return journal->xacts.size(); } -void session_t::read_journal_files() +journal_t * session_t::read_journal_files() { INFO_START(journal, "Read journal file"); @@ -203,6 +205,37 @@ void session_t::read_journal_files() #if defined(DEBUG_ON) INFO("Found " << count << " transactions"); #endif + + return journal.get(); +} + +journal_t * session_t::read_journal(const path& pathname) +{ + HANDLER(file_).data_files.clear(); + HANDLER(file_).data_files.push_back(pathname); + + return read_journal_files(); +} + +journal_t * session_t::read_journal_from_string(const string& data) +{ + HANDLER(file_).data_files.clear(); + + shared_ptr stream(new std::istringstream(data)); + parsing_context.push(stream); + + parsing_context.get_current().journal = journal.get(); + parsing_context.get_current().master = journal->master; + try { + journal->read(parsing_context); + } + catch (...) { + parsing_context.pop(); + throw; + } + parsing_context.pop(); + + return journal.get(); } void session_t::close_journal_files() diff --git a/src/session.h b/src/session.h index 54b9912a..38062b78 100644 --- a/src/session.h +++ b/src/session.h @@ -75,9 +75,11 @@ public: flush_on_next_data_file = truth; } + journal_t * read_journal(const path& pathname); + journal_t * read_journal_from_string(const string& data); std::size_t read_data(const string& master_account = ""); - void read_journal_files(); + journal_t * read_journal_files(); void close_journal_files(); value_t fn_account(call_scope_t& scope); diff --git a/src/system.hh.in b/src/system.hh.in index e14166b2..3aa60f71 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -257,4 +257,6 @@ void serialize(Archive& ar, istream_pos_type& pos, const unsigned int) #include #include +#include + #endif // HAVE_BOOST_PYTHON diff --git a/src/textual.cc b/src/textual.cc index 97c80e4f..2f09c063 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1746,7 +1746,7 @@ std::size_t journal_t::read_textual(parse_context_stack_t& context_stack) TRACE_FINISH(parsing_total, 1); if (context_stack.get_current().errors > 0) - throw static_cast(context_stack.get_current().errors); + throw error_count(context_stack.get_current().errors); return context_stack.get_current().count; } diff --git a/src/utils.cc b/src/utils.cc index 09526267..eb1d8009 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -612,8 +612,7 @@ optional _log_category_re; struct __maybe_enable_debugging { __maybe_enable_debugging() { - const char * p = std::getenv("LEDGER_DEBUG"); - if (p != NULL) { + if (const char * p = std::getenv("LEDGER_DEBUG")) { _log_level = LOG_DEBUG; _log_category = p; } diff --git a/test/python/JournalTest.py b/test/python/JournalTest.py index 66447f87..e65c671d 100644 --- a/test/python/JournalTest.py +++ b/test/python/JournalTest.py @@ -1,22 +1,27 @@ # -*- coding: utf-8 -*- import unittest -import exceptions -import operator from ledger import * -from StringIO import * -from datetime import * class JournalTestCase(unittest.TestCase): - def setUp(self): - pass - def tearDown(self): - pass + session.close_journal_files() + + def testBasicRead(self): + journal = read_journal_from_string(""" +2012-03-01 KFC + Expenses:Food $21.34 + Assets:Cash +""") + self.assertEqual(type(journal), Journal) + + for xact in journal: + self.assertEqual(xact.payee, "KFC") - def test_(self): - pass + for post in journal.query("food"): + self.assertEqual(str(post.account), "Expenses:Food") + self.assertEqual(post.amount, Amount("$21.34")) def suite(): return unittest.TestLoader().loadTestsFromTestCase(JournalTestCase) diff --git a/tools/Makefile.am b/tools/Makefile.am index fe0681e5..4fdd8393 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -226,6 +226,7 @@ libledger_python_la_SOURCES = \ src/py_expr.cc \ src/py_format.cc \ src/py_item.cc \ + src/py_session.cc \ src/py_journal.cc \ src/py_post.cc \ src/py_times.cc \ -- cgit v1.2.3 From e493a3859c7c854b79e772ffb1c0a69f6868ac98 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 15:09:39 -0600 Subject: Use filesystem::complete not absolute with Boost < 1.46 --- src/context.h | 4 ++++ src/pyinterp.cc | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/context.h b/src/context.h index 0533536f..d7999e5a 100644 --- a/src/context.h +++ b/src/context.h @@ -110,7 +110,11 @@ inline parse_context_t open_for_reading(const path& pathname, throw_(std::runtime_error, _("Cannot read journal file %1") << filename); +#if BOOST_VERSION >= 104600 && BOOST_FILESYSTEM_VERSION >= 3 path parent(filesystem::absolute(pathname, cwd).parent_path()); +#else + path parent(filesystem::complete(pathname, cwd).parent_path()); +#endif shared_ptr stream(new ifstream(filename)); parse_context_t context(stream, parent); context.pathname = filename; diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 4dbb7134..adcd0167 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -206,7 +206,11 @@ object python_interpreter_t::import_option(const string& str) if (contains(str, ".py")) { #if BOOST_VERSION >= 103700 path& cwd(parsing_context.get_current().current_directory); +#if BOOST_VERSION >= 104600 && BOOST_FILESYSTEM_VERSION >= 3 path parent(filesystem::absolute(file, cwd).parent_path()); +#else + path parent(filesystem::complete(file, cwd).parent_path()); +#endif DEBUG("python.interp", "Adding " << parent << " to PYTHONPATH"); paths.insert(0, parent.string()); sys_dict["path"] = paths; -- cgit v1.2.3 From ff89cb9c4de8240d7a7f79406755a86d8d2d5f18 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 16:22:08 -0600 Subject: Fixed a bug that was causing includes to crash Fixes #661 --- src/textual.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 2f09c063..4a384866 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -740,6 +740,7 @@ void instance_t::include_directive(char * line) try { instance_t instance(context_stack, context_stack.get_current(), this); + instance.apply_stack.push_front(application_t("account", master)); instance.parse(); } catch (...) { -- cgit v1.2.3 From f6c087cfe48e6410db61a9367ce7c718a490af77 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 17:32:51 -0600 Subject: Added a new 'python' directive --- src/py_value.cc | 2 +- src/pyinterp.cc | 77 +++++++++++++++++++++++++++------------ src/pyinterp.h | 5 ++- src/textual.cc | 62 +++++++++++++++++++++++++++++++ test/LedgerHarness.py | 4 ++ test/RegressTests.py | 4 +- test/baseline/dir-python_py.test | 26 +++++++++++++ test/baseline/feat-import_py.test | 23 ++++++++++++ test/baseline/featimport.py | 4 ++ tools/Makefile.am | 16 +++++--- 10 files changed, 190 insertions(+), 33 deletions(-) create mode 100644 test/baseline/dir-python_py.test create mode 100644 test/baseline/feat-import_py.test create mode 100644 test/baseline/featimport.py (limited to 'src') diff --git a/src/py_value.cc b/src/py_value.cc index 3b67c4c6..949f2a49 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -147,7 +147,7 @@ void export_value() .def(init()) .def(init()) .def(init()) - // jww (2009-11-02): Need to support conversion eof value_t::sequence_t + // jww (2009-11-02): Need to support conversion of value_t::sequence_t //.def(init()) .def(init()) diff --git a/src/pyinterp.cc b/src/pyinterp.cc index adcd0167..dc6fb4f7 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -32,6 +32,7 @@ #include #include "pyinterp.h" +#include "pyutils.h" #include "account.h" #include "xact.h" #include "post.h" @@ -103,7 +104,7 @@ void python_interpreter_t::initialize() hack_system_paths(); - object main_module = python::import("__main__"); + main_module = python::import("__main__"); if (! main_module) throw_(std::runtime_error, _("Python failed to initialize (couldn't find __main__)")); @@ -446,28 +447,56 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind, } namespace { - void append_value(list& lst, const value_t& value) + object convert_value_to_python(const value_t& val) { - if (value.is_scope()) { - const scope_t * scope = value.as_scope(); - if (const post_t * post = dynamic_cast(scope)) - lst.append(ptr(post)); - else if (const xact_t * xact = dynamic_cast(scope)) - lst.append(ptr(xact)); - else if (const account_t * account = - dynamic_cast(scope)) - lst.append(ptr(account)); - else if (const period_xact_t * period_xact = - dynamic_cast(scope)) - lst.append(ptr(period_xact)); - else if (const auto_xact_t * auto_xact = - dynamic_cast(scope)) - lst.append(ptr(auto_xact)); - else - throw_(std::logic_error, - _("Cannot downcast scoped object to specific type")); - } else { - lst.append(value); + switch (val.type()) { + case value_t::VOID: // a null value (i.e., uninitialized) + return object(); + case value_t::BOOLEAN: // a boolean + return object(val.to_boolean()); + case value_t::DATETIME: // a date and time (Boost posix_time) + return object(val.to_datetime()); + case value_t::DATE: // a date (Boost gregorian::date) + return object(val.to_date()); + case value_t::INTEGER: // a signed integer value + return object(val.to_long()); + case value_t::AMOUNT: // a ledger::amount_t + return object(val.as_amount()); + case value_t::BALANCE: // a ledger::balance_t + return object(val.as_balance()); + case value_t::STRING: // a string object + return object(handle<>(borrowed(str_to_py_unicode(val.as_string())))); + case value_t::MASK: // a regular expression mask + return object(handle<>(borrowed(str_to_py_unicode(val.as_mask().str())))); + case value_t::SEQUENCE: { // a vector of value_t objects + list arglist; + foreach (const value_t& elem, val.as_sequence()) + arglist.append(elem); + return arglist; + } + case value_t::SCOPE: // a pointer to a scope + if (const scope_t * scope = val.as_scope()) { + if (const post_t * post = dynamic_cast(scope)) + return object(ptr(post)); + else if (const xact_t * xact = dynamic_cast(scope)) + return object(ptr(xact)); + else if (const account_t * account = + dynamic_cast(scope)) + return object(ptr(account)); + else if (const period_xact_t * period_xact = + dynamic_cast(scope)) + return object(ptr(period_xact)); + else if (const auto_xact_t * auto_xact = + dynamic_cast(scope)) + return object(ptr(auto_xact)); + else + throw_(std::logic_error, + _("Cannot downcast scoped object to specific type")); + } + return object(); + case value_t::ANY: // a pointer to an arbitrary object + assert("Attempted to convert an Value.ANY object to Python" == NULL); + return object(); } } } @@ -491,9 +520,9 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args) // rather than a sequence of arguments? if (args.value().is_sequence()) foreach (const value_t& value, args.value().as_sequence()) - append_value(arglist, value); + arglist.append(convert_value_to_python(value)); else - append_value(arglist, args.value()); + arglist.append(convert_value_to_python(args.value())); if (PyObject * val = PyObject_CallObject(func.ptr(), python::tuple(arglist).ptr())) { diff --git a/src/pyinterp.h b/src/pyinterp.h index ae8dd9c2..c3397840 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -41,8 +41,9 @@ namespace ledger { class python_interpreter_t : public session_t { public: - python::dict main_nspace; - bool is_initialized; + python::object main_module; + python::dict main_nspace; + bool is_initialized; python_interpreter_t() : session_t(), main_nspace(), is_initialized(false) { diff --git a/src/textual.cc b/src/textual.cc index 4a384866..51b5849b 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -40,6 +40,9 @@ #include "query.h" #include "pstream.h" #include "pool.h" +#if defined(HAVE_BOOST_PYTHON) +#include "pyinterp.h" +#endif #define TIMELOG_SUPPORT 1 #if defined(TIMELOG_SUPPORT) @@ -104,6 +107,10 @@ namespace { return (in.good() && ! in.eof() && (in.peek() == ' ' || in.peek() == '\t')); } + bool peek_blank_line() { + return (in.good() && ! in.eof() && + (in.peek() == '\n' || in.peek() == '\r')); + } void read_next_directive(); @@ -157,6 +164,10 @@ namespace { void assert_directive(char * line); void check_directive(char * line); +#if defined(HAVE_BOOST_PYTHON) + void python_directive(char * line); +#endif + post_t * parse_post(char * line, std::streamsize len, account_t * account, @@ -1094,6 +1105,48 @@ void instance_t::comment_directive(char * line) } } +#if defined(HAVE_BOOST_PYTHON) +void instance_t::python_directive(char * line) +{ + std::ostringstream script; + + if (line) + script << skip_ws(line) << '\n'; + + std::size_t indent = 0; + + while (peek_whitespace_line() || peek_blank_line()) { + if (read_line(line) > 0) { + if (! indent) { + const char * p = line; + while (*p && std::isspace(*p)) { + ++indent; + ++p; + } + } + + const char * p = line; + for (std::size_t i = 0; i < indent; i++) { + if (std::isspace(*p)) + ++p; + else + break; + } + + if (*p) + script << p << '\n'; + } + } + + if (! python_session->is_initialized) + python_session->initialize(); + + python_session->main_nspace["journal"] = + python::object(python::ptr(context.journal)); + python_session->eval(script.str(), python_interpreter_t::PY_EVAL_MULTI); +} +#endif // HAVE_BOOST_PYTHON + bool instance_t::general_directive(char * line) { char buf[8192]; @@ -1178,6 +1231,15 @@ bool instance_t::general_directive(char * line) payee_directive(arg); return true; } + else if (std::strcmp(p, "python") == 0) { +#if defined(HAVE_BOOST_PYTHON) + python_directive(arg); +#else + throw_(parse_error, + _("'python' directive seen, but Python support is missing")); +#endif + return true; + } break; case 't': diff --git a/test/LedgerHarness.py b/test/LedgerHarness.py index c0dbe368..7b4dfa83 100755 --- a/test/LedgerHarness.py +++ b/test/LedgerHarness.py @@ -34,6 +34,7 @@ class LedgerHarness: failed = 0 verify = False gmalloc = False + python = False def __init__(self, argv): if not os.path.isfile(argv[1]): @@ -49,6 +50,9 @@ class LedgerHarness: self.failed = 0 self.verify = '--verify' in argv self.gmalloc = '--gmalloc' in argv + self.python = '--python' in argv + + os.chdir(self.sourcepath) def run(self, command, verify=None, gmalloc=None, columns=True): env = os.environ.copy() diff --git a/test/RegressTests.py b/test/RegressTests.py index 28a6c709..def202e4 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -179,7 +179,9 @@ if __name__ == '__main__': if os.path.isdir(tests): tests = [os.path.join(tests, x) - for x in os.listdir(tests) if x.endswith('.test')] + for x in os.listdir(tests) + if (x.endswith('.test') and + (not '_py.test' in x or harness.python))] if pool: pool.map(do_test, tests, 1) else: diff --git a/test/baseline/dir-python_py.test b/test/baseline/dir-python_py.test new file mode 100644 index 00000000..e4681075 --- /dev/null +++ b/test/baseline/dir-python_py.test @@ -0,0 +1,26 @@ +python + import os + def check_path(path_value): + return os.path.isfile(path_value) + +tag PATH + check check_path(value) + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_py.test + Expenses:Food $20 + Assets:Cash + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_noexist.test + Expenses:Food $20 + Assets:Cash + +test reg +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +__ERROR__ +Warning: "$sourcepath/test/baseline/dir-python_py.test", line 17: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) +end test diff --git a/test/baseline/feat-import_py.test b/test/baseline/feat-import_py.test new file mode 100644 index 00000000..6bd77586 --- /dev/null +++ b/test/baseline/feat-import_py.test @@ -0,0 +1,23 @@ +--import featimport.py + +tag PATH + check check_path(value) + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_py.test + Expenses:Food $20 + Assets:Cash + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_noexist.test + Expenses:Food $20 + Assets:Cash + +test reg +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +__ERROR__ +Warning: "$sourcepath/test/baseline/feat-import_py.test", line 14: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) +end test diff --git a/test/baseline/featimport.py b/test/baseline/featimport.py new file mode 100644 index 00000000..9edd9ba3 --- /dev/null +++ b/test/baseline/featimport.py @@ -0,0 +1,4 @@ +import os + +def check_path(path_value): + return os.path.isfile(str(path_value)) diff --git a/tools/Makefile.am b/tools/Makefile.am index 4fdd8393..bff1af1a 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -358,8 +358,14 @@ RegressTests_SOURCES = test/RegressTests.py EXTRA_DIST += test/regress test/convert.py test/LedgerHarness.py +if HAVE_BOOST_PYTHON +TEST_PYTHON_FLAGS = --python +else +TEST_PYTHON_FLAGS = +endif + RegressTests: $(srcdir)/test/RegressTests.py - echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/regress \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/regress $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ BaselineTests_SOURCES = test/RegressTests.py @@ -367,7 +373,7 @@ BaselineTests_SOURCES = test/RegressTests.py EXTRA_DIST += test/baseline BaselineTests: $(srcdir)/test/RegressTests.py - echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/baseline \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/baseline $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ ManualTests_SOURCES = test/RegressTests.py @@ -375,7 +381,7 @@ ManualTests_SOURCES = test/RegressTests.py EXTRA_DIST += test/manual ManualTests: $(srcdir)/test/RegressTests.py - echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/manual \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/manual $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ ConfirmTests_SOURCES = test/ConfirmTests.py @@ -390,13 +396,13 @@ test/input/mondo.dat: test/input/standard.dat done ConfirmTests: $(srcdir)/test/ConfirmTests.py - echo "$(PYTHON) $(srcdir)/test/ConfirmTests.py $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/input \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/ConfirmTests.py $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/input $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ GenerateTests_SOURCES = test/GenerateTests.py GenerateTests: $(srcdir)/test/GenerateTests.py - echo "$(PYTHON) $(srcdir)/test/GenerateTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) 1 ${1:-20} \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/GenerateTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) 1 ${1:-20} $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ CheckTests_SOURCES = test/CheckTests.py -- cgit v1.2.3 From 72b1a948def6c7a2e969d18faef9c2260ccf57e6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 17:45:25 -0600 Subject: Fixes to value_t to Python conversion --- src/pyinterp.cc | 8 +++----- src/value.cc | 10 ++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/pyinterp.cc b/src/pyinterp.cc index dc6fb4f7..048f5a39 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -467,7 +467,7 @@ namespace { case value_t::STRING: // a string object return object(handle<>(borrowed(str_to_py_unicode(val.as_string())))); case value_t::MASK: // a regular expression mask - return object(handle<>(borrowed(str_to_py_unicode(val.as_mask().str())))); + return object(val); case value_t::SEQUENCE: { // a vector of value_t objects list arglist; foreach (const value_t& elem, val.as_sequence()) @@ -495,8 +495,7 @@ namespace { } return object(); case value_t::ANY: // a pointer to an arbitrary object - assert("Attempted to convert an Value.ANY object to Python" == NULL); - return object(); + return object(val); } } } @@ -535,8 +534,7 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args) Py_DECREF(val); } else { Py_DECREF(val); - throw_(calc_error, - _("Could not evaluate Python variable '%1'") << name); + return NULL_VALUE; } std::signal(SIGINT, sigint_handler); return result; diff --git a/src/value.cc b/src/value.cc index 5fa748f6..2446c97a 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1251,6 +1251,16 @@ void value_t::in_place_cast(type_t cast_type) } break; + case MASK: + switch (cast_type) { + case STRING: + set_string(as_mask().str()); + return; + default: + break; + } + break; + default: break; } -- cgit v1.2.3 From a3a877284a5d5bc5683c6f818037970b81cf664f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 21:18:49 -0600 Subject: Was missing an assignment of context.scope --- src/textual.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 51b5849b..839e871b 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -743,11 +743,13 @@ void instance_t::include_directive(char * line) if (glob.match(base)) { journal_t * journal = context.journal; account_t * master = context.master; + account_t * scope = context.scope; context_stack.push(*iter); context_stack.get_current().journal = journal; context_stack.get_current().master = master; + context_stack.get_current().scope = scope; try { instance_t instance(context_stack, context_stack.get_current(), this); -- cgit v1.2.3 From 0e81c60d70b88ef98f1da4f884632a7d4d8a2c49 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 21:33:27 -0600 Subject: Fixed a typo --- src/textual.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 839e871b..dc07efa0 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -743,7 +743,7 @@ void instance_t::include_directive(char * line) if (glob.match(base)) { journal_t * journal = context.journal; account_t * master = context.master; - account_t * scope = context.scope; + scope_t * scope = context.scope; context_stack.push(*iter); -- cgit v1.2.3 From 7a141edb49fc91aee09d09a0eab22aa475b6c37b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 22:09:17 -0600 Subject: Propagate out parsing context counts --- src/textual.cc | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index dc07efa0..2ec58898 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -741,9 +741,12 @@ void instance_t::include_directive(char * line) string base = (*iter).leaf(); #endif // BOOST_VERSION >= 103700 if (glob.match(base)) { - journal_t * journal = context.journal; - account_t * master = context.master; - scope_t * scope = context.scope; + journal_t * journal = context.journal; + account_t * master = context.master; + scope_t * scope = context.scope; + std::size_t& errors = context.errors; + std::size_t& count = context.count; + std::size_t& sequence = context.sequence; context_stack.push(*iter); @@ -757,9 +760,18 @@ void instance_t::include_directive(char * line) instance.parse(); } catch (...) { + errors += context_stack.get_current().errors; + count += context_stack.get_current().count; + sequence += context_stack.get_current().sequence; + context_stack.pop(); throw; } + + errors += context_stack.get_current().errors; + count += context_stack.get_current().count; + sequence += context_stack.get_current().sequence; + context_stack.pop(); files_found = true; -- cgit v1.2.3 From 4e377489990e8c11ebe3cc7d7dced90d0fa76202 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 22:09:42 -0600 Subject: Re-enabled some necessary code --- src/scope.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index c42ec66b..90f9c37a 100644 --- a/src/scope.h +++ b/src/scope.h @@ -201,9 +201,7 @@ public: virtual void define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { -#if 0 parent->define(kind, name, def); -#endif grandchild.define(kind, name, def); } -- cgit v1.2.3 From cfd7ffb12645c198a5b15891654f6ad6a0e4db27 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 22:20:38 -0600 Subject: Provide more context if a valexpr function call fails --- src/op.cc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index 372101f0..8e9df812 100644 --- a/src/op.cc +++ b/src/op.cc @@ -272,10 +272,16 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) if (has_right()) call_args.set_args(split_cons_expr(right())); - if (func->is_function()) - result = func->as_function()(call_args); - else - result = func->calc(call_args, locus, depth + 1); + try { + if (func->is_function()) + result = func->as_function()(call_args); + else + result = func->calc(call_args, locus, depth + 1); + } + catch (const std::exception&) { + add_error_context(_("While calling function '%1':" << name)); + throw; + } check_type_context(scope, result); break; @@ -308,7 +314,8 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) if (args_index < args_count) throw_(calc_error, - _("Too few arguments in function call (saw %1)") << args_count); + _("Too few arguments in function call (saw %1, wanted %2)") + << args_count << args_index); result = right()->calc(call_scope, locus, depth + 1); break; -- cgit v1.2.3 From a125f24d29fd8275be7283a2edca9abc125703d7 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 23:40:02 -0600 Subject: Allow --options to be added by the user in Python --- src/context.h | 7 +++++++ src/option.cc | 1 - src/pyinterp.cc | 11 ++++++++++- src/session.cc | 2 ++ src/session.h | 1 + src/textual.cc | 5 +++-- test/baseline/feat-option_py.test | 14 ++++++++++++++ test/baseline/featoption.py | 5 +++++ 8 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 test/baseline/feat-option_py.test create mode 100644 test/baseline/featoption.py (limited to 'src') diff --git a/src/context.h b/src/context.h index d7999e5a..45bb9990 100644 --- a/src/context.h +++ b/src/context.h @@ -71,6 +71,10 @@ public: std::size_t count; std::size_t sequence; + explicit parse_context_t(const path& cwd) + : current_directory(cwd), master(NULL), scope(NULL), + linenum(0), errors(0), count(0), sequence(1) {} + explicit parse_context_t(shared_ptr _stream, const path& cwd) : stream(_stream), current_directory(cwd), master(NULL), @@ -126,6 +130,9 @@ class parse_context_stack_t std::list parsing_context; public: + void push() { + parsing_context.push_front(parse_context_t(filesystem::current_path())); + } void push(shared_ptr stream, const path& cwd = filesystem::current_path()) { parsing_context.push_front(parse_context_t(stream, cwd)); diff --git a/src/option.cc b/src/option.cc index 170b94af..418980bd 100644 --- a/src/option.cc +++ b/src/option.cc @@ -89,7 +89,6 @@ namespace { catch (const std::exception&) { if (name[0] == '-') add_error_context(_("While parsing option '%1'") << name); - else add_error_context(_("While parsing environent variable '%1'") << name); throw; diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 048f5a39..3157079a 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -419,10 +419,19 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind, } break; - case symbol_t::OPTION: + case symbol_t::OPTION: { if (option_t * handler = lookup_option(name.c_str())) return MAKE_OPT_HANDLER(python_interpreter_t, handler); + + string option_name(string("option_") + name); + if (is_initialized && main_nspace.has_key(option_name.c_str())) { + DEBUG("python.interp", "Python lookup option: " << option_name); + + if (python::object obj = main_nspace.get(option_name.c_str())) + return WRAP_FUNCTOR(functor_t(obj, option_name)); + } break; + } case symbol_t::PRECOMMAND: { const char * p = name.c_str(); diff --git a/src/session.cc b/src/session.cc index db01fbf6..3e7cdb3d 100644 --- a/src/session.cc +++ b/src/session.cc @@ -68,6 +68,8 @@ session_t::session_t() HANDLER(price_db_).on(none, (path(home_var) / ".pricedb").string()); else HANDLER(price_db_).on(none, path("./.pricedb").string()); + + parsing_context.push(); } std::size_t session_t::read_data(const string& master_account) diff --git a/src/session.h b/src/session.h index 38062b78..879efeb6 100644 --- a/src/session.h +++ b/src/session.h @@ -65,6 +65,7 @@ public: explicit session_t(); virtual ~session_t() { TRACE_DTOR(session_t); + parsing_context.pop(); } virtual string description() { diff --git a/src/textual.cc b/src/textual.cc index 2ec58898..a4e03435 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -508,8 +508,9 @@ void instance_t::option_directive(char * line) *p++ = '\0'; } - if (! process_option(context.pathname.string(), line + 2, - *context.scope, p, line)) + path abs_path(filesystem::absolute(context.pathname, + context.current_directory)); + if (! process_option(abs_path.string(), line + 2, *context.scope, p, line)) throw_(option_error, _("Illegal option --%1") << line + 2); } diff --git a/test/baseline/feat-option_py.test b/test/baseline/feat-option_py.test new file mode 100644 index 00000000..a67b27c3 --- /dev/null +++ b/test/baseline/feat-option_py.test @@ -0,0 +1,14 @@ +python + def option_pyfirst(context): + print "In --pyfirst (from %s)" % context + + def option_pysecond(context, val): + print "In --pysecond=%s (from %s)" % (val, context) + +--pyfirst +--pysecond Hey + +test reg +In --pyfirst (from $sourcepath/test/baseline/feat-option_py.test) +In --pysecond=Hey (from $sourcepath/test/baseline/feat-option_py.test) +end test diff --git a/test/baseline/featoption.py b/test/baseline/featoption.py new file mode 100644 index 00000000..caa4f2bc --- /dev/null +++ b/test/baseline/featoption.py @@ -0,0 +1,5 @@ +def option_pyfirst(context): + print "In --pyfirst (from %s)" % context + +def option_pysecond(context, val): + print "In --pysecond=%sh (from %s)" % (val, context) -- cgit v1.2.3 From ddba59b703fcfeb546627ee7e44a25fab49c0c12 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 2 Mar 2012 01:36:58 -0600 Subject: This now works: ledger --import os eval 'os.path.isdir("/tmp")' --- src/pyinterp.cc | 143 +++++++++++++++++++++++---------------- src/pyinterp.h | 58 +++++++++++----- src/textual.cc | 4 +- test/baseline/dir-python_py.test | 8 ++- 4 files changed, 135 insertions(+), 78 deletions(-) (limited to 'src') diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 3157079a..d733c40d 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -84,16 +84,56 @@ struct python_run python_run(python_interpreter_t * intepreter, const string& str, int input_mode) - : result(handle<>(borrowed(PyRun_String(str.c_str(), input_mode, - intepreter->main_nspace.ptr(), - intepreter->main_nspace.ptr())))) {} + : result + (handle<> + (borrowed + (PyRun_String(str.c_str(), input_mode, + intepreter->main_module->module_globals.ptr(), + intepreter->main_module->module_globals.ptr())))) {} operator object() { return result; } }; +python_module_t::python_module_t(const string& name) + : scope_t(), module_name(name), module_globals() +{ + import_module(name); +} + +python_module_t::python_module_t(const string& name, python::object obj) + : scope_t(), module_name(name), module_globals() +{ + module_object = obj; + module_globals = extract(module_object.attr("__dict__")); +} + +void python_module_t::import_module(const string& name, bool import_direct) +{ + object mod = python::import(name.c_str()); + if (! mod) + throw_(std::runtime_error, + _("Module import failed (couldn't find %1)") << name); + + dict globals = extract(mod.attr("__dict__")); + if (! globals) + throw_(std::runtime_error, + _("Module import failed (couldn't find %1)") << name); + + if (! import_direct) { + module_object = mod; + module_globals = globals; + } else { + // Import all top-level entries directly into the namespace + module_globals.update(mod.attr("__dict__")); + } +} + void python_interpreter_t::initialize() { + if (is_initialized) + return; + TRACE_START(python_init, 1, "Initialized Python"); try { @@ -104,15 +144,7 @@ void python_interpreter_t::initialize() hack_system_paths(); - main_module = python::import("__main__"); - if (! main_module) - throw_(std::runtime_error, - _("Python failed to initialize (couldn't find __main__)")); - - main_nspace = extract(main_module.attr("__dict__")); - if (! main_nspace) - throw_(std::runtime_error, - _("Python failed to initialize (couldn't find __dict__)")); + main_module = import_module("__main__"); python::detail::init_module("ledger", &initialize_for_python); @@ -170,28 +202,6 @@ void python_interpreter_t::hack_system_paths() #endif } -object python_interpreter_t::import_into_main(const string& str) -{ - if (! is_initialized) - initialize(); - - try { - object mod = python::import(str.c_str()); - if (! mod) - throw_(std::runtime_error, - _("Failed to import Python module %1") << str); - - // Import all top-level entries directly into the main namespace - main_nspace.update(mod.attr("__dict__")); - - return mod; - } - catch (const error_already_set&) { - PyErr_Print(); - } - return object(); -} - object python_interpreter_t::import_option(const string& str) { if (! is_initialized) @@ -229,13 +239,10 @@ object python_interpreter_t::import_option(const string& str) } try { - if (contains(str, ".py")) { - import_into_main(name); - } else { - object obj = python::import(python::str(name.c_str())); - main_nspace[name.c_str()] = obj; - return obj; - } + if (contains(str, ".py")) + main_module->import_module(name, true); + else + import_module(str); } catch (const error_already_set&) { PyErr_Print(); @@ -399,6 +406,40 @@ python_interpreter_t::lookup_option(const char * p) return NULL; } +expr_t::ptr_op_t python_module_t::lookup(const symbol_t::kind_t kind, + const string& name) +{ + switch (kind) { + case symbol_t::FUNCTION: + DEBUG("python.interp", "Python lookup: " << name); + if (module_globals.has_key(name.c_str())) { + if (python::object obj = module_globals.get(name.c_str())) { + if (PyModule_Check(obj.ptr())) { + shared_ptr mod; + python_module_map_t::iterator i = + python_session->modules_map.find(obj.ptr()); + if (i == python_session->modules_map.end()) { + mod.reset(new python_module_t(name, obj)); + python_session->modules_map.insert + (python_module_map_t::value_type(obj.ptr(), mod)); + } else { + mod = (*i).second; + } + return expr_t::op_t::wrap_value(scope_value(mod.get())); + } else { + return WRAP_FUNCTOR(python_interpreter_t::functor_t(obj, name)); + } + } + } + break; + + default: + break; + } + + return NULL; +} + expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind, const string& name) { @@ -408,28 +449,16 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind, switch (kind) { case symbol_t::FUNCTION: - if (option_t * handler = lookup_option(name.c_str())) - return MAKE_OPT_FUNCTOR(python_interpreter_t, handler); - - if (is_initialized && main_nspace.has_key(name.c_str())) { - DEBUG("python.interp", "Python lookup: " << name); - - if (python::object obj = main_nspace.get(name.c_str())) - return WRAP_FUNCTOR(functor_t(obj, name)); - } + if (is_initialized) + return main_module->lookup(kind, name); break; case symbol_t::OPTION: { if (option_t * handler = lookup_option(name.c_str())) return MAKE_OPT_HANDLER(python_interpreter_t, handler); - string option_name(string("option_") + name); - if (is_initialized && main_nspace.has_key(option_name.c_str())) { - DEBUG("python.interp", "Python lookup option: " << option_name); - - if (python::object obj = main_nspace.get(option_name.c_str())) - return WRAP_FUNCTOR(functor_t(obj, option_name)); - } + if (is_initialized) + return main_module->lookup(symbol_t::FUNCTION, string("option_") + name); break; } diff --git a/src/pyinterp.h b/src/pyinterp.h index c3397840..8699f69d 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -38,21 +38,52 @@ namespace ledger { +class python_module_t : public scope_t, public noncopyable +{ +public: + string module_name; + python::object module_object; + python::dict module_globals; + + explicit python_module_t(const string& name); + explicit python_module_t(const string& name, python::object obj); + + void import_module(const string& name, bool import_direct = false); + + virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, + const string& name); + + void define_global(const string& name, python::object obj) { + module_globals[name] = obj; + } + + virtual string description() { + return module_name; + } +}; + +typedef std::map > python_module_map_t; + class python_interpreter_t : public session_t { public: - python::object main_module; - python::dict main_nspace; - bool is_initialized; + bool is_initialized; - python_interpreter_t() - : session_t(), main_nspace(), is_initialized(false) { + shared_ptr main_module; + python_module_map_t modules_map; + + shared_ptr import_module(const string& name) { + shared_ptr mod(new python_module_t(name)); + if (name != "__main__") + main_module->define_global(name, mod->module_object); + return mod; + } + + python_interpreter_t() : session_t(), is_initialized(false) { TRACE_CTOR(python_interpreter_t, ""); } - virtual ~python_interpreter_t() { TRACE_DTOR(python_interpreter_t); - if (is_initialized) Py_Finalize(); } @@ -60,7 +91,6 @@ public: void initialize(); void hack_system_paths(); - python::object import_into_main(const string& name); python::object import_option(const string& name); enum py_eval_mode_t { @@ -69,14 +99,10 @@ public: PY_EVAL_MULTI }; - python::object eval(std::istream& in, - py_eval_mode_t mode = PY_EVAL_EXPR); - python::object eval(const string& str, - py_eval_mode_t mode = PY_EVAL_EXPR); - python::object eval(const char * c_str, - py_eval_mode_t mode = PY_EVAL_EXPR) { - string str(c_str); - return eval(str, mode); + python::object eval(std::istream& in, py_eval_mode_t mode = PY_EVAL_EXPR); + python::object eval(const string& str, py_eval_mode_t mode = PY_EVAL_EXPR); + python::object eval(const char * c_str, py_eval_mode_t mode = PY_EVAL_EXPR) { + return eval(string(c_str), mode); } value_t python_command(call_scope_t& scope); diff --git a/src/textual.cc b/src/textual.cc index a4e03435..95635184 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1156,8 +1156,8 @@ void instance_t::python_directive(char * line) if (! python_session->is_initialized) python_session->initialize(); - python_session->main_nspace["journal"] = - python::object(python::ptr(context.journal)); + python_session->main_module->define_global + ("journal", python::object(python::ptr(context.journal))); python_session->eval(script.str(), python_interpreter_t::PY_EVAL_MULTI); } #endif // HAVE_BOOST_PYTHON diff --git a/test/baseline/dir-python_py.test b/test/baseline/dir-python_py.test index e4681075..99ff4b1b 100644 --- a/test/baseline/dir-python_py.test +++ b/test/baseline/dir-python_py.test @@ -1,10 +1,11 @@ python import os - def check_path(path_value): - return os.path.isfile(path_value) + def check_path(path): + return os.path.isfile(path) tag PATH check check_path(value) + check os.path.isfile(value) 2012-02-29 KFC ; PATH: test/baseline/feat-import_py.test @@ -22,5 +23,6 @@ test reg 12-Feb-29 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 __ERROR__ -Warning: "$sourcepath/test/baseline/dir-python_py.test", line 17: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) +Warning: "$sourcepath/test/baseline/dir-python_py.test", line 18: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) +Warning: "$sourcepath/test/baseline/dir-python_py.test", line 18: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): ((os.path).isfile(value)) end test -- cgit v1.2.3 From 4e4cbc6f1ef949b420818c02a4ced5d0adeaa751 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 2 Mar 2012 01:49:07 -0600 Subject: Removed unneeded code --- src/query.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/query.h b/src/query.h index 52168539..f95988a7 100644 --- a/src/query.h +++ b/src/query.h @@ -153,8 +153,6 @@ public: case TERM: return string("TERM(") + *value + ")"; case END_REACHED: return "END_REACHED"; } - assert(false); - return empty_string; } string symbol() const { -- cgit v1.2.3 From 8b9b9e9e723cc24a030f0cb5ae101d0978a0aa81 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 2 Mar 2012 10:18:42 -0600 Subject: Removed an old comment --- src/token.cc | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/token.cc b/src/token.cc index 9449d9b7..57b97eca 100644 --- a/src/token.cc +++ b/src/token.cc @@ -415,9 +415,6 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags) try { amount_t temp; if (! temp.parse(in, parse_flags.plus_flags(PARSE_SOFT_FAIL))) { - // If the amount had no commodity, it must be an unambiguous - // variable reference - in.clear(); in.seekg(pos, std::ios::beg); if (in.fail()) -- cgit v1.2.3 From 35ace8816adb76e80e99afb947d66777b31ba43f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 2 Mar 2012 10:58:16 -0600 Subject: Improvements to format parsing Fixes #337 --- src/format.cc | 270 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 172 insertions(+), 98 deletions(-) (limited to 'src') diff --git a/src/format.cc b/src/format.cc index a391fdf1..4ff1fc19 100644 --- a/src/format.cc +++ b/src/format.cc @@ -70,6 +70,27 @@ void format_t::element_t::dump(std::ostream& out) const } namespace { + struct format_mapping_t { + char letter; + const char * expr; + } single_letter_mappings[] = { + { 'd', "date" }, + { 'S', "filename" }, + { 'B', "beg_pos" }, + { 'b', "beg_line" }, + { 'E', "end_pos" }, + { 'e', "end_line" }, + { 'X', "cleared" }, + { 'Y', "xact.cleared" }, + { 'C', "code" }, + { 'P', "payee" }, + { 'a', "account.name" }, + { 'A', "account" }, + { 't', "justify(scrub(display_amount), $min, $max, $left, color)" }, + { 'T', "justify(scrub(display_total), $min, $max, $left, color)" }, + { 'N', "note" }, + }; + expr_t parse_single_expression(const char *& p, bool single_expr = true) { string temp(p); @@ -92,6 +113,13 @@ namespace { } return expr; } + + inline expr_t::ptr_op_t ident_node(const string& ident) + { + expr_t::ptr_op_t node(new expr_t::op_t(expr_t::op_t::IDENT)); + node->set_ident(ident); + return node; + } } format_t::element_t * format_t::parse_elements(const string& fmt, @@ -172,122 +200,168 @@ format_t::element_t * format_t::parse_elements(const string& fmt, current->min_width = current->max_width; } - switch (*p) { - case '%': - current->type = element_t::STRING; - current->data = string("%"); - break; + if (std::isalpha(*p)) { + bool found = false; + for (std::size_t i = 0; i < (sizeof(single_letter_mappings) / + sizeof(format_mapping_t)); i++) { + if (*p == single_letter_mappings[i].letter) { + std::ostringstream expr; + for (const char * ptr = single_letter_mappings[i].expr; *ptr; ){ + if (*ptr == '$') { + const char * beg = ++ptr; + while (*ptr && std::isalpha(*ptr)) + ++ptr; + string::size_type klen = static_cast(ptr - beg); + string keyword(beg, 0, klen); + if (keyword == "min") + expr << (current->min_width > 0 ? + static_cast(current->min_width) : -1); + else if (keyword == "max") + expr << (current->max_width > 0 ? + static_cast(current->max_width) : -1); + else if (keyword == "left") + expr << (current->has_flags(ELEMENT_ALIGN_LEFT) ? "false" : "true"); + else + assert("Unrecognized format substitution keyword" == NULL); + } else { + expr << *ptr++; + } + } + current->type = element_t::EXPR; + current->data = expr_t(expr.str()); + found = true; + break; + } + } + if (! found) + throw_(format_error, _("Unrecognized formatting character: %1") << *p); + } else { + switch (*p) { + case '%': + current->type = element_t::STRING; + current->data = string("%"); + break; - case '$': { - if (! tmpl) - throw_(format_error, _("Prior field reference, but no template")); + case '$': { + if (! tmpl) + throw_(format_error, _("Prior field reference, but no template")); - p++; - if (*p == '0' || (! std::isdigit(*p) && - *p != 'A' && *p != 'B' && *p != 'C' && - *p != 'D' && *p != 'E' && *p != 'F')) - throw_(format_error, _("%$ field reference must be a digit from 1-9")); + p++; + if (*p == '0' || (! std::isdigit(*p) && + *p != 'A' && *p != 'B' && *p != 'C' && + *p != 'D' && *p != 'E' && *p != 'F')) + throw_(format_error, _("%$ field reference must be a digit from 1-9")); - int index = std::isdigit(*p) ? *p - '0' : (*p - 'A' + 10); - element_t * tmpl_elem = tmpl->elements.get(); + int index = std::isdigit(*p) ? *p - '0' : (*p - 'A' + 10); + element_t * tmpl_elem = tmpl->elements.get(); - for (int i = 1; i < index && tmpl_elem; i++) { - tmpl_elem = tmpl_elem->next.get(); - while (tmpl_elem && tmpl_elem->type != element_t::EXPR) + for (int i = 1; i < index && tmpl_elem; i++) { tmpl_elem = tmpl_elem->next.get(); - } + while (tmpl_elem && tmpl_elem->type != element_t::EXPR) + tmpl_elem = tmpl_elem->next.get(); + } - if (! tmpl_elem) - throw_(format_error, _("%$ reference to a non-existent prior field")); + if (! tmpl_elem) + throw_(format_error, _("%$ reference to a non-existent prior field")); - *current = *tmpl_elem; - break; - } + *current = *tmpl_elem; + break; + } - case '(': - case '{': { - bool format_amount = *p == '{'; - if (format_amount) p++; + case '(': + case '{': { + bool format_amount = *p == '{'; - current->type = element_t::EXPR; - current->data = parse_single_expression(p, ! format_amount); + current->type = element_t::EXPR; + current->data = parse_single_expression(p); - // Wrap the subexpression in calls to justify and scrub - if (format_amount) { - if (! *p || *(p + 1) != '}') - throw_(format_error, _("Expected closing brace")); - else - p++; + // Wrap the subexpression in calls to justify and scrub + if (! format_amount) + break; 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; - if (op->kind == expr_t::op_t::O_CONS) { - amount_op = op->left(); - colorize_op = op->has_right() ? op->right() : NULL; - } else { - amount_op = op; + expr_t::ptr_op_t call2_node(new expr_t::op_t(expr_t::op_t::O_CALL)); + { + call2_node->set_left(ident_node("justify")); + + { + expr_t::ptr_op_t args3_node(new expr_t::op_t(expr_t::op_t::O_CONS)); + { + { + expr_t::ptr_op_t call1_node(new expr_t::op_t(expr_t::op_t::O_CALL)); + { + call1_node->set_left(ident_node("scrub")); + call1_node->set_right(op->kind == expr_t::op_t::O_CONS ? op->left() : op); + } + + args3_node->set_left(call1_node); + } + + expr_t::ptr_op_t args2_node(new expr_t::op_t(expr_t::op_t::O_CONS)); + { + { + expr_t::ptr_op_t arg1_node(new expr_t::op_t(expr_t::op_t::VALUE)); + arg1_node->set_value(current->min_width > 0 ? + long(current->min_width) : -1); + + args2_node->set_left(arg1_node); + } + + { + expr_t::ptr_op_t args1_node(new expr_t::op_t(expr_t::op_t::O_CONS)); + { + { + expr_t::ptr_op_t arg2_node(new expr_t::op_t(expr_t::op_t::VALUE)); + arg2_node->set_value(current->max_width > 0 ? + long(current->max_width) : -1); + + args1_node->set_left(arg2_node); + } + + { + expr_t::ptr_op_t arg3_node(new expr_t::op_t(expr_t::op_t::VALUE)); + arg3_node->set_value(! current->has_flags(ELEMENT_ALIGN_LEFT)); + + args1_node->set_right(arg3_node); + } + } + + args2_node->set_right(args1_node); + } + + args3_node->set_right(args2_node); + } + } + + call2_node->set_right(args3_node); + } } - expr_t::ptr_op_t scrub_node(new expr_t::op_t(expr_t::op_t::IDENT)); - scrub_node->set_ident("scrub"); - - expr_t::ptr_op_t call1_node(new expr_t::op_t(expr_t::op_t::O_CALL)); - call1_node->set_left(scrub_node); - call1_node->set_right(amount_op); - - expr_t::ptr_op_t arg1_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)); - - 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; current->max_width = 0; - expr_t::ptr_op_t args1_node(new expr_t::op_t(expr_t::op_t::O_CONS)); - args1_node->set_left(arg2_node); - args1_node->set_right(arg3_node); - - expr_t::ptr_op_t args2_node(new expr_t::op_t(expr_t::op_t::O_CONS)); - args2_node->set_left(arg1_node); - args2_node->set_right(args1_node); - - expr_t::ptr_op_t args3_node(new expr_t::op_t(expr_t::op_t::O_CONS)); - args3_node->set_left(call1_node); - args3_node->set_right(args2_node); - - expr_t::ptr_op_t seq1_node(new expr_t::op_t(expr_t::op_t::O_SEQ)); - seq1_node->set_left(args3_node); - - expr_t::ptr_op_t justify_node(new expr_t::op_t(expr_t::op_t::IDENT)); - justify_node->set_ident("justify"); - - expr_t::ptr_op_t call2_node(new expr_t::op_t(expr_t::op_t::O_CALL)); - call2_node->set_left(justify_node); - call2_node->set_right(seq1_node); - 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)); - ansify_if_node->set_ident("ansify_if"); - - expr_t::ptr_op_t args4_node(new expr_t::op_t(expr_t::op_t::O_CONS)); - args4_node->set_left(call2_node); - args4_node->set_right(colorize_op); - - expr_t::ptr_op_t seq2_node(new expr_t::op_t(expr_t::op_t::O_SEQ)); - seq2_node->set_left(args4_node); + expr_t::ptr_op_t colorize_op; + if (op->kind == expr_t::op_t::O_CONS) + colorize_op = op->has_right() ? op->right() : NULL; + if (colorize_op) { expr_t::ptr_op_t call3_node(new expr_t::op_t(expr_t::op_t::O_CALL)); - call3_node->set_left(ansify_if_node); - call3_node->set_right(seq2_node); + { + call3_node->set_left(ident_node("ansify_if")); + + { + expr_t::ptr_op_t args4_node(new expr_t::op_t(expr_t::op_t::O_CONS)); + { + args4_node->set_left(call2_node); // from above + args4_node->set_right(colorize_op); + } + + call3_node->set_right(args4_node); + } + } current->data = expr_t(call3_node); } else { @@ -295,12 +369,12 @@ format_t::element_t * format_t::parse_elements(const string& fmt, } boost::get(current->data).set_text(prev_expr); + break; } - break; - } - default: - throw_(format_error, _("Unrecognized formatting character: %1") << *p); + default: + throw_(format_error, _("Unrecognized formatting character: %1") << *p); + } } } -- cgit v1.2.3 From c8c2a17e282c2cbf3c0edb1b756e16be58328331 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 3 Mar 2012 01:17:21 -0600 Subject: Fixed invocation of lambda expressions foo = x, y, z -> print(x, y, z) foo(1, 2, 3) However, this still does not work: (x, y, z -> print(x, y, z))(1, 2, 3) --- src/op.cc | 2 +- src/scope.h | 24 +++++++++++++++++++----- test/baseline/cmd-script.test | 5 +++++ tools/proof | 4 ++-- 4 files changed, 27 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index 8e9df812..bd2cc32f 100644 --- a/src/op.cc +++ b/src/op.cc @@ -288,7 +288,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) } case O_LAMBDA: { - call_scope_t& call_args(downcast(scope)); + call_scope_t& call_args(find_scope(scope, true)); std::size_t args_count(call_args.size()); std::size_t args_index(0); symbol_scope_t call_scope(call_args); diff --git a/src/scope.h b/src/scope.h index 90f9c37a..785ce284 100644 --- a/src/scope.h +++ b/src/scope.h @@ -227,15 +227,17 @@ private: }; template -T * search_scope(scope_t * ptr) +T * search_scope(scope_t * ptr, bool prefer_direct_parents = false) { if (T * sought = dynamic_cast(ptr)) return sought; if (bind_scope_t * scope = dynamic_cast(ptr)) { - if (T * sought = search_scope(&scope->grandchild)) + if (T * sought = search_scope(prefer_direct_parents ? + scope->parent : &scope->grandchild)) return sought; - return search_scope(scope->parent); + return search_scope(prefer_direct_parents ? + &scope->grandchild : scope->parent); } else if (child_scope_t * child_scope = dynamic_cast(ptr)) { return search_scope(child_scope->parent); @@ -244,9 +246,21 @@ T * search_scope(scope_t * ptr) } template -inline T& find_scope(child_scope_t& scope, bool skip_this = true) +inline T& find_scope(child_scope_t& scope, bool skip_this = true, + bool prefer_direct_parents = false) { - if (T * sought = search_scope(skip_this ? scope.parent : &scope)) + if (T * sought = search_scope(skip_this ? scope.parent : &scope, + prefer_direct_parents)) + return *sought; + + throw_(std::runtime_error, _("Could not find scope")); + return reinterpret_cast(scope); // never executed +} + +template +inline T& find_scope(scope_t& scope, bool prefer_direct_parents = false) +{ + if (T * sought = search_scope(&scope, prefer_direct_parents)) return *sought; throw_(std::runtime_error, _("Could not find scope")); diff --git a/test/baseline/cmd-script.test b/test/baseline/cmd-script.test index 12e9c868..ce920ebb 100644 --- a/test/baseline/cmd-script.test +++ b/test/baseline/cmd-script.test @@ -10,3 +10,8 @@ end test test eval 'total_expr=$100;amount_expr=$15;x=total_expr;x=x/5;x=amount_expr-x*5;x' $-85 end test + +test eval 'foo = x, y, z -> print(x, y, z); foo(1, 2, 3)' +123 +1 +end test diff --git a/tools/proof b/tools/proof index f37c1488..3b997143 100755 --- a/tools/proof +++ b/tools/proof @@ -15,12 +15,12 @@ ledger_proof() { exit 0 fi - rm -fr $DEST/ledger-proof + sudo rm -fr $DEST/ledger-proof time nice -n 20 \ ./acprep --debug --enable-doxygen --universal --clang -j16 proof 2>&1 | \ tee $LOGDIR/ledger-proof.log - rm -fr $DEST/ledger-proof + sudo rm -fr $DEST/ledger-proof time nice -n 20 \ ./acprep --debug --enable-doxygen --universal --python --clang -j16 proof 2>&1 | \ tee -a $LOGDIR/ledger-proof.log -- cgit v1.2.3 From 44061554756fce740de949f4a4585b963c9aa1cb Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 3 Mar 2012 02:26:15 -0600 Subject: Make --change a synonym for --gain --- src/report.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 2c0c3970..d0dcd091 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1021,7 +1021,7 @@ option_t * report_t::lookup_option(const char * p) else OPT_ALT(head_, first_); break; case 'g': - OPT(gain); + OPT_ALT(gain, change); else OPT(group_by_); else OPT(group_title_format_); else OPT(generated); -- cgit v1.2.3 From 58d912827d2c6bd90c063537378baffff014c0f2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 3 Mar 2012 02:26:28 -0600 Subject: Make --value a synonym for market --- src/report.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index d0dcd091..647df3d2 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1046,7 +1046,7 @@ option_t * report_t::lookup_option(const char * p) else OPT_ALT(tail_, last_); break; case 'm': - OPT(market); + OPT_ALT(market, value); else OPT(monthly); else OPT(meta_); else OPT(meta_width_); -- cgit v1.2.3 From 48ab6ad1dbab100bb8abd87029a0ca5bc501a3db Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 4 Mar 2012 03:35:06 -0600 Subject: Switched to using Boost.Graph for commodity pricing --- src/commodity.cc | 442 +++++------------------------------------------------- src/commodity.h | 114 +------------- src/filters.cc | 3 + src/history.cc | 196 ++++++++++++++++++++++++ src/history.h | 184 +++++++++++++++++++++++ src/iterators.cc | 3 + src/pool.cc | 88 ++--------- src/pool.h | 23 ++- src/report.cc | 10 +- src/system.hh.in | 15 ++ tools/Makefile.am | 2 + 11 files changed, 476 insertions(+), 604 deletions(-) create mode 100644 src/history.cc create mode 100644 src/history.h (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index 643d0d1e..c0ccae11 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -40,410 +40,68 @@ namespace ledger { bool commodity_t::decimal_comma_by_default = false; -void commodity_t::history_t::add_price(commodity_t& source, - const datetime_t& date, - const amount_t& price, - const bool reflexive) +void commodity_t::add_price(const datetime_t& date, const amount_t& price, + const bool reflexive) { - DEBUG("commodity.prices.add", "add_price to " << source - << (reflexive ? " (secondary)" : " (primary)") - << " : " << date << ", " << price); + if (! reflexive) + add_flags(COMMODITY_PRIMARY); + pool().commodity_price_history.add_price(*this, date, price); - history_map::iterator i = prices.find(date); - if (i != prices.end()) { - (*i).second = price; - } else { - std::pair result - = prices.insert(history_map::value_type(date, price)); - assert(result.second); - } - - if (reflexive) { - amount_t inverse = price.inverted(); - inverse.set_commodity(const_cast(source)); - price.commodity().add_price(date, inverse, false); - } else { - DEBUG("commodity.prices.add", - "marking commodity " << source.symbol() << " as primary"); - source.add_flags(COMMODITY_PRIMARY); - } -} - -bool commodity_t::history_t::remove_price(const datetime_t& date) -{ - DEBUG("commodity.prices.add", "remove_price: " << date); - - history_map::size_type n = prices.erase(date); - if (n > 0) - return true; - return false; -} - -void commodity_t::varied_history_t:: - add_price(commodity_t& source, - const datetime_t& date, - const amount_t& price, - const bool reflexive) -{ - optional hist = history(price.commodity()); - if (! hist) { - std::pair result - = histories.insert(history_by_commodity_map::value_type - (&price.commodity(), history_t())); - assert(result.second); - - hist = (*result.first).second; - } - assert(hist); - - hist->add_price(source, date, price, reflexive); + DEBUG("commodity.prices.find", "Price added, clearing price_map"); + base->price_map.clear(); // a price was added, invalid the map } -bool commodity_t::varied_history_t::remove_price(const datetime_t& date, - commodity_t& comm) +void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) { - DEBUG("commodity.prices.add", "varied_remove_price: " << date << ", " << comm); + pool().commodity_price_history.remove_price(*this, commodity, date); - if (optional hist = history(comm)) - return hist->remove_price(date); - return false; + DEBUG("commodity.prices.find", "Price removed, clearing price_map"); + base->price_map.clear(); // a price was added, invalid the map } optional -commodity_t::history_t::find_price(const optional& moment, - const optional& oldest -#if defined(DEBUG_ON) - , const int indent -#endif - ) const +commodity_t::find_price(const optional& target = none, + const optional& moment = none, + const optional& oldest = none) const { - price_point_t point; - bool found = false; - -#if defined(DEBUG_ON) -#define DEBUG_INDENT(cat, indent) \ - do { \ - if (SHOW_DEBUG(cat)) \ - for (int _i = 0; _i < indent; _i++) \ - ledger::_log_buffer << " "; \ - } while (false) -#else -#define DEBUG_INDENT(cat, indent) -#endif - -#if defined(DEBUG_ON) + pair = base_t::time_and_commodity_t + (base_t::optional_time_pair_t(moment, oldest), + commodity ? &(*commodity) : NULL); DEBUG_INDENT("commodity.prices.find", indent); - if (moment) - DEBUG("commodity.prices.find", "find price nearest before or on: " << *moment); - else - DEBUG("commodity.prices.find", "find any price"); + DEBUG("commodity.prices.find", "looking for memoized args: " + << (moment ? format_datetime(*moment) : "NONE") << ", " + << (oldest ? format_datetime(*oldest) : "NONE") << ", " + << (commodity ? commodity->symbol() : "NONE")); - if (oldest) { + base_t::memoized_price_map::iterator i = base->price_map.find(*pair); + if (i != base->price_map.end()) { DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "but no older than: " << *oldest); - } -#endif - - if (prices.size() == 0) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "there are no prices in this history"); - return none; + DEBUG("commodity.prices.find", "found! returning: " + << ((*i).second ? (*i).second->price : amount_t(0L))); + return (*i).second; } - if (! moment) { - history_map::const_reverse_iterator r = prices.rbegin(); - point.when = (*r).first; - point.price = (*r).second; - found = true; - - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "using most recent price"); - } else { - history_map::const_iterator i = prices.upper_bound(*moment); - if (i == prices.end()) { - history_map::const_reverse_iterator r = prices.rbegin(); - point.when = (*r).first; - point.price = (*r).second; - found = true; + optional point = + pool().commodity_price_history.find_price + (*this, commodity, moment ? *moment : epoch, oldest); + if (pair) { + if (base->price_map.size() > base_t::max_price_map_size) { DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "using last price"); - } else { - point.when = (*i).first; - if (*moment < point.when) { - if (i != prices.begin()) { - --i; - point.when = (*i).first; - point.price = (*i).second; - found = true; - } - } else { - point.price = (*i).second; - found = true; - } - - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "using found price"); - } - } - - if (! found) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "could not find a price"); - return none; - } - else if (moment && point.when > *moment) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "price is too young "); - return none; - } - else if (oldest && point.when < *oldest) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "price is too old "); - return none; - } - else { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", - "returning price: " << point.when << ", " << point.price); - return point; - } -} - -optional -commodity_t::varied_history_t::find_price(const commodity_t& source, - const optional& commodity, - const optional& moment, - const optional& oldest -#if defined(DEBUG_ON) - , const int indent -#endif - ) const -{ - optional point; - optional limit = oldest; - -#if defined(VERIFY_ON) - if (commodity) { - VERIFY(source != *commodity); - VERIFY(! commodity->has_annotation()); - VERIFY(source.referent() != commodity->referent()); - } -#endif - -#if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "varied_find_price for: " << source); - - DEBUG_INDENT("commodity.prices.find", indent); - if (commodity) - DEBUG("commodity.prices.find", "looking for: commodity '" << *commodity << "'"); - else - DEBUG("commodity.prices.find", "looking for: any commodity"); - - if (moment) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "time index: " << *moment); - } - - if (oldest) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "only consider prices younger than: " << *oldest); - } -#endif - - // Either we couldn't find a history for the target commodity, or we - // couldn't find a price. In either case, search all histories known - // to this commodity for a price which we can calculate in terms of - // the goal commodity. - price_point_t best; - bool found = false; - - foreach (const history_by_commodity_map::value_type& hist, histories) { - commodity_t& comm(*hist.first); - if (comm == source) - continue; - - DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", - "searching for price via commodity '" << comm << "'"); - - point = hist.second.find_price(moment, limit -#if defined(DEBUG_ON) - , indent + 2 -#endif - ); - assert(! point || point->price.commodity() == comm); - - if (point) { - optional xlat; - - if (commodity && comm != *commodity) { - DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", "looking for translation price"); - - xlat = comm.find_price(commodity, moment, limit, true -#if defined(DEBUG_ON) - , indent + 2 -#endif - ); - if (xlat) { - DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", "found translated price " - << xlat->price << " from " << xlat->when); - - point->price = xlat->price * point->price; - if (xlat->when < point->when) { - point->when = xlat->when; - - DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", - "adjusting date of result back to " << point->when); - } - } else { - DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", "saw no translated price there"); - continue; - } - } - - assert(! commodity || point->price.commodity() == *commodity); - - DEBUG_INDENT("commodity.prices.find", indent + 1); DEBUG("commodity.prices.find", - "saw a price there: " << point->price << " from " << point->when); - - if (! limit || point->when > *limit) { - limit = point->when; - best = *point; - found = true; + "price map has grown too large, clearing it by half"); - DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", - "search limit adjusted to " << *limit); - } - } else { - DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", "saw no price there"); + for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) + base->price_map.erase(base->price_map.begin()); } - } - if (found) { DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.download", - "found price " << best.price << " from " << best.when); - return best; - } - return none; -} - -optional -commodity_t::varied_history_t::history(const optional& commodity) -{ - commodity_t * comm = NULL; - if (! commodity) { - if (histories.size() > 1) - return none; - comm = (*histories.begin()).first; - } else { - comm = &(*commodity); - } - - history_by_commodity_map::iterator i = histories.find(comm); - if (i != histories.end()) - return (*i).second; - - return none; -} - -optional -commodity_t::find_price(const optional& commodity, - const optional& moment, - const optional& oldest, - const bool nested -#if defined(DEBUG_ON) - , const int indent -#endif - ) const -{ - if (! has_flags(COMMODITY_WALKED) && base->varied_history) { - optional pair; -#if defined(VERIFY_ON) - optional checkpoint; - bool found = false; -#endif - - if (! nested) { - pair = base_t::time_and_commodity_t - (base_t::optional_time_pair_t(moment, oldest), - commodity ? &(*commodity) : NULL); - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "looking for memoized args: " - << (moment ? format_datetime(*moment) : "NONE") << ", " - << (oldest ? format_datetime(*oldest) : "NONE") << ", " - << (commodity ? commodity->symbol() : "NONE")); - - base_t::memoized_price_map::iterator i = base->price_map.find(*pair); - if (i != base->price_map.end()) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "found! returning: " - << ((*i).second ? (*i).second->price : amount_t(0L))); -#if defined(VERIFY_ON) - IF_VERIFY() { - found = true; - checkpoint = (*i).second; - } else -#endif // defined(VERIFY_ON) - return (*i).second; - } - } - - optional point; - - const_cast(*this).add_flags(COMMODITY_WALKED); - try { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "manually finding price..."); - - point = base->varied_history->find_price(*this, commodity, - moment, oldest -#if defined(DEBUG_ON) - , indent -#endif - ); - } - catch (...) { - const_cast(*this).drop_flags(COMMODITY_WALKED); - throw; - } - const_cast(*this).drop_flags(COMMODITY_WALKED); - -#if defined(VERIFY_ON) - if (DO_VERIFY() && found) { - VERIFY(checkpoint == point); - return checkpoint; - } -#endif // defined(VERIFY_ON) - - if (! nested && pair) { - if (base->price_map.size() > base_t::max_price_map_size) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", - "price map has grown too large, clearing it by half"); - - for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) - base->price_map.erase(base->price_map.begin()); - } - - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", - "remembered: " << (point ? point->price : amount_t(0L))); - base->price_map.insert - (base_t::memoized_price_map::value_type(*pair, point)); - } - return point; + DEBUG("commodity.prices.find", + "remembered: " << (point ? point->price : amount_t(0L))); + base->price_map.insert + (base_t::memoized_price_map::value_type(*pair, point)); } - return none; + return point; } optional @@ -767,28 +425,6 @@ void to_xml(std::ostream& out, const commodity_t& comm, if (commodity_details) { if (comm.has_annotation()) to_xml(out, as_annotated_commodity(comm).details); - - if (comm.varied_history()) { - push_xml y(out, "varied-history"); - - foreach (const commodity_t::history_by_commodity_map::value_type& pair, - comm.varied_history()->histories) { - { - push_xml z(out, "symbol"); - out << y.guard(pair.first->symbol()); - } - { - push_xml z(out, "history"); - - foreach (const commodity_t::history_map::value_type& inner_pair, - pair.second.prices) { - push_xml w(out, "price-point"); - to_xml(out, inner_pair.first); - to_xml(out, inner_pair.second); - } - } - } - } } } diff --git a/src/commodity.h b/src/commodity.h index 68f788e3..1505fe24 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -85,78 +85,6 @@ class commodity_t : public delegates_flags, public equality_comparable1 { -public: - typedef std::map history_map; - - struct history_t - { - history_map prices; - - void add_price(commodity_t& source, - const datetime_t& date, - const amount_t& price, - const bool reflexive = true); - bool remove_price(const datetime_t& date); - - optional - find_price(const optional& moment = none, - const optional& oldest = none -#if defined(DEBUG_ON) - , const int indent = 0 -#endif - ) const; - -#if defined(HAVE_BOOST_SERIALIZATION) - private: - /** Serialization. */ - - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int /* version */) { - ar & prices; - } -#endif // HAVE_BOOST_SERIALIZATION - }; - - typedef std::map history_by_commodity_map; - - struct varied_history_t - { - history_by_commodity_map histories; - - void add_price(commodity_t& source, - const datetime_t& date, - const amount_t& price, - const bool reflexive = true); - bool remove_price(const datetime_t& date, commodity_t& commodity); - - optional - find_price(const commodity_t& source, - const optional& commodity = none, - const optional& moment = none, - const optional& oldest = none -#if defined(DEBUG_ON) - , const int indent = 0 -#endif - ) const; - - optional - history(const optional& commodity = none); - -#if defined(HAVE_BOOST_SERIALIZATION) - private: - /** Serialization. */ - - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int /* version */) { - ar & histories; - } -#endif // HAVE_BOOST_SERIALIZATION - }; - protected: friend class commodity_pool_t; friend class annotated_commodity_t; @@ -182,7 +110,6 @@ protected: amount_t::precision_t precision; optional name; optional note; - optional varied_history; optional smaller; optional larger; @@ -228,7 +155,6 @@ protected: ar & precision; ar & name; ar & note; - ar & varied_history; ar & smaller; ar & larger; } @@ -335,48 +261,14 @@ public: base->larger = arg; } - optional varied_history() { - if (base->varied_history) - return *base->varied_history; - return none; - } - optional varied_history() const { - if (base->varied_history) - return *base->varied_history; - return none; - } - - optional history(const optional& commodity); - - // These methods provide a transparent pass-through to the underlying - // base->varied_history object. - void add_price(const datetime_t& date, const amount_t& price, - const bool reflexive = true) { - if (! base->varied_history) - base->varied_history = varied_history_t(); - base->varied_history->add_price(*this, date, price, reflexive); - DEBUG("commodity.prices.find", "Price added, clearing price_map"); - base->price_map.clear(); // a price was added, invalid the map - } - bool remove_price(const datetime_t& date, commodity_t& commodity) { - if (base->varied_history) { - base->varied_history->remove_price(date, commodity); - DEBUG("commodity.prices.find", "Price removed, clearing price_map"); - base->price_map.clear(); // a price was added, invalid the map - } - return false; - } + const bool reflexive = true); + void remove_price(const datetime_t& date, commodity_t& commodity); optional find_price(const optional& commodity = none, const optional& moment = none, - const optional& oldest = none, - const bool nested = false -#if defined(DEBUG_ON) - , const int indent = 0 -#endif - ) const; + const optional& oldest = none) const; optional check_for_updated_price(const optional& point, diff --git a/src/filters.cc b/src/filters.cc index 72ce9c32..0c6222d7 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -754,6 +754,8 @@ void changed_value_posts::output_intermediate_prices(post_t& post, // fall through... case value_t::BALANCE: { +#if 0 + // jww (2012-03-04): TODO commodity_t::history_map all_prices; foreach (const balance_t::amounts_map::value_type& amt_comm, @@ -797,6 +799,7 @@ void changed_value_posts::output_intermediate_prices(post_t& post, output_revaluation(post, price.first); last_total = repriced_total; } +#endif break; } default: diff --git a/src/history.cc b/src/history.cc new file mode 100644 index 00000000..44d19f5a --- /dev/null +++ b/src/history.cc @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2003-2012, 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. + */ + +#include + +#include "history.h" + +template +struct f_max : public std::binary_function { + T operator()(const T& x, const T& y) const { + return std::max(x, y); + } +}; + +namespace ledger { + +void commodity_history_t::add_commodity(const commodity_t& comm) +{ + const vertex_descriptor vert = add_vertex(&comm, price_graph); + put(indexmap, vert, reinterpret_cast(&comm)); +} + +void commodity_history_t::add_price(const commodity_t& source, + const datetime_t& when, + const amount_t& price) +{ + vertex_descriptor sv = + vertex(reinterpret_cast(&source), price_graph); + vertex_descriptor tv = + vertex(reinterpret_cast(&price.commodity()), price_graph); + + std::pair e1 = add_edge(sv, tv, 0, price_graph); + price_map_t& prices(get(ratiomap, e1.first)); + + std::pair result = + prices.insert(price_map_t::value_type(when, price)); + if (! result.second) { + // There is already an entry for this moment, so update it + (*result.first).second = price; + } +} + +void commodity_history_t::remove_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& date) +{ + vertex_descriptor sv = + vertex(reinterpret_cast(&source), price_graph); + vertex_descriptor tv = + vertex(reinterpret_cast(&target), price_graph); + + std::pair e1 = add_edge(sv, tv, 0, price_graph); + price_map_t& prices(get(ratiomap, e1.first)); + + // jww (2012-03-04): If it fails, should we give a warning? + prices.erase(date); +} + +optional +commodity_history_t::find_price(const commodity_t& source, + const datetime_t& moment, + const optional& oldest, + const optional& target) +{ + vertex_descriptor sv = + vertex(reinterpret_cast(&source), price_graph); + vertex_descriptor tv = + vertex(reinterpret_cast(&*target), price_graph); + + // Filter out edges which came into being after the reference time + + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + moment, oldest)); + + std::vector predecessors(num_vertices(fg)); + std::vector distances(num_vertices(fg)); + + PredecessorMap predecessorMap(&predecessors[0]); + DistanceMap distanceMap(&distances[0]); + + dijkstra_shortest_paths(fg, /* start= */ sv, + predecessor_map(predecessorMap) + .distance_map(distanceMap) + .distance_combine(f_max())); + + // Extract the shortest path and performance the calculations + datetime_t least_recent = moment; + amount_t price; + + vertex_descriptor v = tv; + for (vertex_descriptor u = predecessorMap[v]; + u != v; + v = u, u = predecessorMap[v]) + { + std::pair edgePair = edge(u, v, fg); + Graph::edge_descriptor edge = edgePair.first; + + const price_point_t& point(get(pricemap, edge)); + + if (price.is_null()) { + least_recent = point.when; + price = point.price; + } + else if (point.when < least_recent) + least_recent = point.when; + + // jww (2012-03-04): TODO + //price *= point.price; + } + + return price_point_t(least_recent, price); +} + +#if 0 + print_vertices(fg, f_commmap); + print_edges(fg, f_commmap); + print_graph(fg, f_commmap); + + graph_traits::vertex_iterator f_vi, f_vend; + for(tie(f_vi, f_vend) = vertices(fg); f_vi != f_vend; ++f_vi) + std::cerr << get(f_commmap, *f_vi) << " is in the filtered graph" + << std::endl; + + for (tie(f_vi, f_vend) = vertices(fg); f_vi != f_vend; ++f_vi) { + std::cerr << "distance(" << get(f_commmap, *f_vi) << ") = " + << distanceMap[*f_vi] << ", "; + std::cerr << "parent(" << get(f_commmap, *f_vi) << ") = " + << get(f_commmap, predecessorMap[*f_vi]) + << std::endl; + } + + // Write shortest path + FCommMap f_commmap = get(vertex_comm, fg); + + std::cerr << "Shortest path from CAD to EUR:" << std::endl; + for (PathType::reverse_iterator pathIterator = path.rbegin(); + pathIterator != path.rend(); + ++pathIterator) + { + std::cerr << get(f_commmap, source(*pathIterator, fg)) + << " -> " << get(f_commmap, target(*pathIterator, fg)) + << " = " << get(edge_weight, fg, *pathIterator) + << std::endl; + } + std::cerr << std::endl; + + std::cerr << "Distance: " << distanceMap[vd4] << std::endl; +#endif + +#if 0 + #include + + // Writing graph to file + { + std::ofstream f("test.dot"); + + dynamic_properties p; + p.property("label", get(edge_weight, g)); + p.property("weight", get(edge_weight, g)); + p.property("node_id", get(vertex_comm, g)); + write_graphviz(f,g,p); + f.close(); + } +#endif + +} // namespace ledger diff --git a/src/history.h b/src/history.h new file mode 100644 index 00000000..486602dd --- /dev/null +++ b/src/history.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2003-2012, 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 math + */ + +/** + * @file history.h + * @author John Wiegley + * + * @ingroup math + * + * @brief Types for managing commodity historys + * + * Long. + */ +#ifndef _HISTORY_H +#define _HISTORY_H + +#include "amount.h" +#include "commodity.h" + +namespace boost { + enum edge_price_point_t { edge_price_point }; + enum edge_price_ratio_t { edge_price_ratio }; + BOOST_INSTALL_PROPERTY(edge, price_point); + BOOST_INSTALL_PROPERTY(edge, price_ratio); +} + +namespace ledger { + +typedef std::map price_map_t; + +template +class recent_edge_weight +{ +public: + EdgeWeightMap weight; + PricePointMap price_point; + PriceRatioMap ratios; + + datetime_t reftime; + optional oldest; + + recent_edge_weight() { } + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + datetime_t _reftime, + const optional& _oldest = none) + : weight(_weight), price_point(_price_point), ratios(_ratios), + reftime(_reftime), oldest(_oldest) { } + + template + bool operator()(const Edge& e) const { + const price_map_t& prices(get(ratios, e)); + price_map_t::const_iterator low = prices.upper_bound(reftime); + if (prices.empty() || + (low != prices.end() && low == prices.begin())) { + return false; + } else { + if (low == prices.end()) + --low; + assert(((*low).first <= reftime)); + + if (oldest && (*low).first <= *oldest) + return false; + + long secs = (reftime - (*low).first).total_seconds(); + assert(secs >= 0); + + put(weight, e, secs); + put(price_point, e, price_point_t((*low).first, (*low).second)); + + return true; + } + } +}; + +class commodity_history_t : public noncopyable +{ +public: + typedef adjacency_list + >, + + // All edges are weights computed as the absolute difference between + // the reference time of a search and a known price point. A + // filtered_graph is used to select the recent price point to the + // reference time before performing the search. + property > >, + + // Graph itself has a std::string name + property + > Graph; + + Graph price_graph; + + typedef graph_traits::vertex_descriptor vertex_descriptor; + typedef graph_traits::edge_descriptor edge_descriptor; + + typedef property_map::type IndexMap; + typedef property_map::type NameMap; + + typedef iterator_property_map PredecessorMap; + typedef iterator_property_map DistanceMap; + + typedef property_map::type EdgeWeightMap; + typedef property_map::type PricePointMap; + typedef property_map::type PriceRatioMap; + + IndexMap indexmap; + PricePointMap pricemap; + PriceRatioMap ratiomap; + + typedef filtered_graph > FGraph; + typedef property_map::type FNameMap; + + commodity_history_t() + : indexmap(get(vertex_index, price_graph)), + pricemap(get(edge_price_point, price_graph)), + ratiomap(get(edge_price_ratio, price_graph)) {} + + void add_commodity(const commodity_t& comm); + + void add_price(const commodity_t& source, + const datetime_t& when, + const amount_t& price); + void remove_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& date); + + optional + find_price(const commodity_t& source, + const datetime_t& moment, + const optional& oldest = none, + const optional& commodity = none); +}; + +} // namespace ledger + +#endif // _HISTORY_H diff --git a/src/iterators.cc b/src/iterators.cc index 72e0481c..b7ed011e 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -90,6 +90,8 @@ void posts_commodities_iterator::reset(journal_t& journal) std::map xacts_by_commodity; +#if 0 + // jww (2012-03-04): TODO foreach (commodity_t * comm, commodities) { if (optional history = comm->varied_history()) { @@ -136,6 +138,7 @@ void posts_commodities_iterator::reset(journal_t& journal) } } } +#endif xacts.reset(xact_temps.begin(), xact_temps.end()); diff --git a/src/pool.cc b/src/pool.cc index ba408fc5..67cfe3d1 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -35,6 +35,7 @@ #include "commodity.h" #include "annotate.h" #include "pool.h" +#include "history.h" #include "quotes.h" namespace ledger { @@ -74,15 +75,15 @@ commodity_t * commodity_pool_t::create(const string& symbol) commodity.get())); assert(result.second); + commodity_price_history.add_commodity(*commodity.get()); + return commodity.release(); } commodity_t * commodity_pool_t::find_or_create(const string& symbol) { DEBUG("pool.commodities", "Find-or-create commodity " << symbol); - - commodity_t * commodity = find(symbol); - if (commodity) + if (commodity_t * commodity = find(symbol)) return commodity; return create(symbol); } @@ -222,6 +223,15 @@ commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, return create(comm, details, name); } +optional +commodity_pool_t::find_price(const commodity_t& source, + const optional& commodity, + const optional& moment, + const optional& oldest) const +{ + return commodity_price_history.find_price(source, commodity, moment, oldest); +} + void commodity_pool_t::exchange(commodity_t& commodity, const amount_t& per_unit_cost, const datetime_t& moment) @@ -382,76 +392,4 @@ commodity_pool_t::parse_price_expression(const std::string& str, return NULL; } -void commodity_pool_t::print_pricemap(std::ostream& out, - const keep_details_t& keep, - const optional& moment) -{ - typedef std::map comm_map_t; - - comm_map_t comm_map; - - foreach (const commodities_map::value_type& comm_pair, commodities) { - commodity_t * comm(&comm_pair.second->strip_annotations(keep)); - comm_map.insert(comm_map_t::value_type(comm, NULL)); - } - - out << "digraph commodities {\n"; - - foreach (const comm_map_t::value_type& comm_pair, comm_map) { - commodity_t * comm(comm_pair.first); - if (comm->has_flags(COMMODITY_BUILTIN)) - continue; - - out << " "; - if (commodity_t::symbol_needs_quotes(comm->symbol())) - out << comm->symbol() << ";\n"; - else - out << "\"" << comm->symbol() << "\";\n"; - - if (! comm->has_flags(COMMODITY_NOMARKET) && - (! commodity_pool_t::current_pool->default_commodity || - comm != commodity_pool_t::current_pool->default_commodity)) { - if (optional vhist = - comm->varied_history()) { - foreach (const commodity_t::history_by_commodity_map::value_type& pair, - vhist->histories) { - datetime_t most_recent; - amount_t most_recent_amt; - foreach (const commodity_t::history_map::value_type& inner_pair, - pair.second.prices) { - if ((most_recent.is_not_a_date_time() || - inner_pair.first > most_recent) && - (! moment || inner_pair.first <= moment)) { - most_recent = inner_pair.first; - most_recent_amt = inner_pair.second; - } - } - - if (! most_recent.is_not_a_date_time()) { - out << " "; - if (commodity_t::symbol_needs_quotes(comm->symbol())) - out << comm->symbol(); - else - out << "\"" << comm->symbol() << "\""; - - out << " -> "; - - if (commodity_t::symbol_needs_quotes(pair.first->symbol())) - out << pair.first->symbol(); - else - out << "\"" << pair.first->symbol() << "\""; - - out << " [label=\"" - << most_recent_amt.number() << "\\n" - << format_date(most_recent.date(), FMT_WRITTEN) - << "\" fontcolor=\"#008e28\"];\n"; - } - } - } - } - } - - out << "}\n"; -} - } // namespace ledger diff --git a/src/pool.h b/src/pool.h index 87b315f9..709f5c71 100644 --- a/src/pool.h +++ b/src/pool.h @@ -46,6 +46,8 @@ #ifndef _POOL_H #define _POOL_H +#include "history.h" + namespace ledger { struct cost_breakdown_t @@ -66,15 +68,16 @@ public: */ typedef std::map commodities_map; - commodities_map commodities; - commodity_t * null_commodity; - commodity_t * default_commodity; + commodities_map commodities; + commodity_history_t commodity_price_history; + commodity_t * null_commodity; + commodity_t * default_commodity; - bool keep_base; // --base + bool keep_base; // --base - optional price_db; // --price-db= - long quote_leeway; // --leeway= - bool get_quotes; // --download + optional price_db; // --price-db= + long quote_leeway; // --leeway= + bool get_quotes; // --download static shared_ptr current_pool; @@ -131,12 +134,6 @@ public: const bool add_prices = true, const optional& moment = none); - // Output the commodity price map for a given date as a DOT file - - void print_pricemap(std::ostream& out, - const keep_details_t& keep, - const optional& moment = none); - #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ diff --git a/src/report.cc b/src/report.cc index 647df3d2..689028d0 100644 --- a/src/report.cc +++ b/src/report.cc @@ -878,11 +878,12 @@ value_t report_t::echo_command(call_scope_t& args) value_t report_t::pricemap_command(call_scope_t& args) { std::ostream& out(output_stream); - +#if 0 + // jww (2012-03-04): TODO commodity_pool_t::current_pool->print_pricemap (out, what_to_keep(), args.has(0) ? optional(datetime_t(parse_date(args.get(0)))) : none); - +#endif return true; } @@ -913,6 +914,11 @@ option_t * report_t::lookup_option(const char * p) case 'G': OPT_CH(gain); break; +#if 0 + case 'H': + OPT_CH(historical); + break; +#endif case 'I': OPT_CH(price); break; diff --git a/src/system.hh.in b/src/system.hh.in index 3aa60f71..8f684486 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -138,37 +138,52 @@ typedef std::ostream::pos_type ostream_pos_type; #include #include #include + #include #include #include + #include #include #include #include #include + #if !(defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__) #include #endif #include + +#include +#include +#include +#include + #include + #include #include #define BOOST_IOSTREAMS_USE_DEPRECATED 1 #include + #include #include + #include #include #include #include + #include #include #include #include + #if defined(HAVE_BOOST_REGEX_UNICODE) #include #else #include + #endif // HAVE_BOOST_REGEX_UNICODE #include #include diff --git a/tools/Makefile.am b/tools/Makefile.am index 09021b1f..671db294 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -30,6 +30,7 @@ libledger_util_la_LDFLAGS = -release $(LIBVERSION) libledger_math_la_SOURCES = \ src/balance.cc \ src/quotes.cc \ + src/history.cc \ src/pool.cc \ src/annotate.cc \ src/commodity.cc \ @@ -104,6 +105,7 @@ pkginclude_HEADERS = \ src/amount.h \ src/commodity.h \ src/annotate.h \ + src/history.h \ src/pool.h \ src/quotes.h \ src/balance.h \ -- cgit v1.2.3 From e9108783122ae4d775046ced646b14552f1e184d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 4 Mar 2012 04:03:32 -0600 Subject: Changes to get all the code to compile --- src/commodity.cc | 54 ++++++++++++++++++++++++++++++++---------------------- src/commodity.h | 24 +++++++++++++++--------- src/history.cc | 31 ++++++++++++++++--------------- src/history.h | 2 +- src/pool.cc | 9 --------- 5 files changed, 64 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index c0ccae11..a01847c5 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -60,42 +60,52 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) } optional -commodity_t::find_price(const optional& target = none, - const optional& moment = none, - const optional& oldest = none) const +commodity_t::find_price(const optional& commodity, + const optional& moment, + const optional& oldest) const { - pair = base_t::time_and_commodity_t - (base_t::optional_time_pair_t(moment, oldest), - commodity ? &(*commodity) : NULL); - DEBUG_INDENT("commodity.prices.find", indent); + optional pair = + base_t::time_and_commodity_t(base_t::optional_time_pair_t(moment, oldest), + commodity ? &(*commodity) : NULL); + DEBUG("commodity.prices.find", "looking for memoized args: " - << (moment ? format_datetime(*moment) : "NONE") << ", " - << (oldest ? format_datetime(*oldest) : "NONE") << ", " - << (commodity ? commodity->symbol() : "NONE")); - - base_t::memoized_price_map::iterator i = base->price_map.find(*pair); - if (i != base->price_map.end()) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "found! returning: " - << ((*i).second ? (*i).second->price : amount_t(0L))); - return (*i).second; + << (moment ? format_datetime(*moment) : "NONE") << ", " + << (oldest ? format_datetime(*oldest) : "NONE") << ", " + << (commodity ? commodity->symbol() : "NONE")); + { + base_t::memoized_price_map::iterator i = base->price_map.find(*pair); + if (i != base->price_map.end()) { + DEBUG("commodity.prices.find", "found! returning: " + << ((*i).second ? (*i).second->price : amount_t(0L))); + return (*i).second; + } } + datetime_t when; + if (moment) + when = *moment; + else if (epoch) + when = *epoch; + else + when = CURRENT_TIME(); + + optional target; + if (commodity) + target = commodity; + else if (pool().default_commodity) + target = *pool().default_commodity; + optional point = - pool().commodity_price_history.find_price - (*this, commodity, moment ? *moment : epoch, oldest); + pool().commodity_price_history.find_price(*this, when, oldest, target); if (pair) { if (base->price_map.size() > base_t::max_price_map_size) { - DEBUG_INDENT("commodity.prices.find", indent); DEBUG("commodity.prices.find", "price map has grown too large, clearing it by half"); - for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) base->price_map.erase(base->price_map.begin()); } - DEBUG_INDENT("commodity.prices.find", indent); DEBUG("commodity.prices.find", "remembered: " << (point ? point->price : amount_t(0L))); base->price_map.insert diff --git a/src/commodity.h b/src/commodity.h index 1505fe24..a1ad0147 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -106,12 +106,13 @@ protected: #define COMMODITY_SAW_ANN_PRICE_FLOAT 0x400 #define COMMODITY_SAW_ANN_PRICE_FIXATED 0x800 - string symbol; - amount_t::precision_t precision; - optional name; - optional note; - optional smaller; - optional larger; + string symbol; + optional graph_index; + amount_t::precision_t precision; + optional name; + optional note; + optional smaller; + optional larger; typedef std::pair, optional > optional_time_pair_t; @@ -123,15 +124,13 @@ protected: static const std::size_t max_price_map_size = 16; mutable memoized_price_map price_map; - mutable bool searched; - public: explicit base_t(const string& _symbol) : supports_flags (commodity_t::decimal_comma_by_default ? static_cast(COMMODITY_STYLE_DECIMAL_COMMA) : static_cast(COMMODITY_STYLE_DEFAULTS)), - symbol(_symbol), precision(0), searched(false) { + symbol(_symbol), precision(0) { TRACE_CTOR(base_t, "const string&"); } virtual ~base_t() { @@ -226,6 +225,13 @@ public: return base_symbol(); } + optional graph_index() const {; + return base->graph_index; + } + void set_graph_index(const optional& arg = none) { + base->graph_index = arg; + } + optional name() const { return base->name; } diff --git a/src/history.cc b/src/history.cc index 44d19f5a..c92a0102 100644 --- a/src/history.cc +++ b/src/history.cc @@ -42,20 +42,22 @@ struct f_max : public std::binary_function { namespace ledger { -void commodity_history_t::add_commodity(const commodity_t& comm) +void commodity_history_t::add_commodity(commodity_t& comm) { - const vertex_descriptor vert = add_vertex(&comm, price_graph); - put(indexmap, vert, reinterpret_cast(&comm)); + if (! comm.graph_index()) { + std::size_t index = num_vertices(price_graph); + comm.set_graph_index(index); + const vertex_descriptor vert = add_vertex(&comm, price_graph); + put(indexmap, vert, index); + } } void commodity_history_t::add_price(const commodity_t& source, const datetime_t& when, const amount_t& price) { - vertex_descriptor sv = - vertex(reinterpret_cast(&source), price_graph); - vertex_descriptor tv = - vertex(reinterpret_cast(&price.commodity()), price_graph); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + vertex_descriptor tv = vertex(*price.commodity().graph_index(), price_graph); std::pair e1 = add_edge(sv, tv, 0, price_graph); price_map_t& prices(get(ratiomap, e1.first)); @@ -72,10 +74,8 @@ void commodity_history_t::remove_price(const commodity_t& source, const commodity_t& target, const datetime_t& date) { - vertex_descriptor sv = - vertex(reinterpret_cast(&source), price_graph); - vertex_descriptor tv = - vertex(reinterpret_cast(&target), price_graph); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + vertex_descriptor tv = vertex(*target.graph_index(), price_graph); std::pair e1 = add_edge(sv, tv, 0, price_graph); price_map_t& prices(get(ratiomap, e1.first)); @@ -90,10 +90,11 @@ commodity_history_t::find_price(const commodity_t& source, const optional& oldest, const optional& target) { - vertex_descriptor sv = - vertex(reinterpret_cast(&source), price_graph); - vertex_descriptor tv = - vertex(reinterpret_cast(&*target), price_graph); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + // jww (2012-03-04): What to do when target is null? In that case, + // should we just return whatever is the most recent price for that + // commodity? + vertex_descriptor tv = vertex(*target->graph_index(), price_graph); // Filter out edges which came into being after the reference time diff --git a/src/history.h b/src/history.h index 486602dd..f94d12c3 100644 --- a/src/history.h +++ b/src/history.h @@ -163,7 +163,7 @@ public: pricemap(get(edge_price_point, price_graph)), ratiomap(get(edge_price_ratio, price_graph)) {} - void add_commodity(const commodity_t& comm); + void add_commodity(commodity_t& comm); void add_price(const commodity_t& source, const datetime_t& when, diff --git a/src/pool.cc b/src/pool.cc index 67cfe3d1..2c094d47 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -223,15 +223,6 @@ commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, return create(comm, details, name); } -optional -commodity_pool_t::find_price(const commodity_t& source, - const optional& commodity, - const optional& moment, - const optional& oldest) const -{ - return commodity_price_history.find_price(source, commodity, moment, oldest); -} - void commodity_pool_t::exchange(commodity_t& commodity, const amount_t& per_unit_cost, const datetime_t& moment) -- cgit v1.2.3 From 5d8cb30774cf630cddd26407202c1cad8568bbef Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 4 Mar 2012 05:22:30 -0600 Subject: Implemented first cut at price conversion logic --- src/commodity.cc | 4 +- src/history.cc | 152 ++++++++++++++++++++++++++++----------------- src/history.h | 26 +++++--- src/report.cc | 7 +-- src/system.hh.in | 2 +- test/regress/D943AE0F.test | 2 +- 6 files changed, 119 insertions(+), 74 deletions(-) (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index a01847c5..5e55db31 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -96,7 +96,9 @@ commodity_t::find_price(const optional& commodity, target = *pool().default_commodity; optional point = - pool().commodity_price_history.find_price(*this, when, oldest, target); + target ? + pool().commodity_price_history.find_price(*this, *target, when, oldest) : + pool().commodity_price_history.find_price(*this, when, oldest); if (pair) { if (base->price_map.size() > base_t::max_price_map_size) { diff --git a/src/history.cc b/src/history.cc index c92a0102..a3d9139b 100644 --- a/src/history.cc +++ b/src/history.cc @@ -85,19 +85,50 @@ void commodity_history_t::remove_price(const commodity_t& source, } optional -commodity_history_t::find_price(const commodity_t& source, - const datetime_t& moment, - const optional& oldest, - const optional& target) +commodity_history_t::find_price(const commodity_t& source, + const datetime_t& moment, + const optional& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); - // jww (2012-03-04): What to do when target is null? In that case, - // should we just return whatever is the most recent price for that - // commodity? - vertex_descriptor tv = vertex(*target->graph_index(), price_graph); // Filter out edges which came into being after the reference time + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + moment, oldest)); + + datetime_t most_recent = moment; + amount_t price; + + graph_traits::adjacency_iterator f_vi, f_vend; + for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { + std::pair edgePair = edge(sv, *f_vi, fg); + Graph::edge_descriptor edge = edgePair.first; + + const price_point_t& point(get(pricemap, edge)); + + if (price.is_null() || point.when > most_recent) { + most_recent = point.when; + price = point.price; + } + } + + if (price.is_null()) + return none; + else + return price_point_t(most_recent, price); +} + +optional +commodity_history_t::find_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& moment, + const optional& oldest) +{ + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + vertex_descriptor tv = vertex(*target.graph_index(), price_graph); + // Filter out edges which came into being after the reference time FGraph fg(price_graph, recent_edge_weight (get(edge_weight, price_graph), pricemap, ratiomap, @@ -118,6 +149,8 @@ commodity_history_t::find_price(const commodity_t& source, datetime_t least_recent = moment; amount_t price; + FNameMap ptrs = get(vertex_name, fg); + vertex_descriptor v = tv; for (vertex_descriptor u = predecessorMap[v]; u != v; @@ -128,70 +161,73 @@ commodity_history_t::find_price(const commodity_t& source, const price_point_t& point(get(pricemap, edge)); + const commodity_t * last_source = &source; + + bool first_run = false; if (price.is_null()) { least_recent = point.when; price = point.price; + first_run = true; } - else if (point.when < least_recent) + else if (point.when < least_recent) { least_recent = point.when; + } - // jww (2012-03-04): TODO - //price *= point.price; - } - - return price_point_t(least_recent, price); -} + DEBUG("history.find", "u commodity = " << get(ptrs, u)->symbol()); + DEBUG("history.find", "v commodity = " << get(ptrs, v)->symbol()); + DEBUG("history.find", "last source = " << last_source->symbol()); -#if 0 - print_vertices(fg, f_commmap); - print_edges(fg, f_commmap); - print_graph(fg, f_commmap); - - graph_traits::vertex_iterator f_vi, f_vend; - for(tie(f_vi, f_vend) = vertices(fg); f_vi != f_vend; ++f_vi) - std::cerr << get(f_commmap, *f_vi) << " is in the filtered graph" - << std::endl; - - for (tie(f_vi, f_vend) = vertices(fg); f_vi != f_vend; ++f_vi) { - std::cerr << "distance(" << get(f_commmap, *f_vi) << ") = " - << distanceMap[*f_vi] << ", "; - std::cerr << "parent(" << get(f_commmap, *f_vi) << ") = " - << get(f_commmap, predecessorMap[*f_vi]) - << std::endl; - } + // Determine which direction we are converting in + amount_t pprice(point.price); + DEBUG("history.find", "pprice = " << pprice); - // Write shortest path - FCommMap f_commmap = get(vertex_comm, fg); + DEBUG("history.find", "price was = " << price); + if (! first_run) { + if (pprice.commodity() == *last_source) + price *= pprice.inverted(); + else + price *= pprice; + } + else if (price.commodity() == *last_source) { + price = price.inverted(); + } + DEBUG("history.find", "price is = " << price); - std::cerr << "Shortest path from CAD to EUR:" << std::endl; - for (PathType::reverse_iterator pathIterator = path.rbegin(); - pathIterator != path.rend(); - ++pathIterator) - { - std::cerr << get(f_commmap, source(*pathIterator, fg)) - << " -> " << get(f_commmap, target(*pathIterator, fg)) - << " = " << get(edge_weight, fg, *pathIterator) - << std::endl; + if (*last_source == *get(ptrs, v)) + last_source = get(ptrs, u); + else + last_source = get(ptrs, v); } - std::cerr << std::endl; - std::cerr << "Distance: " << distanceMap[vd4] << std::endl; -#endif + price.set_commodity(const_cast(target)); + DEBUG("history.find", "final price is = " << price); -#if 0 - #include + if (price.is_null()) + return none; + else + return price_point_t(least_recent, price); +} - // Writing graph to file - { - std::ofstream f("test.dot"); - - dynamic_properties p; - p.property("label", get(edge_weight, g)); - p.property("weight", get(edge_weight, g)); - p.property("node_id", get(vertex_comm, g)); - write_graphviz(f,g,p); - f.close(); +void commodity_history_t::print_map(std::ostream& out, + const optional& moment) +{ +#if 0 + dynamic_properties p; + p.property("label", get(edge_weight, price_graph)); + p.property("weight", get(edge_weight, price_graph)); + p.property("node_id", get(vertex_index, price_graph)); + + if (moment) { + // Filter out edges which came into being after the reference time + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + *moment)); + write_graphviz(out, fg, p); + } else { + write_graphviz(out, price_graph, p); } #endif +} } // namespace ledger diff --git a/src/history.h b/src/history.h index f94d12c3..70831445 100644 --- a/src/history.h +++ b/src/history.h @@ -83,18 +83,20 @@ public: reftime(_reftime), oldest(_oldest) { } template - bool operator()(const Edge& e) const { + bool operator()(const Edge& e) const + { const price_map_t& prices(get(ratios, e)); + if (prices.empty()) + return false; + price_map_t::const_iterator low = prices.upper_bound(reftime); - if (prices.empty() || - (low != prices.end() && low == prices.begin())) { + if (low != prices.end() && low == prices.begin()) { return false; } else { - if (low == prices.end()) - --low; + --low; assert(((*low).first <= reftime)); - if (oldest && (*low).first <= *oldest) + if (oldest && (*low).first < *oldest) return false; long secs = (reftime - (*low).first).total_seconds(); @@ -175,8 +177,16 @@ public: optional find_price(const commodity_t& source, const datetime_t& moment, - const optional& oldest = none, - const optional& commodity = none); + const optional& oldest = none); + + optional + find_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& moment, + const optional& oldest = none); + + void print_map(std::ostream& out, + const optional& moment = none); }; } // namespace ledger diff --git a/src/report.cc b/src/report.cc index 689028d0..2d825751 100644 --- a/src/report.cc +++ b/src/report.cc @@ -878,12 +878,9 @@ value_t report_t::echo_command(call_scope_t& args) value_t report_t::pricemap_command(call_scope_t& args) { std::ostream& out(output_stream); -#if 0 - // jww (2012-03-04): TODO - commodity_pool_t::current_pool->print_pricemap - (out, what_to_keep(), args.has(0) ? + commodity_pool_t::current_pool->commodity_price_history.print_map + (out, args.has(0) ? optional(datetime_t(parse_date(args.get(0)))) : none); -#endif return true; } diff --git a/src/system.hh.in b/src/system.hh.in index 8f684486..5e5a0c1d 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -157,7 +157,7 @@ typedef std::ostream::pos_type ostream_pos_type; #include #include #include -#include +#include #include diff --git a/test/regress/D943AE0F.test b/test/regress/D943AE0F.test index 960fbe13..10082f75 100644 --- a/test/regress/D943AE0F.test +++ b/test/regress/D943AE0F.test @@ -6,7 +6,7 @@ D 1000.00 EUR P 2008/04/20 00:00:00 CAD 1.20 EUR -test reg -V +test reg -V --now=2008/04/20 08-Apr-15 Paid expenses back .. Exp:Cie-Reimbursements 2200.00 EUR 2200.00 EUR Assets:Checking -2200.00 EUR 0 08-Apr-20 Commodities revalued 200.00 EUR 200.00 EUR -- cgit v1.2.3 From 8d6bf11334562d7781b339cf822a93ff42fee2b5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 01:48:21 -0600 Subject: All tests are working again but one --- src/amount.cc | 9 +- src/amount.h | 7 +- src/commodity.cc | 54 +++++++--- src/commodity.h | 4 + src/filters.cc | 52 +++++----- src/history.cc | 179 ++++++++++++++++++++++++--------- src/history.h | 61 ++++++++--- src/iterators.cc | 104 +++++++++---------- test/baseline/feat-fixated-prices.test | 2 + test/regress/25A099C9.test | 12 +-- test/unit/t_commodity.cc | 2 +- 11 files changed, 318 insertions(+), 168 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 4d26a688..9704dd21 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -605,16 +605,13 @@ void amount_t::in_place_negate() } } -amount_t amount_t::inverted() const +void amount_t::in_place_invert() { if (! quantity) throw_(amount_error, _("Cannot invert an uninitialized amount")); - amount_t t(*this); - t._dup(); - mpq_inv(MP(t.quantity), MP(t.quantity)); - - return t; + _dup(); + mpq_inv(MP(quantity), MP(quantity)); } void amount_t::in_place_round() diff --git a/src/amount.h b/src/amount.h index 3a8e06b9..1db59b7e 100644 --- a/src/amount.h +++ b/src/amount.h @@ -327,7 +327,12 @@ public: return *this; } - amount_t inverted() const; + amount_t inverted() const { + amount_t temp(*this); + temp.in_place_invert(); + return temp; + } + void in_place_invert(); /** Yields an amount whose display precision when output is truncated to the display precision of its commodity. This is normally the diff --git a/src/commodity.cc b/src/commodity.cc index 5e55db31..7d473d74 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -43,11 +43,20 @@ bool commodity_t::decimal_comma_by_default = false; void commodity_t::add_price(const datetime_t& date, const amount_t& price, const bool reflexive) { - if (! reflexive) + if (reflexive) { + DEBUG("history.find", "Marking " + << price.commodity().symbol() << " as a primary commodity"); + price.commodity().add_flags(COMMODITY_PRIMARY); + } else { + DEBUG("history.find", "Marking " << symbol() << " as a primary commodity"); add_flags(COMMODITY_PRIMARY); + } + + DEBUG("history.find", "Adding price: " << symbol() + << " for " << price << " on " << date); + pool().commodity_price_history.add_price(*this, date, price); - DEBUG("commodity.prices.find", "Price added, clearing price_map"); base->price_map.clear(); // a price was added, invalid the map } @@ -55,27 +64,52 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) { pool().commodity_price_history.remove_price(*this, commodity, date); - DEBUG("commodity.prices.find", "Price removed, clearing price_map"); + DEBUG("history.find", "Removing price: " << symbol() << " on " << date); + base->price_map.clear(); // a price was added, invalid the map } +void commodity_t::map_prices(function fn, + const optional& moment, + const optional& _oldest) +{ + datetime_t when; + if (moment) + when = *moment; + else if (epoch) + when = *epoch; + else + when = CURRENT_TIME(); + + pool().commodity_price_history.map_prices(fn, *this, when, _oldest); +} + optional commodity_t::find_price(const optional& commodity, const optional& moment, const optional& oldest) const { + optional target; + if (commodity) + target = commodity; + else if (pool().default_commodity) + target = *pool().default_commodity; + + if (target && *this == *target) + return none; + optional pair = base_t::time_and_commodity_t(base_t::optional_time_pair_t(moment, oldest), commodity ? &(*commodity) : NULL); - DEBUG("commodity.prices.find", "looking for memoized args: " + DEBUG("history.find", "looking for memoized args: " << (moment ? format_datetime(*moment) : "NONE") << ", " << (oldest ? format_datetime(*oldest) : "NONE") << ", " << (commodity ? commodity->symbol() : "NONE")); { base_t::memoized_price_map::iterator i = base->price_map.find(*pair); if (i != base->price_map.end()) { - DEBUG("commodity.prices.find", "found! returning: " + DEBUG("history.find", "found! returning: " << ((*i).second ? (*i).second->price : amount_t(0L))); return (*i).second; } @@ -89,12 +123,6 @@ commodity_t::find_price(const optional& commodity, else when = CURRENT_TIME(); - optional target; - if (commodity) - target = commodity; - else if (pool().default_commodity) - target = *pool().default_commodity; - optional point = target ? pool().commodity_price_history.find_price(*this, *target, when, oldest) : @@ -102,13 +130,13 @@ commodity_t::find_price(const optional& commodity, if (pair) { if (base->price_map.size() > base_t::max_price_map_size) { - DEBUG("commodity.prices.find", + DEBUG("history.find", "price map has grown too large, clearing it by half"); for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) base->price_map.erase(base->price_map.begin()); } - DEBUG("commodity.prices.find", + DEBUG("history.find", "remembered: " << (point ? point->price : amount_t(0L))); base->price_map.insert (base_t::memoized_price_map::value_type(*pair, point)); diff --git a/src/commodity.h b/src/commodity.h index a1ad0147..524daaab 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -271,6 +271,10 @@ public: const bool reflexive = true); void remove_price(const datetime_t& date, commodity_t& commodity); + void map_prices(function fn, + const optional& moment = none, + const optional& _oldest = none); + optional find_price(const optional& commodity = none, const optional& moment = none, diff --git a/src/filters.cc b/src/filters.cc index 0c6222d7..6e52c40f 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -37,6 +37,7 @@ #include "report.h" #include "compare.h" #include "pool.h" +#include "history.h" namespace ledger { @@ -688,6 +689,27 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) } } +namespace { + struct create_price_xact { + post_t& post; + const date_t& current; + price_map_t& all_prices; + + create_price_xact(post_t& _post, const date_t& _current, + price_map_t& _all_prices) + : post(_post), current(_current), all_prices(_all_prices) {} + + void operator()(datetime_t& date, const amount_t& price) { + if (date.date() > post.value_date() && date.date() < current) { + DEBUG("filters.revalued", + post.value_date() << " < " << date << " < " << current); + DEBUG("filters.revalued", "inserting " << price << " at " << date); + all_prices.insert(price_map_t::value_type(date, price)); + } + } + }; +} + void changed_value_posts::output_intermediate_prices(post_t& post, const date_t& current) { @@ -754,38 +776,17 @@ void changed_value_posts::output_intermediate_prices(post_t& post, // fall through... case value_t::BALANCE: { -#if 0 - // jww (2012-03-04): TODO - commodity_t::history_map all_prices; + price_map_t all_prices; foreach (const balance_t::amounts_map::value_type& amt_comm, - display_total.as_balance().amounts) { - if (optional hist = - amt_comm.first->varied_history()) { - foreach - (const commodity_t::history_by_commodity_map::value_type& comm_hist, - hist->histories) { - foreach (const commodity_t::history_map::value_type& price, - comm_hist.second.prices) { - if (price.first.date() > post.value_date() && - price.first.date() < current) { - DEBUG("filters.revalued", post.value_date() << " < " - << price.first.date() << " < " << current); - DEBUG("filters.revalued", "inserting " - << price.second << " at " << price.first.date()); - all_prices.insert(price); - } - } - } - } - } + display_total.as_balance().amounts) + amt_comm.first->map_prices(create_price_xact(post, current, all_prices)); // Choose the last price from each day as the price to use typedef std::map date_map; date_map pricing_dates; - BOOST_REVERSE_FOREACH - (const commodity_t::history_map::value_type& price, all_prices) { + BOOST_REVERSE_FOREACH(const price_map_t::value_type& price, all_prices) { // This insert will fail if a later price has already been inserted // for that date. DEBUG("filters.revalued", @@ -799,7 +800,6 @@ void changed_value_posts::output_intermediate_prices(post_t& post, output_revaluation(post, price.first); last_total = repriced_total; } -#endif break; } default: diff --git a/src/history.cc b/src/history.cc index a3d9139b..95ed584f 100644 --- a/src/history.cc +++ b/src/history.cc @@ -67,6 +67,9 @@ void commodity_history_t::add_price(const commodity_t& source, if (! result.second) { // There is already an entry for this moment, so update it (*result.first).second = price; + } else { + last_reftime = none; // invalidate the FGraph cache + last_oldest = none; } } @@ -82,64 +85,128 @@ void commodity_history_t::remove_price(const commodity_t& source, // jww (2012-03-04): If it fails, should we give a warning? prices.erase(date); + + last_reftime = none; // invalidate the FGraph cache + last_oldest = none; +} + +void commodity_history_t::map_prices(function fn, + const commodity_t& source, + const datetime_t& moment, + const optional& _oldest) +{ + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + + reftime = moment; + oldest = _oldest; + + graph_traits::adjacency_iterator f_vi, f_vend; + for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { + std::pair edgePair = edge(sv, *f_vi, fg); + Graph::edge_descriptor edge = edgePair.first; + + const price_map_t& prices(get(ratiomap, edge)); + + foreach (const price_map_t::value_type& pair, prices) { + const datetime_t& when(pair.first); + + if ((! _oldest || when >= *_oldest) && when <= moment) { + if (pair.second.commodity() == source) { + amount_t price(pair.second); + price.in_place_invert(); + if (source == *get(namemap, sv)) + price.set_commodity(const_cast(*get(namemap, *f_vi))); + else + price.set_commodity(const_cast(*get(namemap, sv))); + } + fn(when, pair.second); + } + } + } } optional commodity_history_t::find_price(const commodity_t& source, const datetime_t& moment, - const optional& oldest) + const optional& _oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); - // Filter out edges which came into being after the reference time - FGraph fg(price_graph, - recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - moment, oldest)); - + DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); +#if defined(DEBUG_ON) + if (source.has_flags(COMMODITY_PRIMARY)) + DEBUG("history.find", "sv commodity is primary"); +#endif + DEBUG("history.find", "tv commodity = none "); + datetime_t most_recent = moment; amount_t price; + reftime = moment; + oldest = _oldest; + graph_traits::adjacency_iterator f_vi, f_vend; for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { std::pair edgePair = edge(sv, *f_vi, fg); Graph::edge_descriptor edge = edgePair.first; + DEBUG("history.find", "u commodity = " << get(namemap, sv)->symbol()); + DEBUG("history.find", "v commodity = " << get(namemap, *f_vi)->symbol()); + const price_point_t& point(get(pricemap, edge)); if (price.is_null() || point.when > most_recent) { most_recent = point.when; price = point.price; } + + DEBUG("history.find", "price was = " << price.unrounded()); + + if (price.commodity() == source) { + price.in_place_invert(); + if (source == *get(namemap, sv)) + price.set_commodity(const_cast(*get(namemap, *f_vi))); + else + price.set_commodity(const_cast(*get(namemap, sv))); + } + + DEBUG("history.find", "price is = " << price.unrounded()); } - if (price.is_null()) + last_reftime = reftime; // invalidate the FGraph cache + last_oldest = oldest; + + if (price.is_null()) { + DEBUG("history.find", "there is no final price"); return none; - else + } else { + DEBUG("history.find", "final price is = " << price.unrounded()); return price_point_t(most_recent, price); + } } optional commodity_history_t::find_price(const commodity_t& source, const commodity_t& target, const datetime_t& moment, - const optional& oldest) + const optional& _oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); - // Filter out edges which came into being after the reference time - FGraph fg(price_graph, - recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - moment, oldest)); - + DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); + DEBUG("history.find", "tv commodity = " << get(namemap, tv)->symbol()); + std::vector predecessors(num_vertices(fg)); std::vector distances(num_vertices(fg)); PredecessorMap predecessorMap(&predecessors[0]); DistanceMap distanceMap(&distances[0]); + reftime = moment; + oldest = _oldest; + dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) .distance_map(distanceMap) @@ -149,7 +216,7 @@ commodity_history_t::find_price(const commodity_t& source, datetime_t least_recent = moment; amount_t price; - FNameMap ptrs = get(vertex_name, fg); + const commodity_t * last_target = ⌖ vertex_descriptor v = tv; for (vertex_descriptor u = predecessorMap[v]; @@ -161,73 +228,85 @@ commodity_history_t::find_price(const commodity_t& source, const price_point_t& point(get(pricemap, edge)); - const commodity_t * last_source = &source; - bool first_run = false; if (price.is_null()) { least_recent = point.when; - price = point.price; first_run = true; } else if (point.when < least_recent) { least_recent = point.when; } - DEBUG("history.find", "u commodity = " << get(ptrs, u)->symbol()); - DEBUG("history.find", "v commodity = " << get(ptrs, v)->symbol()); - DEBUG("history.find", "last source = " << last_source->symbol()); + DEBUG("history.find", "u commodity = " << get(namemap, u)->symbol()); + DEBUG("history.find", "v commodity = " << get(namemap, v)->symbol()); + DEBUG("history.find", "last target = " << last_target->symbol()); // Determine which direction we are converting in amount_t pprice(point.price); - DEBUG("history.find", "pprice = " << pprice); + DEBUG("history.find", "pprice = " << pprice.unrounded()); - DEBUG("history.find", "price was = " << price); if (! first_run) { - if (pprice.commodity() == *last_source) + DEBUG("history.find", "price was = " << price.unrounded()); + if (pprice.commodity() != *last_target) price *= pprice.inverted(); else price *= pprice; } - else if (price.commodity() == *last_source) { - price = price.inverted(); + else if (pprice.commodity() != *last_target) { + price = pprice.inverted(); + } + else { + price = pprice; } - DEBUG("history.find", "price is = " << price); + DEBUG("history.find", "price is = " << price.unrounded()); - if (*last_source == *get(ptrs, v)) - last_source = get(ptrs, u); + if (*last_target == *get(namemap, v)) + last_target = get(namemap, u); else - last_source = get(ptrs, v); + last_target = get(namemap, v); + + DEBUG("history.find", "last target now = " << last_target->symbol()); } - price.set_commodity(const_cast(target)); - DEBUG("history.find", "final price is = " << price); + last_reftime = reftime; // invalidate the FGraph cache + last_oldest = oldest; - if (price.is_null()) + if (price.is_null()) { + DEBUG("history.find", "there is no final price"); return none; - else + } else { + price.set_commodity(const_cast(target)); + DEBUG("history.find", "final price is = " << price.unrounded()); + return price_point_t(least_recent, price); + } } +template +class label_writer { +public: + label_writer(Name _name) : name(_name) {} + + template + void operator()(std::ostream& out, const VertexOrEdge& v) const { + out << "[label=\"" << name[v]->symbol() << "\"]"; + } + +private: + Name name; +}; + void commodity_history_t::print_map(std::ostream& out, const optional& moment) { -#if 0 - dynamic_properties p; - p.property("label", get(edge_weight, price_graph)); - p.property("weight", get(edge_weight, price_graph)); - p.property("node_id", get(vertex_index, price_graph)); - if (moment) { - // Filter out edges which came into being after the reference time - FGraph fg(price_graph, - recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - *moment)); - write_graphviz(out, fg, p); + reftime = *moment; + write_graphviz(out, fg, label_writer(namemap)); + last_reftime = reftime; } else { - write_graphviz(out, price_graph, p); + write_graphviz(out, price_graph, + label_writer(get(vertex_name, price_graph))); } -#endif } } // namespace ledger diff --git a/src/history.h b/src/history.h index 70831445..eaca07ac 100644 --- a/src/history.h +++ b/src/history.h @@ -70,36 +70,50 @@ public: PricePointMap price_point; PriceRatioMap ratios; - datetime_t reftime; - optional oldest; + datetime_t * reftime; + optional * last_reftime; + optional * oldest; + optional * last_oldest; recent_edge_weight() { } - recent_edge_weight(EdgeWeightMap _weight, - PricePointMap _price_point, - PriceRatioMap _ratios, - datetime_t _reftime, - const optional& _oldest = none) + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + datetime_t * _reftime, + optional * _last_reftime, + optional * _oldest, + optional * _last_oldest) : weight(_weight), price_point(_price_point), ratios(_ratios), - reftime(_reftime), oldest(_oldest) { } + reftime(_reftime), last_reftime(_last_reftime), + oldest(_oldest), last_oldest(_last_oldest) { } template bool operator()(const Edge& e) const { + if (*last_reftime && *reftime == **last_reftime && + *oldest == *last_oldest) + return get(weight, e) != std::numeric_limits::max(); + const price_map_t& prices(get(ratios, e)); - if (prices.empty()) + if (prices.empty()) { + put(weight, e, std::numeric_limits::max()); return false; + } - price_map_t::const_iterator low = prices.upper_bound(reftime); + price_map_t::const_iterator low = prices.upper_bound(*reftime); if (low != prices.end() && low == prices.begin()) { + put(weight, e, std::numeric_limits::max()); return false; } else { --low; - assert(((*low).first <= reftime)); + assert(((*low).first <= *reftime)); - if (oldest && (*low).first < *oldest) + if (*oldest && (*low).first < **oldest) { + put(weight, e, std::numeric_limits::max()); return false; + } - long secs = (reftime - (*low).first).total_seconds(); + long secs = (*reftime - (*low).first).total_seconds(); assert(secs >= 0); put(weight, e, secs); @@ -160,10 +174,24 @@ public: PriceRatioMap> > FGraph; typedef property_map::type FNameMap; + FGraph fg; + FNameMap namemap; + + // jww (2012-03-05): Prevents threading + mutable datetime_t reftime; + mutable optional last_reftime; + mutable optional oldest; + mutable optional last_oldest; + commodity_history_t() : indexmap(get(vertex_index, price_graph)), pricemap(get(edge_price_point, price_graph)), - ratiomap(get(edge_price_ratio, price_graph)) {} + ratiomap(get(edge_price_ratio, price_graph)), + fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)), + namemap(get(vertex_name, fg)) {} void add_commodity(commodity_t& comm); @@ -174,6 +202,11 @@ public: const commodity_t& target, const datetime_t& date); + void map_prices(function fn, + const commodity_t& source, + const datetime_t& moment, + const optional& _oldest = none); + optional find_price(const commodity_t& source, const datetime_t& moment, diff --git a/src/iterators.cc b/src/iterators.cc index b7ed011e..b994d59a 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -75,6 +75,55 @@ void journal_posts_iterator::increment() } } +namespace { + struct create_price_xact { + account_t * account; + temporaries_t& temps; + xacts_list& xact_temps; + + std::map xacts_by_commodity; + + create_price_xact(account_t * _account, temporaries_t& _temps, + xacts_list& _xact_temps) + : account(_account), temps(_temps), xact_temps(_xact_temps) {} + + void operator()(datetime_t& date, const amount_t& price) { + xact_t * xact; + string symbol = price.commodity().symbol(); + + std::map::iterator i = + xacts_by_commodity.find(symbol); + if (i != xacts_by_commodity.end()) { + xact = (*i).second; + } else { + xact = &temps.create_xact(); + xact_temps.push_back(xact); + xact->payee = symbol; + xact->_date = date.date(); + xacts_by_commodity.insert + (std::pair(symbol, xact)); + } + + bool post_already_exists = false; + + foreach (post_t * post, xact->posts) { + if (post->date() == date.date() && post->amount == price) { + post_already_exists = true; + break; + } + } + + if (! post_already_exists) { + post_t& temp = temps.create_post(*xact, account); + temp._date = date.date(); + temp.amount = price; + + temp.xdata().datetime = date; + } + } + }; +} + void posts_commodities_iterator::reset(journal_t& journal) { journal_posts.reset(journal); @@ -88,57 +137,10 @@ void posts_commodities_iterator::reset(journal_t& journal) commodities.insert(&comm); } - std::map xacts_by_commodity; - -#if 0 - // jww (2012-03-04): TODO - foreach (commodity_t * comm, commodities) { - if (optional history = - comm->varied_history()) { - account_t * account = journal.master->find_account(comm->symbol()); - - foreach (commodity_t::history_by_commodity_map::value_type& pair, - history->histories) { - foreach (commodity_t::history_map::value_type& hpair, - pair.second.prices) { - xact_t * xact; - string symbol = hpair.second.commodity().symbol(); - - std::map::iterator i = - xacts_by_commodity.find(symbol); - if (i != xacts_by_commodity.end()) { - xact = (*i).second; - } else { - xact = &temps.create_xact(); - xact_temps.push_back(xact); - xact->payee = symbol; - xact->_date = hpair.first.date(); - xacts_by_commodity.insert - (std::pair(symbol, xact)); - } - - bool post_already_exists = false; - - foreach (post_t * post, xact->posts) { - if (post->_date == hpair.first.date() && - post->amount == hpair.second) { - post_already_exists = true; - break; - } - } - - if (! post_already_exists) { - post_t& temp = temps.create_post(*xact, account); - temp._date = hpair.first.date(); - temp.amount = hpair.second; - - temp.xdata().datetime = hpair.first; - } - } - } - } - } -#endif + foreach (commodity_t * comm, commodities) + comm->map_prices + (create_price_xact(journal.master->find_account(comm->symbol()), + temps, xact_temps)); xacts.reset(xact_temps.begin(), xact_temps.end()); diff --git a/test/baseline/feat-fixated-prices.test b/test/baseline/feat-fixated-prices.test index f4370870..4767d866 100644 --- a/test/baseline/feat-fixated-prices.test +++ b/test/baseline/feat-fixated-prices.test @@ -1,3 +1,5 @@ +P 1989/01/15 12:00:00 GAL $3 + 1990/01/01 Payee Expenses:Gas 100 GAL {=$2} Liabilities:MasterCard $-200 diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index fc06449b..48b6814e 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -2,16 +2,16 @@ test -f $sourcepath/src/amount.h reg -> 7 __ERROR__ While parsing file "$sourcepath/src/amount.h", line 66: Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 726: +While parsing file "$sourcepath/src/amount.h", line 731: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 732: +While parsing file "$sourcepath/src/amount.h", line 737: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 738: +While parsing file "$sourcepath/src/amount.h", line 743: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 744: +While parsing file "$sourcepath/src/amount.h", line 749: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 750: +While parsing file "$sourcepath/src/amount.h", line 755: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 757: +While parsing file "$sourcepath/src/amount.h", line 762: Error: Invalid date/time: line std::istream& end test diff --git a/test/unit/t_commodity.cc b/test/unit/t_commodity.cc index dc64dcfb..6a6f27aa 100644 --- a/test/unit/t_commodity.cc +++ b/test/unit/t_commodity.cc @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(testPriceHistory) amt = x1.value(CURRENT_TIME(), euro); BOOST_CHECK(amt); - BOOST_CHECK_EQUAL(string("EUR 1366.87"), amt->rounded().to_string()); + BOOST_CHECK_EQUAL(string("EUR 1787.50"), amt->rounded().to_string()); // Add a newer Euro pricing aapl.add_price(jan17_07, amount_t("EUR 23.00")); -- cgit v1.2.3 From 3ea5d88eb3ddfd8cac248fe96fdaa293e3b5bf70 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 02:26:15 -0600 Subject: The last test is closer to working now --- src/filters.cc | 22 ++++++++-------------- src/history.h | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 6e52c40f..8543cddb 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -690,22 +690,14 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) } namespace { - struct create_price_xact { - post_t& post; - const date_t& current; - price_map_t& all_prices; + struct insert_prices_in_map { + price_map_t& all_prices; - create_price_xact(post_t& _post, const date_t& _current, - price_map_t& _all_prices) - : post(_post), current(_current), all_prices(_all_prices) {} + insert_prices_in_map(price_map_t& _all_prices) + : all_prices(_all_prices) {} void operator()(datetime_t& date, const amount_t& price) { - if (date.date() > post.value_date() && date.date() < current) { - DEBUG("filters.revalued", - post.value_date() << " < " << date << " < " << current); - DEBUG("filters.revalued", "inserting " << price << " at " << date); - all_prices.insert(price_map_t::value_type(date, price)); - } + all_prices.insert(price_map_t::value_type(date, price)); } }; } @@ -780,7 +772,9 @@ void changed_value_posts::output_intermediate_prices(post_t& post, foreach (const balance_t::amounts_map::value_type& amt_comm, display_total.as_balance().amounts) - amt_comm.first->map_prices(create_price_xact(post, current, all_prices)); + amt_comm.first->map_prices(insert_prices_in_map(all_prices), + datetime_t(current), + datetime_t(post.value_date())); // Choose the last price from each day as the price to use typedef std::map date_map; diff --git a/src/history.h b/src/history.h index eaca07ac..6430202b 100644 --- a/src/history.h +++ b/src/history.h @@ -90,18 +90,30 @@ public: template bool operator()(const Edge& e) const { + DEBUG("history.find", " reftime = " << *reftime); + if (*last_reftime) + DEBUG("history.find", " last_reftime = " << **last_reftime); + if (*oldest) + DEBUG("history.find", " oldest = " << **oldest); + if (*last_oldest) + DEBUG("history.find", " last_oldest = " << **last_oldest); + if (*last_reftime && *reftime == **last_reftime && - *oldest == *last_oldest) + *oldest == *last_oldest) { + DEBUG("history.find", " using previous reftime"); return get(weight, e) != std::numeric_limits::max(); + } const price_map_t& prices(get(ratios, e)); if (prices.empty()) { + DEBUG("history.find", " prices map is empty for this edge"); put(weight, e, std::numeric_limits::max()); return false; } price_map_t::const_iterator low = prices.upper_bound(*reftime); if (low != prices.end() && low == prices.begin()) { + DEBUG("history.find", " don't use this edge"); put(weight, e, std::numeric_limits::max()); return false; } else { @@ -109,6 +121,7 @@ public: assert(((*low).first <= *reftime)); if (*oldest && (*low).first < **oldest) { + DEBUG("history.find", " edge is out of range"); put(weight, e, std::numeric_limits::max()); return false; } @@ -119,6 +132,8 @@ public: put(weight, e, secs); put(price_point, e, price_point_t((*low).first, (*low).second)); + DEBUG("history.find", " using edge at price point " + << (*low).first << " " << (*low).second); return true; } } -- cgit v1.2.3 From 5abbf445833f985f2aa22bde57d47cd9f83d0c92 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 05:02:11 -0600 Subject: Corrected copy of format_t objects Copying of sub-elements was causing pointer overlap. --- src/format.cc | 11 +++++++++++ src/format.h | 5 ++--- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/format.cc b/src/format.cc index 4ff1fc19..97dcfb22 100644 --- a/src/format.cc +++ b/src/format.cc @@ -40,6 +40,17 @@ namespace ledger { format_t::elision_style_t format_t::default_style = TRUNCATE_TRAILING; bool format_t::default_style_changed = false; +format_t::element_t::element_t(const element_t& elem) + : supports_flags<>(), + type(elem.type), + min_width(elem.min_width), + max_width(elem.max_width), + data(elem.data), + next(elem.next ? new element_t(*elem.next) : NULL) +{ + TRACE_CTOR(element_t, "copy"); +} + void format_t::element_t::dump(std::ostream& out) const { out << "Element: "; diff --git a/src/format.h b/src/format.h index 74d77768..0073b427 100644 --- a/src/format.h +++ b/src/format.h @@ -71,12 +71,11 @@ class format_t : public expr_base_t : supports_flags<>(), type(STRING), min_width(0), max_width(0) { TRACE_CTOR(element_t, ""); } + element_t(const element_t& elem); + ~element_t() throw() { TRACE_DTOR(element_t); } - element_t(const element_t& elem) : supports_flags<>() { - *this = elem; - } element_t& operator=(const element_t& elem) { if (this != &elem) { -- cgit v1.2.3 From eabd5c6a0c6796547a91cf6e642090cdb298f4da Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 05:02:45 -0600 Subject: Create a FGraph each time a lookup is done --- src/history.cc | 44 +++++++++++++++++++++++++++++++++++++------- src/history.h | 12 +++--------- 2 files changed, 40 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/history.cc b/src/history.cc index 95ed584f..c4f6b3fc 100644 --- a/src/history.cc +++ b/src/history.cc @@ -101,6 +101,13 @@ void commodity_history_t::map_prices(function + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)); + + FNameMap namemap(get(vertex_name, fg)); + graph_traits::adjacency_iterator f_vi, f_vend; for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { std::pair edgePair = edge(sv, *f_vi, fg); @@ -133,6 +140,16 @@ commodity_history_t::find_price(const commodity_t& source, { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + reftime = moment; + oldest = _oldest; + + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)); + + FNameMap namemap(get(vertex_name, fg)); + DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); #if defined(DEBUG_ON) if (source.has_flags(COMMODITY_PRIMARY)) @@ -143,9 +160,6 @@ commodity_history_t::find_price(const commodity_t& source, datetime_t most_recent = moment; amount_t price; - reftime = moment; - oldest = _oldest; - graph_traits::adjacency_iterator f_vi, f_vend; for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { std::pair edgePair = edge(sv, *f_vi, fg); @@ -195,6 +209,16 @@ commodity_history_t::find_price(const commodity_t& source, vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); + reftime = moment; + oldest = _oldest; + + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)); + + FNameMap namemap(get(vertex_name, fg)); + DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); DEBUG("history.find", "tv commodity = " << get(namemap, tv)->symbol()); @@ -204,9 +228,6 @@ commodity_history_t::find_price(const commodity_t& source, PredecessorMap predecessorMap(&predecessors[0]); DistanceMap distanceMap(&distances[0]); - reftime = moment; - oldest = _oldest; - dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) .distance_map(distanceMap) @@ -301,8 +322,17 @@ void commodity_history_t::print_map(std::ostream& out, { if (moment) { reftime = *moment; - write_graphviz(out, fg, label_writer(namemap)); + oldest = none; + + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)); + + write_graphviz(out, fg, label_writer(get(vertex_name, fg))); + last_reftime = reftime; + last_oldest = none; } else { write_graphviz(out, price_graph, label_writer(get(vertex_name, price_graph))); diff --git a/src/history.h b/src/history.h index 6430202b..fc984c1b 100644 --- a/src/history.h +++ b/src/history.h @@ -98,11 +98,13 @@ public: if (*last_oldest) DEBUG("history.find", " last_oldest = " << **last_oldest); +#if 0 if (*last_reftime && *reftime == **last_reftime && *oldest == *last_oldest) { DEBUG("history.find", " using previous reftime"); return get(weight, e) != std::numeric_limits::max(); } +#endif const price_map_t& prices(get(ratios, e)); if (prices.empty()) { @@ -189,9 +191,6 @@ public: PriceRatioMap> > FGraph; typedef property_map::type FNameMap; - FGraph fg; - FNameMap namemap; - // jww (2012-03-05): Prevents threading mutable datetime_t reftime; mutable optional last_reftime; @@ -201,12 +200,7 @@ public: commodity_history_t() : indexmap(get(vertex_index, price_graph)), pricemap(get(edge_price_point, price_graph)), - ratiomap(get(edge_price_ratio, price_graph)), - fg(price_graph, - recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)), - namemap(get(vertex_name, fg)) {} + ratiomap(get(edge_price_ratio, price_graph)) {} void add_commodity(commodity_t& comm); -- cgit v1.2.3 From 96ec764c4ad31e3d66782775fcc6885ad0818111 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 05:06:57 -0600 Subject: Don't copy the next pointer in element_t's copy ctor --- src/format.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/format.cc b/src/format.cc index 97dcfb22..f16b4ba9 100644 --- a/src/format.cc +++ b/src/format.cc @@ -45,8 +45,7 @@ format_t::element_t::element_t(const element_t& elem) type(elem.type), min_width(elem.min_width), max_width(elem.max_width), - data(elem.data), - next(elem.next ? new element_t(*elem.next) : NULL) + data(elem.data) { TRACE_CTOR(element_t, "copy"); } -- cgit v1.2.3 From 036200e959db6a3b9fe0d02f9eec3eb131983ab4 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 05:53:36 -0600 Subject: Make copying of format_t objects memory-safe --- src/format.cc | 11 ----------- src/format.h | 9 ++++----- 2 files changed, 4 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/format.cc b/src/format.cc index f16b4ba9..9824d5f7 100644 --- a/src/format.cc +++ b/src/format.cc @@ -40,16 +40,6 @@ namespace ledger { format_t::elision_style_t format_t::default_style = TRUNCATE_TRAILING; bool format_t::default_style_changed = false; -format_t::element_t::element_t(const element_t& elem) - : supports_flags<>(), - type(elem.type), - min_width(elem.min_width), - max_width(elem.max_width), - data(elem.data) -{ - TRACE_CTOR(element_t, "copy"); -} - void format_t::element_t::dump(std::ostream& out) const { out << "Element: "; @@ -426,7 +416,6 @@ string format_t::real_calc(scope_t& scope) case element_t::EXPR: { expr_t& expr(boost::get(elem->data)); try { - expr.compile(scope); value_t value; diff --git a/src/format.h b/src/format.h index 0073b427..1b8513bb 100644 --- a/src/format.h +++ b/src/format.h @@ -51,7 +51,7 @@ class unistring; DECLARE_EXCEPTION(format_error, std::runtime_error); -class format_t : public expr_base_t +class format_t : public expr_base_t, public noncopyable { typedef expr_base_t base_type; @@ -65,14 +65,12 @@ class format_t : public expr_base_t std::size_t min_width; std::size_t max_width; variant data; - scoped_ptr next; + shared_ptr next; element_t() throw() : supports_flags<>(), type(STRING), min_width(0), max_width(0) { TRACE_CTOR(element_t, ""); } - element_t(const element_t& elem); - ~element_t() throw() { TRACE_DTOR(element_t); } @@ -84,6 +82,7 @@ class format_t : public expr_base_t min_width = elem.min_width; max_width = elem.max_width; data = elem.data; + next = elem.next; } return *this; } @@ -105,7 +104,7 @@ class format_t : public expr_base_t void dump(std::ostream& out) const; }; - scoped_ptr elements; + shared_ptr elements; public: static enum elision_style_t { -- cgit v1.2.3 From fa3805282a7967ebd4dd26d2dc1d5cc1a3140a74 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 05:53:41 -0600 Subject: Added an assert --- src/op.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index bd2cc32f..c8e099e7 100644 --- a/src/op.cc +++ b/src/op.cc @@ -89,6 +89,8 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) } #endif + assert(kind < LAST); + if (is_ident()) { DEBUG("expr.compile", "Lookup: " << as_ident() << " in " << scope_ptr); if (ptr_op_t def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident())) { -- cgit v1.2.3 From b3e21e8ae240cb2a44a56b174b12d50740264fcc Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 05:53:49 -0600 Subject: Guarded a call to add_or_set_value --- src/post.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/post.cc b/src/post.cc index e0ca149f..191a9142 100644 --- a/src/post.cc +++ b/src/post.cc @@ -606,7 +606,8 @@ bool post_t::valid() const void post_t::add_to_value(value_t& value, const optional& expr) const { if (xdata_ && xdata_->has_flags(POST_EXT_COMPOUND)) { - add_or_set_value(value, xdata_->compound_value); + if (! xdata_->compound_value.is_null()) + add_or_set_value(value, xdata_->compound_value); } else if (expr) { bind_scope_t bound_scope(*expr->get_context(), -- cgit v1.2.3 From dc9ecc600aa766caa3e7d718b0b8656da4ba1f70 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 15:17:59 -0600 Subject: Guard against double-freeing of report objects --- src/report.cc | 91 +++++++++++++++++++++++++++++++++++++---------------------- src/report.h | 2 +- 2 files changed, 59 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 2d825751..7789ff80 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1386,8 +1386,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, switch (*p) { case 'a': if (is_eq(p, "accounts")) { - return WRAP_FUNCTOR(reporter<>(new report_accounts(*this), *this, - "#accounts")); + return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new report_accounts(*this)), + *this, "#accounts")); } break; @@ -1395,9 +1395,10 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) { return expr_t::op_t::wrap_functor (reporter - (new format_accounts(*this, report_format(HANDLER(balance_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t()), + (acct_handler_ptr(new format_accounts + (*this, report_format(HANDLER(balance_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())), *this, "#balance")); } else if (is_eq(p, "budget")) { @@ -1407,11 +1408,24 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (! (budget_flags & ~BUDGET_WRAP_VALUES)) budget_flags |= BUDGET_BUDGETED; +#if 0 +#define POSTS_REPORT(formatter) + return WRAP_FUNCTOR(reporter<>(post_handler_ptr(formatter), *this, + string("#") + p)); + +#define ACCOUNTS_REPORT(formatter) + return WRAP_FUNCTOR(reporter + (acct_handler_ptr(formatter), *this, + string("#") + p)); +#endif + return expr_t::op_t::wrap_functor (reporter - (new format_accounts(*this, report_format(HANDLER(budget_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t()), + (acct_handler_ptr(new format_accounts + (*this, report_format(HANDLER(budget_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())), *this, "#budget")); } break; @@ -1420,9 +1434,10 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (is_eq(p, "csv")) { return WRAP_FUNCTOR (reporter<> - (new format_posts(*this, report_format(HANDLER(csv_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t()), + (post_handler_ptr(new format_posts + (*this, report_format(HANDLER(csv_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())), *this, "#csv")); } else if (is_eq(p, "cleared")) { @@ -1430,31 +1445,35 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, "(amount, cleared ? amount : 0)"); return expr_t::op_t::wrap_functor (reporter - (new format_accounts(*this, report_format(HANDLER(cleared_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t()), + (acct_handler_ptr(new format_accounts + (*this, report_format(HANDLER(cleared_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())), *this, "#cleared")); } else if (is_eq(p, "convert")) { return WRAP_FUNCTOR(convert_command); } else if (is_eq(p, "commodities")) { - return WRAP_FUNCTOR(reporter<>(new report_commodities(*this), *this, - "#commodities")); + return WRAP_FUNCTOR(reporter<> + (post_handler_ptr(new report_commodities(*this)), + *this, "#commodities")); } break; case 'e': if (is_eq(p, "equity")) { HANDLER(generated).on_only(string("#equity")); - return WRAP_FUNCTOR(reporter<>(new print_xacts(*this), *this, "#equity")); + return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new print_xacts(*this)), + *this, "#equity")); } else if (is_eq(p, "entry")) { return WRAP_FUNCTOR(xact_command); } else if (is_eq(p, "emacs")) { return WRAP_FUNCTOR - (reporter<>(new format_emacs_posts(output_stream), *this, "#emacs")); + (reporter<>(post_handler_ptr(new format_emacs_posts(output_stream)), + *this, "#emacs")); } else if (is_eq(p, "echo")) { return MAKE_FUNCTOR(report_t::echo_command); @@ -1465,7 +1484,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (is_eq(p, "org")) { return WRAP_FUNCTOR (reporter<> - (new posts_to_org_table(*this, maybe_format(HANDLER(prepend_format_))), + (post_handler_ptr(new posts_to_org_table + (*this, maybe_format(HANDLER(prepend_format_)))), *this, "#org")); } break; @@ -1473,30 +1493,33 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'p': if (*(p + 1) == '\0' || is_eq(p, "print")) { return WRAP_FUNCTOR - (reporter<>(new print_xacts(*this, HANDLED(raw)), *this, "#print")); + (reporter<>(post_handler_ptr(new print_xacts(*this, HANDLED(raw))), + *this, "#print")); } else if (is_eq(p, "prices")) { return expr_t::op_t::wrap_functor (reporter - (new format_posts(*this, report_format(HANDLER(prices_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t()), + (post_handler_ptr(new format_posts + (*this, report_format(HANDLER(prices_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())), *this, "#prices")); } else if (is_eq(p, "pricedb")) { return expr_t::op_t::wrap_functor (reporter - (new format_posts(*this, report_format(HANDLER(pricedb_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t()), + (post_handler_ptr(new format_posts + (*this, report_format(HANDLER(pricedb_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())), *this, "#pricedb")); } else if (is_eq(p, "pricemap")) { return MAKE_FUNCTOR(report_t::pricemap_command); } else if (is_eq(p, "payees")) { - return WRAP_FUNCTOR(reporter<>(new report_payees(*this), *this, - "#payees")); + return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new report_payees(*this)), + *this, "#payees")); } break; @@ -1504,9 +1527,10 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) { return WRAP_FUNCTOR (reporter<> - (new format_posts(*this, report_format(HANDLER(register_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t()), + (post_handler_ptr(new format_posts + (*this, report_format(HANDLER(register_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())), *this, "#register")); } else if (is_eq(p, "reload")) { @@ -1525,7 +1549,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (is_eq(p, "xact")) return WRAP_FUNCTOR(xact_command); else if (is_eq(p, "xml")) - return WRAP_FUNCTOR(reporter<>(new format_xml(*this), *this, "#xml")); + return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new format_xml(*this)), + *this, "#xml")); break; } break; @@ -1550,7 +1575,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (is_eq(p, "generate")) { return expr_t::op_t::wrap_functor (reporter - (new print_xacts(*this), *this, "#generate")); + (post_handler_ptr(new print_xacts(*this)), *this, "#generate")); } break; case 'p': diff --git a/src/report.h b/src/report.h index 35d45437..03eee78b 100644 --- a/src/report.h +++ b/src/report.h @@ -1019,7 +1019,7 @@ class reporter string whence; public: - reporter(item_handler * _handler, + reporter(shared_ptr > _handler, report_t& _report, const string& _whence) : handler(_handler), report(_report), whence(_whence) {} -- cgit v1.2.3 From e7aaaeab33d77c37efc16d7e97d422407fd497a0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 15:42:23 -0600 Subject: Added boost::noncopyable to format_t and format_t::element_t --- src/format.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/format.h b/src/format.h index 1b8513bb..60ae2abe 100644 --- a/src/format.h +++ b/src/format.h @@ -55,7 +55,7 @@ class format_t : public expr_base_t, public noncopyable { typedef expr_base_t base_type; - struct element_t : public supports_flags<> + struct element_t : public supports_flags<>, public noncopyable { #define ELEMENT_ALIGN_LEFT 0x01 @@ -65,7 +65,7 @@ class format_t : public expr_base_t, public noncopyable std::size_t min_width; std::size_t max_width; variant data; - shared_ptr next; + scoped_ptr next; element_t() throw() : supports_flags<>(), type(STRING), min_width(0), max_width(0) { @@ -82,7 +82,6 @@ class format_t : public expr_base_t, public noncopyable min_width = elem.min_width; max_width = elem.max_width; data = elem.data; - next = elem.next; } return *this; } @@ -104,7 +103,7 @@ class format_t : public expr_base_t, public noncopyable void dump(std::ostream& out) const; }; - shared_ptr elements; + scoped_ptr elements; public: static enum elision_style_t { -- cgit v1.2.3 From 121d878a0c87daf56498431830194dfcefa4538a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 15:42:45 -0600 Subject: When deleting a post from an xact, remove it from its account --- src/xact.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index 0251edde..f05c6069 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -55,6 +55,8 @@ xact_base_t::~xact_base_t() // If the posting is a temporary, it will be destructed when the // temporary is. assert(! post->has_flags(ITEM_TEMP)); + + post->account->remove_post(post); checked_delete(post); } } -- cgit v1.2.3 From 477a9106e3788b33863a7701466860eb5f9682e3 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 15:52:33 -0600 Subject: Prettified some code in report.cc --- src/report.cc | 139 +++++++++++++++++++++++----------------------------------- 1 file changed, 55 insertions(+), 84 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 7789ff80..90f482cc 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1382,24 +1382,37 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return MAKE_OPT_HANDLER(report_t, handler); break; +#define POSTS_REPORT(formatter) \ + WRAP_FUNCTOR(reporter<>(post_handler_ptr(formatter), *this, \ + string("#") + p)); + + // Can't use WRAP_FUNCTOR here because the template arguments + // confuse the parser +#define POSTS_REPORT_(method, formatter) \ + expr_t::op_t::wrap_functor \ + (reporter \ + (post_handler_ptr(formatter), *this, string("#") + p)); + +#define ACCOUNTS_REPORT(formatter) \ + expr_t::op_t::wrap_functor(reporter \ + (acct_handler_ptr(formatter), *this, \ + string("#") + p)); + case symbol_t::COMMAND: switch (*p) { case 'a': if (is_eq(p, "accounts")) { - return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new report_accounts(*this)), - *this, "#accounts")); + return POSTS_REPORT(new report_accounts(*this)); } break; case 'b': if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) { - return expr_t::op_t::wrap_functor - (reporter - (acct_handler_ptr(new format_accounts - (*this, report_format(HANDLER(balance_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())), - *this, "#balance")); + return ACCOUNTS_REPORT(new format_accounts + (*this, report_format(HANDLER(balance_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())); } else if (is_eq(p, "budget")) { HANDLER(amount_).set_expr(string("#budget"), "(amount, 0)"); @@ -1408,72 +1421,46 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (! (budget_flags & ~BUDGET_WRAP_VALUES)) budget_flags |= BUDGET_BUDGETED; -#if 0 -#define POSTS_REPORT(formatter) - return WRAP_FUNCTOR(reporter<>(post_handler_ptr(formatter), *this, - string("#") + p)); - -#define ACCOUNTS_REPORT(formatter) - return WRAP_FUNCTOR(reporter - (acct_handler_ptr(formatter), *this, - string("#") + p)); -#endif - - return expr_t::op_t::wrap_functor - (reporter - (acct_handler_ptr(new format_accounts - (*this, report_format(HANDLER(budget_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())), - *this, "#budget")); + return ACCOUNTS_REPORT(new format_accounts + (*this, report_format(HANDLER(budget_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())); } break; case 'c': if (is_eq(p, "csv")) { - return WRAP_FUNCTOR - (reporter<> - (post_handler_ptr(new format_posts - (*this, report_format(HANDLER(csv_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())), - *this, "#csv")); + return POSTS_REPORT(new format_posts + (*this, report_format(HANDLER(csv_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())); } else if (is_eq(p, "cleared")) { HANDLER(amount_).set_expr(string("#cleared"), "(amount, cleared ? amount : 0)"); - return expr_t::op_t::wrap_functor - (reporter - (acct_handler_ptr(new format_accounts - (*this, report_format(HANDLER(cleared_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())), - *this, "#cleared")); + return ACCOUNTS_REPORT(new format_accounts + (*this, report_format(HANDLER(cleared_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())); } else if (is_eq(p, "convert")) { return WRAP_FUNCTOR(convert_command); } else if (is_eq(p, "commodities")) { - return WRAP_FUNCTOR(reporter<> - (post_handler_ptr(new report_commodities(*this)), - *this, "#commodities")); + return POSTS_REPORT(new report_commodities(*this)); } break; case 'e': if (is_eq(p, "equity")) { HANDLER(generated).on_only(string("#equity")); - return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new print_xacts(*this)), - *this, "#equity")); + return POSTS_REPORT(new print_xacts(*this)); } else if (is_eq(p, "entry")) { return WRAP_FUNCTOR(xact_command); } else if (is_eq(p, "emacs")) { - return WRAP_FUNCTOR - (reporter<>(post_handler_ptr(new format_emacs_posts(output_stream)), - *this, "#emacs")); + return POSTS_REPORT(new format_emacs_posts(output_stream)); } else if (is_eq(p, "echo")) { return MAKE_FUNCTOR(report_t::echo_command); @@ -1482,56 +1469,43 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'o': if (is_eq(p, "org")) { - return WRAP_FUNCTOR - (reporter<> - (post_handler_ptr(new posts_to_org_table - (*this, maybe_format(HANDLER(prepend_format_)))), - *this, "#org")); + return POSTS_REPORT(new posts_to_org_table + (*this, maybe_format(HANDLER(prepend_format_)))); } break; case 'p': if (*(p + 1) == '\0' || is_eq(p, "print")) { - return WRAP_FUNCTOR - (reporter<>(post_handler_ptr(new print_xacts(*this, HANDLED(raw))), - *this, "#print")); + return POSTS_REPORT(new print_xacts(*this, HANDLED(raw))); } else if (is_eq(p, "prices")) { - return expr_t::op_t::wrap_functor - (reporter - (post_handler_ptr(new format_posts + return POSTS_REPORT_(&report_t::commodities_report, + new format_posts (*this, report_format(HANDLER(prices_format_)), maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())), - *this, "#prices")); + HANDLER(prepend_width_).value.to_size_t())); } else if (is_eq(p, "pricedb")) { - return expr_t::op_t::wrap_functor - (reporter - (post_handler_ptr(new format_posts + return POSTS_REPORT_(&report_t::commodities_report, + new format_posts (*this, report_format(HANDLER(pricedb_format_)), maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())), - *this, "#pricedb")); + HANDLER(prepend_width_).value.to_size_t())); } else if (is_eq(p, "pricemap")) { return MAKE_FUNCTOR(report_t::pricemap_command); } else if (is_eq(p, "payees")) { - return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new report_payees(*this)), - *this, "#payees")); + return POSTS_REPORT(new report_payees(*this)); } break; case 'r': if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) { - return WRAP_FUNCTOR - (reporter<> - (post_handler_ptr(new format_posts - (*this, report_format(HANDLER(register_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())), - *this, "#register")); + return POSTS_REPORT(new format_posts + (*this, report_format(HANDLER(register_format_)), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_size_t())); } else if (is_eq(p, "reload")) { return MAKE_FUNCTOR(report_t::reload_command); @@ -1549,8 +1523,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (is_eq(p, "xact")) return WRAP_FUNCTOR(xact_command); else if (is_eq(p, "xml")) - return WRAP_FUNCTOR(reporter<>(post_handler_ptr(new format_xml(*this)), - *this, "#xml")); + return POSTS_REPORT(new format_xml(*this)); break; } break; @@ -1572,11 +1545,9 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(format_command); break; case 'g': - if (is_eq(p, "generate")) { - return expr_t::op_t::wrap_functor - (reporter - (post_handler_ptr(new print_xacts(*this)), *this, "#generate")); - } + if (is_eq(p, "generate")) + return POSTS_REPORT_(&report_t::generate_report, + new print_xacts(*this)); break; case 'p': if (is_eq(p, "parse")) -- cgit v1.2.3 From b6adc8f4605befc2e6a91dfa28cc9237fb391fbc Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 17:46:42 -0600 Subject: Use unique_ptr instead of std::auto_ptr --- src/amount.cc | 4 ++-- src/csv.cc | 4 ++-- src/draft.cc | 4 ++-- src/format.cc | 2 +- src/main.cc | 2 +- src/report.cc | 2 +- src/session.h | 4 ++-- src/textual.cc | 10 +++++----- src/timelog.cc | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 9704dd21..3ddb7672 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -1027,12 +1027,12 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) } // Allocate memory for the amount's quantity value. We have to - // monitor the allocation in an auto_ptr because this function gets + // monitor the allocation in a unique_ptr because this function gets // called sometimes from amount_t's constructor; and if there is an // exeception thrown by any of the function calls after this point, // the destructor will never be called and the memory never freed. - std::auto_ptr new_quantity; + unique_ptr new_quantity; if (quantity) { if (quantity->refc > 1) diff --git a/src/csv.cc b/src/csv.cc index 305db992..1e55129e 100644 --- a/src/csv.cc +++ b/src/csv.cc @@ -139,8 +139,8 @@ xact_t * csv_reader::read_xact(bool rich_data) std::istringstream instr(line); - std::auto_ptr xact(new xact_t); - std::auto_ptr post(new post_t); + unique_ptr xact(new xact_t); + unique_ptr post(new post_t); xact->set_state(item_t::CLEARED); diff --git a/src/draft.cc b/src/draft.cc index 7c95caf7..017c637d 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -233,7 +233,7 @@ xact_t * draft_t::insert(journal_t& journal) throw std::runtime_error(_("'xact' command requires at least a payee")); xact_t * matching = NULL; - std::auto_ptr added(new xact_t); + unique_ptr added(new xact_t); if (xact_t * xact = lookup_probable_account(tmpl->payee_mask.str(), journal.xacts.rbegin(), @@ -316,7 +316,7 @@ xact_t * draft_t::insert(journal_t& journal) } foreach (xact_template_t::post_template_t& post, tmpl->posts) { - std::auto_ptr new_post; + unique_ptr new_post; commodity_t * found_commodity = NULL; diff --git a/src/format.cc b/src/format.cc index 9824d5f7..d8c03e6a 100644 --- a/src/format.cc +++ b/src/format.cc @@ -125,7 +125,7 @@ namespace { format_t::element_t * format_t::parse_elements(const string& fmt, const optional& tmpl) { - std::auto_ptr result; + unique_ptr result; element_t * current = NULL; diff --git a/src/main.cc b/src/main.cc index aafbdbcb..dd03206f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -80,7 +80,7 @@ int main(int argc, char * argv[], char * envp[]) ::textdomain("ledger"); #endif - std::auto_ptr global_scope; + unique_ptr global_scope; try { // Create the session object, which maintains nearly all state relating to diff --git a/src/report.cc b/src/report.cc index 90f482cc..b86f77eb 100644 --- a/src/report.cc +++ b/src/report.cc @@ -309,7 +309,7 @@ void report_t::posts_report(post_handler_ptr handler) { handler = chain_post_handlers(handler, *this); if (HANDLED(group_by_)) { - std::auto_ptr + unique_ptr splitter(new post_splitter(handler, *this, HANDLER(group_by_).expr)); splitter->set_postflush_func(posts_flusher(handler, *this)); handler = post_handler_ptr(splitter.release()); diff --git a/src/session.h b/src/session.h index 879efeb6..cfeced39 100644 --- a/src/session.h +++ b/src/session.h @@ -59,8 +59,8 @@ class session_t : public symbol_scope_t public: bool flush_on_next_data_file; - std::auto_ptr journal; - parse_context_stack_t parsing_context; + unique_ptr journal; + parse_context_stack_t parsing_context; explicit session_t(); virtual ~session_t() { diff --git a/src/textual.cc b/src/textual.cc index 95635184..2493dc0d 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -527,7 +527,7 @@ void instance_t::automated_xact_directive(char * line) query.parse_args(string_value(skip_ws(line + 1)).to_sequence(), keeper, false, true); - std::auto_ptr ae(new auto_xact_t(predicate_t(expr, keeper))); + unique_ptr ae(new auto_xact_t(predicate_t(expr, keeper))); ae->pos = position_t(); ae->pos->pathname = context.pathname; ae->pos->beg_pos = context.line_beg_pos; @@ -621,7 +621,7 @@ void instance_t::period_xact_directive(char * line) try { - std::auto_ptr pe(new period_xact_t(skip_ws(line + 1))); + unique_ptr pe(new period_xact_t(skip_ws(line + 1))); pe->pos = position_t(); pe->pos->pathname = context.pathname; pe->pos->beg_pos = context.line_beg_pos; @@ -665,7 +665,7 @@ void instance_t::xact_directive(char * line, std::streamsize len) TRACE_START(xacts, 1, "Time spent handling transactions:"); if (xact_t * xact = parse_xact(line, len, top_account())) { - std::auto_ptr manager(xact); + unique_ptr manager(xact); if (context.journal->add_xact(xact)) { manager.release(); // it's owned by the journal now @@ -874,7 +874,7 @@ void instance_t::account_directive(char * line) char * p = skip_ws(line); account_t * account = context.journal->register_account(p, NULL, top_account()); - std::auto_ptr ae; + unique_ptr ae; while (peek_whitespace_line()) { read_line(line); @@ -1287,7 +1287,7 @@ post_t * instance_t::parse_post(char * line, { TRACE_START(post_details, 1, "Time spent parsing postings:"); - std::auto_ptr post(new post_t); + unique_ptr post(new post_t); post->xact = xact; // this could be NULL post->pos = position_t(); diff --git a/src/timelog.cc b/src/timelog.cc index 67ea1015..b46d3922 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -88,7 +88,7 @@ namespace { if (! out_event.note.empty() && event.note.empty()) event.note = out_event.note; - std::auto_ptr curr(new xact_t); + unique_ptr curr(new xact_t); curr->_date = event.checkin.date(); curr->code = out_event.desc; // if it wasn't used above curr->payee = event.desc; -- cgit v1.2.3 From f9de33d21cd62d953147d62e01958557562a1311 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 17:46:55 -0600 Subject: Removed an invalid assertion --- src/account.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index 40ddf70b..e1874839 100644 --- a/src/account.cc +++ b/src/account.cc @@ -137,7 +137,10 @@ void account_t::add_post(post_t * post) bool account_t::remove_post(post_t * post) { - assert(! posts.empty()); + // It's possible that 'post' wasn't yet in this account, but try to + // remove it anyway. This can happen if there is an error during + // parsing, when the posting knows what it's account is, but + // xact_t::finalize has not yet added that posting to the account. posts.remove(post); post->account = NULL; return true; -- cgit v1.2.3 From e5885cc8a8a1d6cd2a65d04c56ba86d65317f4a1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 17:47:12 -0600 Subject: Added annotation_t::operator<() --- src/annotate.cc | 28 ++++++++++++++++++++++++++++ src/annotate.h | 1 + 2 files changed, 29 insertions(+) (limited to 'src') diff --git a/src/annotate.cc b/src/annotate.cc index cd1733ca..1e243beb 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -38,6 +38,34 @@ namespace ledger { +bool annotation_t::operator<(const annotation_t& rhs) const +{ + if (! price && rhs.price) return true; + if (price && ! rhs.price) return false; + if (! date && rhs.date) return true; + if (date && ! rhs.date) return false; + if (! tag && rhs.tag) return true; + if (tag && ! rhs.tag) return false; + + if (price) { + if (price->commodity().symbol() < rhs.price->commodity().symbol()) + return true; + if (price->commodity().symbol() > rhs.price->commodity().symbol()) + return false; + if (*price < *rhs.price) return true; + if (*price > *rhs.price) return false; + } + if (date) { + if (*date < *rhs.date) return true; + if (*date > *rhs.date) return false; + } + if (tag) { + if (*tag < *rhs.tag) return true; + if (*tag > *rhs.tag) return false; + } + return false; +} + void annotation_t::parse(std::istream& in) { do { diff --git a/src/annotate.h b/src/annotate.h index 3c6db8e8..29294e88 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -79,6 +79,7 @@ struct annotation_t : public supports_flags<>, return price || date || tag; } + bool operator<(const annotation_t& rhs) const; bool operator==(const annotation_t& rhs) const { return (price == rhs.price && date == rhs.date && -- cgit v1.2.3 From 46b124a649d652ef88110478db53cc2b7d69069b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 17:47:44 -0600 Subject: Added some DEBUG statements --- src/xact.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index f05c6069..7dbf097d 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -274,6 +274,8 @@ bool xact_base_t::finalize() if (post->amount.has_annotation() && post->amount.annotation().price) { if (breakdown.basis_cost.commodity() == breakdown.final_cost.commodity()) { + DEBUG("xact.finalize", "breakdown.basis_cost = " << breakdown.basis_cost); + DEBUG("xact.finalize", "breakdown.final_cost = " << breakdown.final_cost); if (amount_t gain_loss = breakdown.basis_cost - breakdown.final_cost) { DEBUG("xact.finalize", "gain_loss = " << gain_loss); gain_loss.in_place_round(); -- cgit v1.2.3 From ff9cf572fe5054f2e5c5d7ab4e68d63011e4a38a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 17:49:11 -0600 Subject: Added (disabled) code to stop sorting generated balancing postings --- src/xact.cc | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index 7dbf097d..5cec9ab0 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -331,12 +331,19 @@ bool xact_base_t::finalize() if (balance.is_balance()) { const balance_t& bal(balance.as_balance()); - typedef std::map sorted_amounts_map; +#if 1 + typedef std::map, + amount_t> sorted_amounts_map; sorted_amounts_map samp; foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { std::pair result = - samp.insert(sorted_amounts_map::value_type(pair.first->mapping_key(), - pair.second)); + samp.insert(sorted_amounts_map::value_type + (sorted_amounts_map::key_type + (pair.first->symbol(), + pair.first->has_annotation() ? + as_annotated_commodity(*pair.first).details : + annotation_t()), + pair.second)); assert(result.second); } @@ -353,6 +360,21 @@ bool xact_base_t::finalize() add_post(p); } } +#else + bool first = true; + foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { + if (first) { + null_post->amount = pair.second.negated(); + null_post->add_flags(POST_CALCULATED); + first = false; + } else { + post_t * p = new post_t(null_post->account, pair.second.negated(), + ITEM_GENERATED | POST_CALCULATED); + p->set_state(null_post->state()); + add_post(p); + } + } +#endif } else if (balance.is_amount()) { null_post->amount = balance.as_amount().negated(); -- cgit v1.2.3 From 97dbf379d5e44c80c748483a688502b1cec8f075 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 17:50:17 -0600 Subject: Rewrote the way annotated commodities are managed The new scheme should be much more efficient, and allows for future growth of what annotations may appear on a commodity. --- src/commodity.h | 9 --- src/pool.cc | 161 ++++++++++++++++++++++++++-------------------------- src/pool.h | 49 ++++++++-------- src/py_commodity.cc | 11 ++-- src/textual.cc | 11 +--- 5 files changed, 110 insertions(+), 131 deletions(-) (limited to 'src') diff --git a/src/commodity.h b/src/commodity.h index 524daaab..7934f1ad 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -164,7 +164,6 @@ protected: commodity_pool_t * parent_; optional qualified_symbol; - optional mapping_key_; bool annotated; explicit commodity_t(commodity_pool_t * _parent, @@ -218,13 +217,6 @@ public: return qualified_symbol ? *qualified_symbol : base_symbol(); } - string mapping_key() const { - if (mapping_key_) - return *mapping_key_; - else - return base_symbol(); - } - optional graph_index() const {; return base->graph_index; } @@ -325,7 +317,6 @@ private: ar & base; ar & parent_; ar & qualified_symbol; - ar & mapping_key_; ar & annotated; } #endif // HAVE_BOOST_SERIALIZATION diff --git a/src/pool.cc b/src/pool.cc index 2c094d47..ca50db2c 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -56,7 +56,7 @@ commodity_t * commodity_pool_t::create(const string& symbol) { shared_ptr base_commodity(new commodity_t::base_t(symbol)); - std::auto_ptr commodity(new commodity_t(this, base_commodity)); + shared_ptr commodity(new commodity_t(this, base_commodity)); DEBUG("pool.commodities", "Creating base commodity " << symbol); @@ -71,13 +71,23 @@ commodity_t * commodity_pool_t::create(const string& symbol) "Creating commodity '" << commodity->symbol() << "'"); std::pair result - = commodities.insert(commodities_map::value_type(commodity->mapping_key(), - commodity.get())); + = commodities.insert(commodities_map::value_type + (commodity->base_symbol(), commodity)); assert(result.second); commodity_price_history.add_commodity(*commodity.get()); - return commodity.release(); + return commodity.get(); +} + +commodity_t * commodity_pool_t::find(const string& symbol) +{ + DEBUG("pool.commodities", "Find commodity " << symbol); + + commodities_map::const_iterator i = commodities.find(symbol); + if (i != commodities.end()) + return (*i).second.get(); + return NULL; } commodity_t * commodity_pool_t::find_or_create(const string& symbol) @@ -88,97 +98,103 @@ commodity_t * commodity_pool_t::find_or_create(const string& symbol) return create(symbol); } -commodity_t * commodity_pool_t::find(const string& symbol) +commodity_t * commodity_pool_t::alias(const string& name, commodity_t& referent) { - DEBUG("pool.commodities", "Find commodity " << symbol); + commodities_map::const_iterator i = commodities.find(referent.symbol()); + assert(i != commodities.end()); - commodities_map::const_iterator i = commodities.find(symbol); - if (i != commodities.end()) - return (*i).second; - return NULL; + std::pair result + = commodities.insert(commodities_map::value_type(name, (*i).second)); + assert(result.second); + + return (*result.first).second.get(); } commodity_t * commodity_pool_t::create(const string& symbol, const annotation_t& details) { - commodity_t * new_comm = create(symbol); - if (! new_comm) - return NULL; + DEBUG("pool.commodities", "commodity_pool_t::create[ann] " + << "symbol " << symbol << std::endl << details); if (details) - return find_or_create(*new_comm, details); + return create(*find_or_create(symbol), details); else - return new_comm; + return create(symbol); } -string commodity_pool_t::make_qualified_name(const commodity_t& comm, - const annotation_t& details) +commodity_t * +commodity_pool_t::find(const string& symbol, const annotation_t& details) { - assert(details); - - if (details.price && details.price->sign() < 0) - throw_(amount_error, _("A commodity's price may not be negative")); + DEBUG("pool.commodities", "commodity_pool_t::find[ann] " + << "symbol " << symbol << std::endl << details); - std::ostringstream name; - comm.print(name); - details.print(name, comm.pool().keep_base); - -#if defined(DEBUG_ON) - if (comm.qualified_symbol) - DEBUG("pool.commodities", "make_qualified_name for " - << *comm.qualified_symbol << std::endl << details); -#endif - DEBUG("pool.commodities", "qualified_name is " << name.str()); - - return name.str(); + if (details) { + annotated_commodities_map::const_iterator i = + annotated_commodities.find + (annotated_commodities_map::key_type(symbol, details)); + if (i != annotated_commodities.end()) { + DEBUG("pool.commodities", "commodity_pool_t::find[ann] found " + << "symbol " << (*i).second->symbol() << std::endl + << as_annotated_commodity(*(*i).second.get()).details); + return (*i).second.get(); + } else { + return NULL; + } + } else { + return find(symbol); + } } commodity_t * -commodity_pool_t::find(const string& symbol, const annotation_t& details) +commodity_pool_t::find_or_create(const string& symbol, + const annotation_t& details) { - commodity_t * comm = find(symbol); - if (! comm) - return NULL; + DEBUG("pool.commodities", "commodity_pool_t::find_or_create[ann] " + << "symbol " << symbol << std::endl << details); if (details) { - string name = make_qualified_name(*comm, details); - - if (commodity_t * ann_comm = find(name)) { + if (commodity_t * ann_comm = find(symbol, details)) { assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); return ann_comm; + } else { + return create(symbol, details); } - return NULL; } else { - return comm; + return find_or_create(symbol); } } commodity_t * -commodity_pool_t::find_or_create(const string& symbol, - const annotation_t& details) +commodity_pool_t::find_or_create(commodity_t& comm, const annotation_t& details) { - commodity_t * comm = find_or_create(symbol); - if (! comm) - return NULL; + DEBUG("pool.commodities", "commodity_pool_t::find_or_create[ann:comm] " + << "symbol " << comm.symbol() << std::endl << details); - if (details) - return find_or_create(*comm, details); - else - return comm; + if (details) { + if (commodity_t * ann_comm = find(comm.symbol(), details)) { + assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); + return ann_comm; + } else { + return create(comm, details); + } + } else { + return &comm; + } } -commodity_t * +annotated_commodity_t * commodity_pool_t::create(commodity_t& comm, - const annotation_t& details, - const string& mapping_key) + const annotation_t& details) { + DEBUG("pool.commodities", "commodity_pool_t::create[ann:comm] " + << "symbol " << comm.symbol() << std::endl << details); + assert(comm); assert(! comm.has_annotation()); assert(details); - assert(! mapping_key.empty()); - unique_ptr commodity - (new annotated_commodity_t(&comm, details)); + shared_ptr + commodity(new annotated_commodity_t(&comm, details)); comm.add_flags(COMMODITY_SAW_ANNOTATED); if (details.price) { @@ -193,34 +209,15 @@ commodity_pool_t::create(commodity_t& comm, DEBUG("pool.commodities", "Creating annotated commodity " << "symbol " << commodity->symbol() - << " key " << mapping_key << std::endl << details); - - // Add the fully annotated name to the map, so that this symbol may - // quickly be found again. - commodity->mapping_key_ = mapping_key; + << std::endl << details); - std::pair result - = commodities.insert(commodities_map::value_type(mapping_key, - commodity.get())); + std::pair result + = annotated_commodities.insert(annotated_commodities_map::value_type + (annotated_commodities_map::key_type + (comm.symbol(), details), commodity)); assert(result.second); - return commodity.release(); -} - -commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, - const annotation_t& details) -{ - assert(comm); - assert(details); - - string name = make_qualified_name(comm, details); - assert(! name.empty()); - - if (commodity_t * ann_comm = find(name)) { - assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); - return ann_comm; - } - return create(comm, details, name); + return commodity.get(); } void commodity_pool_t::exchange(commodity_t& commodity, diff --git a/src/pool.h b/src/pool.h index 709f5c71..7203bc20 100644 --- a/src/pool.h +++ b/src/pool.h @@ -47,6 +47,7 @@ #define _POOL_H #include "history.h" +#include "annotate.h" namespace ledger { @@ -66,51 +67,48 @@ public: * explicitly by calling the create methods of commodity_pool_t, or * implicitly by parsing a commoditized amount. */ - typedef std::map commodities_map; + typedef std::map > commodities_map; + typedef std::map, + shared_ptr > annotated_commodities_map; - commodities_map commodities; - commodity_history_t commodity_price_history; - commodity_t * null_commodity; - commodity_t * default_commodity; + commodities_map commodities; + annotated_commodities_map annotated_commodities; + commodity_history_t commodity_price_history; + commodity_t * null_commodity; + commodity_t * default_commodity; - bool keep_base; // --base - - optional price_db; // --price-db= - long quote_leeway; // --leeway= - bool get_quotes; // --download - - static shared_ptr current_pool; + bool keep_base; // --base + optional price_db; // --price-db= + long quote_leeway; // --leeway= + bool get_quotes; // --download function (commodity_t& commodity, const optional& in_terms_of)> get_commodity_quote; + static shared_ptr current_pool; + explicit commodity_pool_t(); virtual ~commodity_pool_t() { TRACE_DTOR(commodity_pool_t); - foreach (commodities_map::value_type& pair, commodities) - checked_delete(pair.second); } - string make_qualified_name(const commodity_t& comm, - const annotation_t& details); - commodity_t * create(const string& symbol); commodity_t * find(const string& name); commodity_t * find_or_create(const string& symbol); + commodity_t * alias(const string& name, commodity_t& referent); - commodity_t * create(const string& symbol, const annotation_t& details); - commodity_t * find(const string& symbol, const annotation_t& details); + commodity_t * create(const string& symbol, + const annotation_t& details); + commodity_t * find(const string& symbol, + const annotation_t& details); commodity_t * find_or_create(const string& symbol, const annotation_t& details); + commodity_t * find_or_create(commodity_t& comm, const annotation_t& details); - commodity_t * create(commodity_t& comm, - const annotation_t& details, - const string& mapping_key); - - commodity_t * find_or_create(commodity_t& comm, - const annotation_t& details); + annotated_commodity_t * create(commodity_t& comm, + const annotation_t& details); // Exchange one commodity for another, while recording the factored price. @@ -144,6 +142,7 @@ private: void serialize(Archive& ar, const unsigned int /* version */) { ar & current_pool; ar & commodities; + ar & annotated_commodities; ar & null_commodity; ar & default_commodity; ar & keep_base; diff --git a/src/py_commodity.cc b/src/py_commodity.cc index b5230850..25e5b918 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -115,7 +115,7 @@ namespace { (string("Could not find commodity ") + symbol).c_str()); throw_error_already_set(); } - return (*i).second; + return (*i).second.get(); } python::list py_pool_keys(commodity_pool_t& pool) { @@ -168,13 +168,15 @@ namespace { py_pool_commodities_values_begin(commodity_pool_t& pool) { return make_transform_iterator (pool.commodities.begin(), - bind(&commodity_pool_t::commodities_map::value_type::second, _1)); + bind(&shared_ptr::get, + bind(&commodity_pool_t::commodities_map::value_type::second, _1))); } commodities_map_seconds_iterator py_pool_commodities_values_end(commodity_pool_t& pool) { return make_transform_iterator (pool.commodities.end(), - bind(&commodity_pool_t::commodities_map::value_type::second, _1)); + bind(&shared_ptr::get, + bind(&commodity_pool_t::commodities_map::value_type::second, _1))); } void py_add_price_2(commodity_t& commodity, @@ -267,8 +269,6 @@ void export_commodity() make_getter(&commodity_pool_t::get_commodity_quote), make_setter(&commodity_pool_t::get_commodity_quote)) - .def("make_qualified_name", &commodity_pool_t::make_qualified_name) - .def("create", py_create_1, return_internal_reference<>()) .def("create", py_create_2, return_internal_reference<>()) @@ -359,7 +359,6 @@ void export_commodity() .add_property("base_symbol", &commodity_t::base_symbol) .add_property("symbol", &commodity_t::symbol) - .add_property("mapping_key", &commodity_t::mapping_key) .add_property("name", &commodity_t::name, &commodity_t::set_name) .add_property("note", &commodity_t::note, &commodity_t::set_note) diff --git a/src/textual.cc b/src/textual.cc index 2493dc0d..7bf67347 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1031,17 +1031,10 @@ void instance_t::commodity_directive(char * line) } } -void instance_t::commodity_alias_directive(commodity_t&, string) +void instance_t::commodity_alias_directive(commodity_t& comm, string alias) { -#if 0 trim(alias); - std::pair result - = commodity_pool_t::current_pool->commodities.insert - (commodity_pool_t::commodities_map::value_type(alias, &comm)); - if (! result.second) - throw_(parse_error, - _("Cannot use existing commodity name as an alias: %1") << alias); -#endif + commodity_pool_t::current_pool->alias(alias, comm); } void instance_t::commodity_format_directive(commodity_t&, string format) -- cgit v1.2.3 From 59a16e59ee2e684f1d5292fe78ef94464a935d73 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 21:48:45 -0600 Subject: Simplified Python import in a journal file --- src/textual.cc | 35 ++++++++++++++++++++++++++++------- test/baseline/dir-import_py.test | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 test/baseline/dir-import_py.test (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 7bf67347..b1df1fb8 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -164,9 +164,8 @@ namespace { void assert_directive(char * line); void check_directive(char * line); -#if defined(HAVE_BOOST_PYTHON) + void import_directive(char * line); void python_directive(char * line); -#endif post_t * parse_post(char * line, std::streamsize len, @@ -1114,6 +1113,14 @@ void instance_t::comment_directive(char * line) } #if defined(HAVE_BOOST_PYTHON) + +void instance_t::import_directive(char * line) +{ + string module_name(line); + trim(module_name); + python_session->import_option(module_name); +} + void instance_t::python_directive(char * line) { std::ostringstream script; @@ -1153,6 +1160,21 @@ void instance_t::python_directive(char * line) ("journal", python::object(python::ptr(context.journal))); python_session->eval(script.str(), python_interpreter_t::PY_EVAL_MULTI); } + +#else + +void instance_t::import_directive(char *) +{ + throw_(parse_error, + _("'python' directive seen, but Python support is missing")); +} + +void instance_t::python_directive(char *) +{ + throw_(parse_error, + _("'import' directive seen, but Python support is missing")); +} + #endif // HAVE_BOOST_PYTHON bool instance_t::general_directive(char * line) @@ -1232,6 +1254,10 @@ bool instance_t::general_directive(char * line) include_directive(arg); return true; } + else if (std::strcmp(p, "import") == 0) { + import_directive(arg); + return true; + } break; case 'p': @@ -1240,12 +1266,7 @@ bool instance_t::general_directive(char * line) return true; } else if (std::strcmp(p, "python") == 0) { -#if defined(HAVE_BOOST_PYTHON) python_directive(arg); -#else - throw_(parse_error, - _("'python' directive seen, but Python support is missing")); -#endif return true; } break; diff --git a/test/baseline/dir-import_py.test b/test/baseline/dir-import_py.test new file mode 100644 index 00000000..ee9f6001 --- /dev/null +++ b/test/baseline/dir-import_py.test @@ -0,0 +1,23 @@ +import os + +tag PATH + check os.path.isfile(value) + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_py.test + Expenses:Food $20 + Assets:Cash + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_noexist.test + Expenses:Food $20 + Assets:Cash + +test reg +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +__ERROR__ +Warning: "$sourcepath/test/baseline/dir-import_py.test", line 14: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): ((os.path).isfile(value)) +end test -- cgit v1.2.3 From acb5e1beea4176ab51ca2c9d32b006e7c0a7bef0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 22:01:41 -0600 Subject: Generalized function call parsing Directly calling a lambda now works: (x -> x + 10)(10) => 20 --- src/op.cc | 19 ++++++++++++------- src/parser.cc | 47 +++++++++++++++++++++++++++++++---------------- src/parser.h | 2 ++ 3 files changed, 45 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index c8e099e7..56d3710e 100644 --- a/src/op.cc +++ b/src/op.cc @@ -262,13 +262,18 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) case O_CALL: { ptr_op_t func = left(); - const string& name(func->as_ident()); - - func = func->left(); - if (! func) - func = scope.lookup(symbol_t::FUNCTION, name); - if (! func) - throw_(calc_error, _("Calling unknown function '%1'") << name); + string name; + + if (func->is_ident()) { + name = func->as_ident(); + func = func->left(); + if (! func) + func = scope.lookup(symbol_t::FUNCTION, name); + if (! func) + throw_(calc_error, _("Calling unknown function '%1'") << name); + } else { + name = ""; + } call_scope_t call_args(scope, locus, depth + 1); if (has_right()) diff --git a/src/parser.cc b/src/parser.cc index 2c9069d7..b3f50e41 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -54,20 +54,6 @@ expr_t::parser_t::parse_value_term(std::istream& in, node = new op_t(op_t::IDENT); node->set_ident(ident); - - // An identifier followed by ( represents a function call - tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); - if (tok.kind == token_t::LPAREN) { - op_t::kind_t kind = op_t::O_CALL; - ptr_op_t call_node(new op_t(kind)); - call_node->set_left(node); - node = call_node; - - push_token(tok); // let the parser see it again - node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE))); - } else { - push_token(tok); - } break; } @@ -85,12 +71,41 @@ expr_t::parser_t::parse_value_term(std::istream& in, return node; } + expr_t::ptr_op_t -expr_t::parser_t::parse_dot_expr(std::istream& in, +expr_t::parser_t::parse_call_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_value_term(in, tflags)); + if (node && ! tflags.has_flags(PARSE_SINGLE)) { + while (true) { + token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); + if (tok.kind == token_t::LPAREN) { + ptr_op_t prev(node); + node = new op_t(op_t::O_CALL); + node->set_left(prev); + push_token(tok); // let the parser see the '(' again + node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE))); + if (! node->right()) + throw_(parse_error, + _("%1 operator not followed by argument") << tok.symbol); + } else { + push_token(tok); + break; + } + } + } + + return node; +} + +expr_t::ptr_op_t +expr_t::parser_t::parse_dot_expr(std::istream& in, + const parse_flags_t& tflags) const +{ + ptr_op_t node(parse_call_expr(in, tflags)); + if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); @@ -98,7 +113,7 @@ expr_t::parser_t::parse_dot_expr(std::istream& in, ptr_op_t prev(node); node = new op_t(op_t::O_LOOKUP); node->set_left(prev); - node->set_right(parse_value_term(in, tflags)); + node->set_right(parse_call_expr(in, tflags)); if (! node->right()) throw_(parse_error, _("%1 operator not followed by argument") << tok.symbol); diff --git a/src/parser.h b/src/parser.h index 75fd9a41..db16a919 100644 --- a/src/parser.h +++ b/src/parser.h @@ -81,6 +81,8 @@ class expr_t::parser_t : public noncopyable ptr_op_t parse_value_term(std::istream& in, const parse_flags_t& flags) const; + ptr_op_t parse_call_expr(std::istream& in, + const parse_flags_t& flags) const; ptr_op_t parse_dot_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_unary_expr(std::istream& in, -- cgit v1.2.3 From 7b12b3041dccfb082949a5f1fdbd83396aae4f26 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 22:47:06 -0600 Subject: Make exprbase_t::text() a const member function --- src/exprbase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/exprbase.h b/src/exprbase.h index 0b1ef243..56900dbb 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -113,7 +113,7 @@ public: return ! str.empty(); } - virtual string text() { + virtual string text() const throw() { return str; } void set_text(const string& txt) { -- cgit v1.2.3 From 8ae2fb87211b2c1a0159480ea6908db2afa20189 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 23:01:41 -0600 Subject: Add support for valuation expressions on commodities --- src/annotate.cc | 62 +++++++++++++++++++++++++++++++++++++++++++---------- src/annotate.h | 46 ++++++++++++++++++++++++++------------- src/expr.h | 3 +++ src/predicate.cc | 40 ---------------------------------- test/unit/t_expr.cc | 2 +- tools/Makefile.am | 41 +++++++++++------------------------ 6 files changed, 98 insertions(+), 96 deletions(-) delete mode 100644 src/predicate.cc (limited to 'src') diff --git a/src/annotate.cc b/src/annotate.cc index 1e243beb..b1db6bd5 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -33,6 +33,7 @@ #include "amount.h" #include "commodity.h" +#include "expr.h" #include "annotate.h" #include "pool.h" @@ -47,6 +48,9 @@ bool annotation_t::operator<(const annotation_t& rhs) const if (! tag && rhs.tag) return true; if (tag && ! rhs.tag) return false; + if (! value_expr && rhs.value_expr) return true; + if (value_expr && ! rhs.value_expr) return false; + if (price) { if (price->commodity().symbol() < rhs.price->commodity().symbol()) return true; @@ -63,6 +67,10 @@ bool annotation_t::operator<(const annotation_t& rhs) const if (*tag < *rhs.tag) return true; if (*tag > *rhs.tag) return false; } + if (value_expr) { + if (value_expr->text() < rhs.value_expr->text()) return true; + if (value_expr->text() > rhs.value_expr->text()) return false; + } return false; } @@ -112,17 +120,41 @@ void annotation_t::parse(std::istream& in) date = parse_date(buf); } else if (c == '(') { - if (tag) - throw_(amount_error, _("Commodity specifies more than one tag")); - in.get(c); - READ_INTO(in, buf, 255, c, c != ')'); - if (c == ')') - in.get(c); - else - throw_(amount_error, _("Commodity tag lacks closing parenthesis")); + c = static_cast(in.peek()); + if (c == '(') { + if (value_expr) + throw_(amount_error, + _("Commodity specifies more than one valuation expresion")); - tag = buf; + in.get(c); + READ_INTO(in, buf, 255, c, c != ')'); + if (c == ')') { + in.get(c); + c = static_cast(in.peek()); + if (c == ')') + in.get(c); + else + throw_(amount_error, + _("Commodity valuation expression lacks closing parentheses")); + } else { + throw_(amount_error, + _("Commodity valuation expression lacks closing parentheses")); + } + + value_expr = expr_t(buf); + } else { + if (tag) + throw_(amount_error, _("Commodity specifies more than one tag")); + + READ_INTO(in, buf, 255, c, c != ')'); + if (c == ')') + in.get(c); + else + throw_(amount_error, _("Commodity tag lacks closing parenthesis")); + + tag = buf; + } } else { in.clear(); @@ -156,6 +188,10 @@ void annotation_t::print(std::ostream& out, bool keep_base, if (tag && (! no_computed_annotations || ! has_flags(ANNOTATION_TAG_CALCULATED))) out << " (" << *tag << ')'; + + if (value_expr && (! no_computed_annotations || + ! has_flags(ANNOTATION_VALUE_EXPR_CALCULATED))) + out << " ((" << *value_expr << "))"; } bool keep_details_t::keep_all(const commodity_t& comm) const @@ -220,12 +256,14 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) if ((keep_price && details.price) || (keep_date && details.date) || - (keep_tag && details.tag)) + (keep_tag && details.tag) || + details.value_expr) { new_comm = pool().find_or_create (referent(), annotation_t(keep_price ? details.price : none, keep_date ? details.date : none, - keep_tag ? details.tag : none)); + keep_tag ? details.tag : none, + details.value_expr)); // Transfer over any relevant annotation flags, as they still apply. if (new_comm->annotated) { @@ -238,6 +276,8 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) new_details.add_flags(details.flags() & ANNOTATION_DATE_CALCULATED); if (keep_tag) new_details.add_flags(details.flags() & ANNOTATION_TAG_CALCULATED); + if (details.value_expr) + new_details.add_flags(details.flags() & ANNOTATION_VALUE_EXPR_CALCULATED); } return *new_comm; diff --git a/src/annotate.h b/src/annotate.h index 29294e88..eb87a1b2 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -46,29 +46,38 @@ #ifndef _ANNOTATE_H #define _ANNOTATE_H +#include "expr.h" + namespace ledger { struct annotation_t : public supports_flags<>, public equality_comparable { -#define ANNOTATION_PRICE_CALCULATED 0x01 -#define ANNOTATION_PRICE_FIXATED 0x02 -#define ANNOTATION_DATE_CALCULATED 0x04 -#define ANNOTATION_TAG_CALCULATED 0x08 +#define ANNOTATION_PRICE_CALCULATED 0x01 +#define ANNOTATION_PRICE_FIXATED 0x02 +#define ANNOTATION_DATE_CALCULATED 0x04 +#define ANNOTATION_TAG_CALCULATED 0x08 +#define ANNOTATION_VALUE_EXPR_CALCULATED 0x10 optional price; optional date; optional tag; - - explicit annotation_t(const optional& _price = none, - const optional& _date = none, - const optional& _tag = none) - : supports_flags<>(), price(_price), date(_date), tag(_tag) { - TRACE_CTOR(annotation_t, "const optional& + date_t + string"); + optional value_expr; + + explicit annotation_t(const optional& _price = none, + const optional& _date = none, + const optional& _tag = none, + const optional& _value_expr = none) + : supports_flags<>(), price(_price), date(_date), tag(_tag), + value_expr(_value_expr) { + TRACE_CTOR(annotation_t, + "const optional& + date_t + string + expr_t"); } annotation_t(const annotation_t& other) : supports_flags<>(other.flags()), - price(other.price), date(other.date), tag(other.tag) { + price(other.price), date(other.date), tag(other.tag), + value_expr(other.value_expr) + { TRACE_CTOR(annotation_t, "copy"); } ~annotation_t() { @@ -76,14 +85,15 @@ struct annotation_t : public supports_flags<>, } operator bool() const { - return price || date || tag; + return price || date || tag || value_expr; } bool operator<(const annotation_t& rhs) const; bool operator==(const annotation_t& rhs) const { - return (price == rhs.price && - date == rhs.date && - tag == rhs.tag); + return (price == rhs.price && + date == rhs.date && + tag == rhs.tag && + value_expr == rhs.value_expr); } void parse(std::istream& in); @@ -133,6 +143,12 @@ inline void to_xml(std::ostream& out, const annotation_t& details) push_xml y(out, "tag"); out << y.guard(*details.tag); } + + if (details.value_expr) + { + push_xml y(out, "value-expr"); + out << y.guard(details.value_expr->text()); + } } struct keep_details_t diff --git a/src/expr.h b/src/expr.h index e082efa5..ab3487fe 100644 --- a/src/expr.h +++ b/src/expr.h @@ -58,6 +58,9 @@ public: typedef intrusive_ptr ptr_op_t; typedef intrusive_ptr const_ptr_op_t; + friend void intrusive_ptr_add_ref(const op_t * op); + friend void intrusive_ptr_release(const op_t * op); + enum check_expr_kind_t { EXPR_GENERAL, EXPR_ASSERTION, diff --git a/src/predicate.cc b/src/predicate.cc deleted file mode 100644 index 58d6c752..00000000 --- a/src/predicate.cc +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2003-2012, 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. - */ - -#include - -#include "predicate.h" -#include "query.h" -#include "op.h" - -namespace ledger { - -} // namespace ledger diff --git a/test/unit/t_expr.cc b/test/unit/t_expr.cc index f882f3a1..c10ee029 100644 --- a/test/unit/t_expr.cc +++ b/test/unit/t_expr.cc @@ -1,5 +1,5 @@ #define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE expr +//#define BOOST_TEST_MODULE expr #include #include diff --git a/tools/Makefile.am b/tools/Makefile.am index 671db294..a598966a 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -8,7 +8,6 @@ DISTCLEANFILES = .timestamp lib_LTLIBRARIES = \ libledger_report.la \ libledger_data.la \ - libledger_expr.la \ libledger_math.la \ libledger_util.la @@ -28,6 +27,14 @@ libledger_util_la_CPPFLAGS = $(lib_cppflags) libledger_util_la_LDFLAGS = -release $(LIBVERSION) libledger_math_la_SOURCES = \ + src/format.cc \ + src/query.cc \ + src/scope.cc \ + src/expr.cc \ + src/op.cc \ + src/parser.cc \ + src/token.cc \ + src/value.cc \ src/balance.cc \ src/quotes.cc \ src/history.cc \ @@ -39,22 +46,8 @@ libledger_math_la_SOURCES = \ libledger_math_la_CPPFLAGS = $(lib_cppflags) libledger_math_la_LDFLAGS = -release $(LIBVERSION) -libledger_expr_la_SOURCES = \ - src/option.cc \ - src/format.cc \ - src/query.cc \ - src/predicate.cc \ - src/scope.cc \ - src/expr.cc \ - src/op.cc \ - src/parser.cc \ - src/token.cc \ - src/value.cc - -libledger_expr_la_CPPFLAGS = $(lib_cppflags) -libledger_expr_la_LDFLAGS = -release $(LIBVERSION) - libledger_data_la_SOURCES = \ + src/option.cc \ src/lookup.cc \ src/compare.cc \ src/iterators.cc \ @@ -204,7 +197,6 @@ DISTCLEANFILES += ledger.elc timeclock.elc all_sources = $(libledger_util_la_SOURCES) \ $(libledger_math_la_SOURCES) \ - $(libledger_expr_la_SOURCES) \ $(libledger_data_la_SOURCES) \ $(libledger_report_la_SOURCES) \ $(libledger_python_la_SOURCES) \ @@ -259,8 +251,7 @@ TESTS = RegressTests BaselineTests ManualTests ConfirmTests \ if HAVE_BOOST_TEST TESTS += \ UtilTests \ - MathTests \ - ExprTests + MathTests # DataTests \ # ReportTests endif @@ -285,6 +276,7 @@ UtilTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags) UtilTests_LDADD = libledger_util.la $(TESTLIBS) MathTests_SOURCES = \ + test/unit/t_expr.cc \ test/unit/t_commodity.cc \ test/unit/t_amount.cc \ test/unit/t_balance.cc @@ -292,16 +284,10 @@ MathTests_SOURCES = \ MathTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags) MathTests_LDADD = libledger_math.la $(UtilTests_LDADD) -ExprTests_SOURCES = \ - test/unit/t_expr.cc - -ExprTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags) -ExprTests_LDADD = libledger_expr.la $(MathTests_LDADD) - DataTests_SOURCES = DataTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags) -DataTests_LDADD = libledger_data.la $(ExprTests_LDADD) +DataTests_LDADD = libledger_data.la $(MathTests_LDADD) ReportTests_SOURCES = @@ -311,7 +297,6 @@ ReportTests_LDADD = libledger_report.la $(DataTests_LDADD) all_tests_sources = \ $(UtilTests_SOURCES) \ $(MathTests_SOURCES) \ - $(ExprTests_SOURCES) \ $(DataTests_SOURCES) \ $(ReportTests_SOURCES) @@ -421,8 +406,6 @@ unittests: check 2>&1 | grep -v '^GuardMalloc:' @sh $(FULLCHECK) $(top_builddir)/MathTests$(EXEEXT) --verify \ 2>&1 | grep -v '^GuardMalloc:' - @sh $(FULLCHECK) $(top_builddir)/ExprTests$(EXEEXT) --verify \ - 2>&1 | grep -v '^GuardMalloc:' # @sh $(FULLCHECK) $(top_builddir)/DataTests$(EXEEXT) --verify \ # 2>&1 | grep -v '^GuardMalloc:' # @sh $(FULLCHECK) $(top_builddir)/ReportTests$(EXEEXT) --verify \ -- cgit v1.2.3 From 02ffa7a515e4ebfb219af8dadf656f4ff806d6eb Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 01:59:47 -0600 Subject: Changed the category of a few DEBUG statements --- src/amount.cc | 8 ++++---- src/commodity.cc | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 3ddb7672..f9e2309b 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -731,13 +731,13 @@ amount_t::value(const optional& moment, { if (quantity) { #if defined(DEBUG_ON) - DEBUG("commodity.prices.find", + DEBUG("commodity.price.find", "amount_t::value of " << commodity().symbol()); if (moment) - DEBUG("commodity.prices.find", - "amount_t::value: moment = " << *moment); + DEBUG("commodity.price.find", + "amount_t::value: moment = " << *moment); if (in_terms_of) - DEBUG("commodity.prices.find", + DEBUG("commodity.price.find", "amount_t::value: in_terms_of = " << in_terms_of->symbol()); #endif if (has_commodity() && diff --git a/src/commodity.cc b/src/commodity.cc index 7d473d74..565204fd 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -89,6 +89,8 @@ commodity_t::find_price(const optional& commodity, const optional& moment, const optional& oldest) const { + DEBUG("commodity.price.find", "commodity_t::find_price(" << symbol() << ")"); + optional target; if (commodity) target = commodity; @@ -102,14 +104,14 @@ commodity_t::find_price(const optional& commodity, base_t::time_and_commodity_t(base_t::optional_time_pair_t(moment, oldest), commodity ? &(*commodity) : NULL); - DEBUG("history.find", "looking for memoized args: " + DEBUG("commodity.price.find", "looking for memoized args: " << (moment ? format_datetime(*moment) : "NONE") << ", " << (oldest ? format_datetime(*oldest) : "NONE") << ", " << (commodity ? commodity->symbol() : "NONE")); { base_t::memoized_price_map::iterator i = base->price_map.find(*pair); if (i != base->price_map.end()) { - DEBUG("history.find", "found! returning: " + DEBUG("commodity.price.find", "found! returning: " << ((*i).second ? (*i).second->price : amount_t(0L))); return (*i).second; } -- cgit v1.2.3 From 9509a7b8819c3ce1fd2680de65f9bd8098ac6961 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 02:00:49 -0600 Subject: Made commodity_t::find_price a virtual function --- src/commodity.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/commodity.h b/src/commodity.h index 7934f1ad..5cf9c53d 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -268,9 +268,9 @@ public: const optional& _oldest = none); optional - find_price(const optional& commodity = none, - const optional& moment = none, - const optional& oldest = none) const; + virtual find_price(const optional& commodity = none, + const optional& moment = none, + const optional& oldest = none) const; optional check_for_updated_price(const optional& point, -- cgit v1.2.3 From eb3591f898e194be0cb6c15107e8e41e9dd67206 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 02:01:25 -0600 Subject: Implemented annotated_commodity_t::find_price Most specifically, it now respects a valuation expression associated with the commodity. --- src/annotate.cc | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/annotate.h | 5 +++++ 2 files changed, 70 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/annotate.cc b/src/annotate.cc index b1db6bd5..83926587 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -34,6 +34,7 @@ #include "amount.h" #include "commodity.h" #include "expr.h" +#include "scope.h" #include "annotate.h" #include "pool.h" @@ -221,6 +222,69 @@ bool annotated_commodity_t::operator==(const commodity_t& comm) const return true; } +optional +annotated_commodity_t::find_price(const optional& commodity, + const optional& moment, + const optional& oldest) const +{ + DEBUG("commodity.price.find", + "annotated_commodity_t::find_price(" << symbol() << ")"); + + datetime_t when; + if (moment) + when = *moment; + else if (epoch) + when = *epoch; + else + when = CURRENT_TIME(); + + DEBUG("commodity.price.find", "reference time: " << when); + + optional target; + if (commodity) + target = commodity; + + if (details.price) { + DEBUG("commodity.price.find", "price annotation: " << *details.price); + + if (details.has_flags(ANNOTATION_PRICE_FIXATED)) { + DEBUG("commodity.price.find", + "amount_t::value: fixated price = " << *details.price); + return price_point_t(when, *details.price); + } + else if (! target) { + DEBUG("commodity.price.find", "setting target commodity from price"); + target = details.price->commodity(); + } + } + +#if defined(DEBUG_ON) + if (target) + DEBUG("commodity.price.find", "target commodity: " << target->symbol()); +#endif + + if (details.value_expr) { +#if defined(DEBUG_ON) + if (SHOW_DEBUG("commodity.price.find")) { + ledger::_log_buffer << "valuation expr: "; + details.value_expr->dump(ledger::_log_buffer); + DEBUG("commodity.price.find", ""); + } +#endif + call_scope_t call_args(*scope_t::default_scope); + + call_args.push_back(string_value(base_symbol())); + call_args.push_back(when); + if (commodity) + call_args.push_back(string_value(commodity->symbol())); + + return price_point_t(when, const_cast(*details.value_expr) + .calc(call_args).to_amount()); + } + + return commodity_t::find_price(commodity, moment, oldest); +} + commodity_t& annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) { @@ -262,8 +326,7 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) new_comm = pool().find_or_create (referent(), annotation_t(keep_price ? details.price : none, keep_date ? details.date : none, - keep_tag ? details.tag : none, - details.value_expr)); + keep_tag ? details.tag : none)); // Transfer over any relevant annotation flags, as they still apply. if (new_comm->annotated) { @@ -276,8 +339,6 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) new_details.add_flags(details.flags() & ANNOTATION_DATE_CALCULATED); if (keep_tag) new_details.add_flags(details.flags() & ANNOTATION_TAG_CALCULATED); - if (details.value_expr) - new_details.add_flags(details.flags() & ANNOTATION_VALUE_EXPR_CALCULATED); } return *new_comm; diff --git a/src/annotate.h b/src/annotate.h index eb87a1b2..f9d62c5b 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -247,6 +247,11 @@ public: return *ptr; } + optional + virtual find_price(const optional& commodity = none, + const optional& moment = none, + const optional& oldest = none) const; + virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); virtual void write_annotations(std::ostream& out, bool no_computed_annotations = false) const; -- cgit v1.2.3 From 97d68ebc8cf2bf88feffaedd6873934dc785c411 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 03:18:10 -0600 Subject: Added "value" sub-directive for commodity directive --- src/annotate.cc | 22 +++------------------- src/annotate.h | 6 ++++++ src/commodity.cc | 26 ++++++++++++++++++++++++++ src/commodity.h | 14 ++++++++++++++ src/textual.cc | 8 ++++++++ test/baseline/dir-commodity-value.test | 24 ++++++++++++++++++++++++ 6 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 test/baseline/dir-commodity-value.test (limited to 'src') diff --git a/src/annotate.cc b/src/annotate.cc index 83926587..d2e4976e 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -34,7 +34,6 @@ #include "amount.h" #include "commodity.h" #include "expr.h" -#include "scope.h" #include "annotate.h" #include "pool.h" @@ -263,24 +262,9 @@ annotated_commodity_t::find_price(const optional& commodity, DEBUG("commodity.price.find", "target commodity: " << target->symbol()); #endif - if (details.value_expr) { -#if defined(DEBUG_ON) - if (SHOW_DEBUG("commodity.price.find")) { - ledger::_log_buffer << "valuation expr: "; - details.value_expr->dump(ledger::_log_buffer); - DEBUG("commodity.price.find", ""); - } -#endif - call_scope_t call_args(*scope_t::default_scope); - - call_args.push_back(string_value(base_symbol())); - call_args.push_back(when); - if (commodity) - call_args.push_back(string_value(commodity->symbol())); - - return price_point_t(when, const_cast(*details.value_expr) - .calc(call_args).to_amount()); - } + if (details.value_expr) + return find_price_from_expr(const_cast(*details.value_expr), + commodity, when); return commodity_t::find_price(commodity, moment, oldest); } diff --git a/src/annotate.h b/src/annotate.h index f9d62c5b..38553752 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -247,6 +247,12 @@ public: return *ptr; } + virtual optional value_expr() const { + if (details.value_expr) + return details.value_expr; + return commodity_t::value_expr(); + } + optional virtual find_price(const optional& commodity = none, const optional& moment = none, diff --git a/src/commodity.cc b/src/commodity.cc index 565204fd..0543c973 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -35,6 +35,7 @@ #include "commodity.h" #include "annotate.h" #include "pool.h" +#include "scope.h" namespace ledger { @@ -84,6 +85,28 @@ void commodity_t::map_prices(function fn, pool().commodity_price_history.map_prices(fn, *this, when, _oldest); } +optional +commodity_t::find_price_from_expr(expr_t& expr, + const optional& commodity, + const datetime_t& moment) const +{ +#if defined(DEBUG_ON) + if (SHOW_DEBUG("commodity.price.find")) { + ledger::_log_buffer << "valuation expr: "; + expr.dump(ledger::_log_buffer); + DEBUG("commodity.price.find", ""); + } +#endif + call_scope_t call_args(*scope_t::default_scope); + + call_args.push_back(string_value(base_symbol())); + call_args.push_back(moment); + if (commodity) + call_args.push_back(string_value(commodity->symbol())); + + return price_point_t(moment, expr.calc(call_args).to_amount()); +} + optional commodity_t::find_price(const optional& commodity, const optional& moment, @@ -125,6 +148,9 @@ commodity_t::find_price(const optional& commodity, else when = CURRENT_TIME(); + if (base->value_expr) + return find_price_from_expr(*base->value_expr, commodity, when); + optional point = target ? pool().commodity_price_history.find_price(*this, *target, when, oldest) : diff --git a/src/commodity.h b/src/commodity.h index 5cf9c53d..3d36e35e 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -47,6 +47,8 @@ #ifndef _COMMODITY_H #define _COMMODITY_H +#include "expr.h" + namespace ledger { struct keep_details_t; @@ -113,6 +115,7 @@ protected: optional note; optional smaller; optional larger; + optional value_expr; typedef std::pair, optional > optional_time_pair_t; @@ -259,6 +262,13 @@ public: base->larger = arg; } + virtual optional value_expr() const { + return base->value_expr; + } + void set_value_expr(const optional& expr = none) { + base->value_expr = expr; + } + void add_price(const datetime_t& date, const amount_t& price, const bool reflexive = true); void remove_price(const datetime_t& date, commodity_t& commodity); @@ -267,6 +277,10 @@ public: const optional& moment = none, const optional& _oldest = none); + optional + find_price_from_expr(expr_t& expr, const optional& commodity, + const datetime_t& moment) const; + optional virtual find_price(const optional& commodity = none, const optional& moment = none, diff --git a/src/textual.cc b/src/textual.cc index b1df1fb8..cf15f048 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -134,6 +134,7 @@ namespace { void commodity_directive(char * line); void commodity_alias_directive(commodity_t& comm, string alias); + void commodity_value_directive(commodity_t& comm, string expr_str); void commodity_format_directive(commodity_t& comm, string format); void commodity_nomarket_directive(commodity_t& comm); void commodity_default_directive(commodity_t& comm); @@ -1018,6 +1019,8 @@ void instance_t::commodity_directive(char * line) string keyword(q); if (keyword == "alias") commodity_alias_directive(*commodity, b); + else if (keyword == "value") + commodity_value_directive(*commodity, b); else if (keyword == "format") commodity_format_directive(*commodity, b); else if (keyword == "nomarket") @@ -1036,6 +1039,11 @@ void instance_t::commodity_alias_directive(commodity_t& comm, string alias) commodity_pool_t::current_pool->alias(alias, comm); } +void instance_t::commodity_value_directive(commodity_t& comm, string expr_str) +{ + comm.set_value_expr(expr_t(expr_str)); +} + void instance_t::commodity_format_directive(commodity_t&, string format) { // jww (2012-02-27): A format specified this way should turn off diff --git a/test/baseline/dir-commodity-value.test b/test/baseline/dir-commodity-value.test new file mode 100644 index 00000000..5e8fe789 --- /dev/null +++ b/test/baseline/dir-commodity-value.test @@ -0,0 +1,24 @@ +commodity $ + value 10 EUR + +commodity USD + alias FOO + value 25 EUR + +2012-03-06 KFC + Expenses:Food $20.00 + Assets:Cash + +2012-03-08 KFC + Expenses:Food USD 750,00 + Assets:Cash + +2012-03-10 KFC + Expenses:Food USD 750,00 + Assets:Cash + +test reg food -X EUR --now=2012-03-15 +12-Mar-06 KFC Expenses:Food 200 EUR 200 EUR +12-Mar-08 KFC Expenses:Food 18750 EUR 18950 EUR +12-Mar-10 KFC Expenses:Food 18750 EUR 37700 EUR +end test -- cgit v1.2.3 From 4a93a8313e76c9a90e56180357792ec44a690cd2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 04:13:46 -0600 Subject: Whitespace fix --- src/scope.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index 785ce284..4a22c366 100644 --- a/src/scope.h +++ b/src/scope.h @@ -70,8 +70,7 @@ struct symbol_t TRACE_CTOR(symbol_t, "symbol_t::kind_t, string"); } symbol_t(const symbol_t& sym) - : kind(sym.kind), name(sym.name), - definition(sym.definition) { + : kind(sym.kind), name(sym.name), definition(sym.definition) { TRACE_CTOR(symbol_t, "copy"); } ~symbol_t() throw() { -- cgit v1.2.3 From 488355e5d92923e1d764625457b866f4fc83d58b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 04:13:54 -0600 Subject: Added symbol_t::operator==() --- src/scope.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index 4a22c366..e5127dbd 100644 --- a/src/scope.h +++ b/src/scope.h @@ -80,6 +80,9 @@ struct symbol_t bool operator<(const symbol_t& sym) const { return kind < sym.kind || name < sym.name; } + bool operator==(const symbol_t& sym) const { + return kind == sym.kind || name == sym.name; + } #if defined(HAVE_BOOST_SERIALIZATION) private: -- cgit v1.2.3 From 71d0033b6f65260698cf0bf367002a9b6429a3fd Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 23:04:27 -0600 Subject: Corrected several compile and link problems --- src/account.cc | 6 ++++-- src/amount.cc | 2 ++ src/draft.cc | 7 +++++++ src/filters.cc | 13 +++++++++++-- src/format.cc | 2 ++ src/op.cc | 10 ++++++++++ src/op.h | 8 ++------ src/pool.cc | 18 +++++++++++------- src/pyinterp.cc | 1 + src/query.h | 1 + src/textual.cc | 10 +++++++--- src/times.h | 2 ++ src/xact.cc | 2 ++ 13 files changed, 62 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index e1874839..29c05719 100644 --- a/src/account.cc +++ b/src/account.cc @@ -89,8 +89,10 @@ account_t * account_t::find_account(const string& acct_name, if (has_flags(ACCOUNT_GENERATED)) account->add_flags(ACCOUNT_GENERATED); - std::pair result - = accounts.insert(accounts_map::value_type(first, account)); +#if defined(DEBUG_ON) + std::pair result = +#endif + accounts.insert(accounts_map::value_type(first, account)); assert(result.second); } else { account = (*i).second; diff --git a/src/amount.cc b/src/amount.cc index f9e2309b..313f8d27 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -120,11 +120,13 @@ namespace { { char * buf = NULL; try { +#if defined(DEBUG_ON) IF_DEBUG("amount.convert") { char * tbuf = mpq_get_str(NULL, 10, quant); DEBUG("amount.convert", "Rational to convert = " << tbuf); std::free(tbuf); } +#endif // Convert the rational number to a floating-point, extending the // floating-point to a large enough size to get a precise answer. diff --git a/src/draft.cc b/src/draft.cc index 017c637d..9abc769e 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -109,7 +109,14 @@ void draft_t::parse_args(const value_t& args) } else if (check_for_date && bool(weekday = string_to_day_of_week(what[0]))) { +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif short dow = static_cast(*weekday); +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +#pragma GCC diagnostic pop +#endif date_t date = CURRENT_DATE() - date_duration(1); while (date.day_of_week() != dow) date -= date_duration(1); diff --git a/src/filters.cc b/src/filters.cc index 8543cddb..34df2056 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -833,10 +833,17 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, foreach (post_t * post, component_posts) { date_t date = post->date(); date_t value_date = post->value_date(); +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif if (! range_start || date < *range_start) range_start = date; if (! range_finish || value_date > *range_finish) range_finish = value_date; +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +#pragma GCC diagnostic pop +#endif } } component_posts.clear(); @@ -880,8 +887,10 @@ void subtotal_posts::operator()(post_t& post) if (i == values.end()) { value_t temp; post.add_to_value(temp, amount_expr); - std::pair result - = values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp))); +#if defined(DEBUG_ON) + std::pair result = +#endif + values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp))); assert(result.second); } else { post.add_to_value((*i).second.value, amount_expr); diff --git a/src/format.cc b/src/format.cc index d8c03e6a..79f94869 100644 --- a/src/format.cc +++ b/src/format.cc @@ -221,8 +221,10 @@ format_t::element_t * format_t::parse_elements(const string& fmt, static_cast(current->max_width) : -1); else if (keyword == "left") expr << (current->has_flags(ELEMENT_ALIGN_LEFT) ? "false" : "true"); +#if defined(DEBUG_ON) else assert("Unrecognized format substitution keyword" == NULL); +#endif } else { expr << *ptr++; } diff --git a/src/op.cc b/src/op.cc index 56d3710e..1889f2aa 100644 --- a/src/op.cc +++ b/src/op.cc @@ -38,6 +38,16 @@ namespace ledger { +void intrusive_ptr_add_ref(const expr_t::op_t * op) +{ + op->acquire(); +} + +void intrusive_ptr_release(const expr_t::op_t * op) +{ + op->release(); +} + namespace { value_t split_cons_expr(expr_t::ptr_op_t op) { diff --git a/src/op.h b/src/op.h index 192c1f5e..1807cdd7 100644 --- a/src/op.h +++ b/src/op.h @@ -260,12 +260,8 @@ private: checked_delete(this); } - friend inline void intrusive_ptr_add_ref(const op_t * op) { - op->acquire(); - } - friend inline void intrusive_ptr_release(const op_t * op) { - op->release(); - } + friend void intrusive_ptr_add_ref(const op_t * op); + friend void intrusive_ptr_release(const op_t * op); ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const { ptr_op_t node(new_node(kind, _left, _right)); diff --git a/src/pool.cc b/src/pool.cc index ca50db2c..a6ae0919 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -70,9 +70,11 @@ commodity_t * commodity_pool_t::create(const string& symbol) DEBUG("pool.commodities", "Creating commodity '" << commodity->symbol() << "'"); - std::pair result - = commodities.insert(commodities_map::value_type - (commodity->base_symbol(), commodity)); +#if defined(DEBUG_ON) + std::pair result = +#endif + commodities.insert(commodities_map::value_type + (commodity->base_symbol(), commodity)); assert(result.second); commodity_price_history.add_commodity(*commodity.get()); @@ -211,10 +213,12 @@ commodity_pool_t::create(commodity_t& comm, << "symbol " << commodity->symbol() << std::endl << details); - std::pair result - = annotated_commodities.insert(annotated_commodities_map::value_type - (annotated_commodities_map::key_type - (comm.symbol(), details), commodity)); +#if defined(DEBUG_ON) + std::pair result = +#endif + annotated_commodities.insert(annotated_commodities_map::value_type + (annotated_commodities_map::key_type + (comm.symbol(), details), commodity)); assert(result.second); return commodity.get(); diff --git a/src/pyinterp.cc b/src/pyinterp.cc index d733c40d..d1f46580 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -535,6 +535,7 @@ namespace { case value_t::ANY: // a pointer to an arbitrary object return object(val); } + return object(); } } diff --git a/src/query.h b/src/query.h index f95988a7..7286e89b 100644 --- a/src/query.h +++ b/src/query.h @@ -186,6 +186,7 @@ public: assert(false); return ""; } + return ""; } void unexpected(); diff --git a/src/textual.cc b/src/textual.cc index cf15f048..258a4d76 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -107,10 +107,12 @@ namespace { return (in.good() && ! in.eof() && (in.peek() == ' ' || in.peek() == '\t')); } +#if defined(HAVE_BOOST_PYTHON) bool peek_blank_line() { return (in.good() && ! in.eof() && (in.peek() == '\n' || in.peek() == '\r')); } +#endif void read_next_directive(); @@ -943,9 +945,11 @@ void instance_t::account_alias_directive(account_t * account, string alias) // (account), add a reference to the account in the `account_aliases' // map, which is used by the post parser to resolve alias references. trim(alias); - std::pair result - = context.journal - ->account_aliases.insert(accounts_map::value_type(alias, account)); +#if defined(DEBUG_ON) + std::pair result = +#endif + context.journal->account_aliases.insert + (accounts_map::value_type(alias, account)); assert(result.second); } diff --git a/src/times.h b/src/times.h index 6eadcad3..edc6d8b8 100644 --- a/src/times.h +++ b/src/times.h @@ -218,6 +218,7 @@ struct date_duration_t case YEARS: return date + gregorian::years(length); } + return date_t(); } date_t subtract(const date_t& date) const { @@ -233,6 +234,7 @@ struct date_duration_t case YEARS: return date - gregorian::years(length); } + return date_t(); } string to_string() const { diff --git a/src/xact.cc b/src/xact.cc index 5cec9ab0..5c43558d 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -336,7 +336,9 @@ bool xact_base_t::finalize() amount_t> sorted_amounts_map; sorted_amounts_map samp; foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { +#if defined(DEBUG_ON) std::pair result = +#endif samp.insert(sorted_amounts_map::value_type (sorted_amounts_map::key_type (pair.first->symbol(), -- cgit v1.2.3 From 17782c9cfea390782096bd8b1c8765c23182756e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 23:05:06 -0600 Subject: Simplified commodity_history_t --- src/history.cc | 45 ++++++++------------------------------------- src/history.h | 52 ++++++++++++++++------------------------------------ 2 files changed, 24 insertions(+), 73 deletions(-) (limited to 'src') diff --git a/src/history.cc b/src/history.cc index c4f6b3fc..27ba42bd 100644 --- a/src/history.cc +++ b/src/history.cc @@ -67,9 +67,6 @@ void commodity_history_t::add_price(const commodity_t& source, if (! result.second) { // There is already an entry for this moment, so update it (*result.first).second = price; - } else { - last_reftime = none; // invalidate the FGraph cache - last_oldest = none; } } @@ -85,26 +82,20 @@ void commodity_history_t::remove_price(const commodity_t& source, // jww (2012-03-04): If it fails, should we give a warning? prices.erase(date); - - last_reftime = none; // invalidate the FGraph cache - last_oldest = none; } void commodity_history_t::map_prices(function fn, const commodity_t& source, const datetime_t& moment, - const optional& _oldest) + const optional& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); - reftime = moment; - oldest = _oldest; - FGraph fg(price_graph, recent_edge_weight (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)); + moment, oldest)); FNameMap namemap(get(vertex_name, fg)); @@ -118,7 +109,7 @@ void commodity_history_t::map_prices(function= *_oldest) && when <= moment) { + if ((! oldest || when >= *oldest) && when <= moment) { if (pair.second.commodity() == source) { amount_t price(pair.second); price.in_place_invert(); @@ -136,17 +127,14 @@ void commodity_history_t::map_prices(function commodity_history_t::find_price(const commodity_t& source, const datetime_t& moment, - const optional& _oldest) + const optional& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); - reftime = moment; - oldest = _oldest; - FGraph fg(price_graph, recent_edge_weight (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)); + moment, oldest)); FNameMap namemap(get(vertex_name, fg)); @@ -188,9 +176,6 @@ commodity_history_t::find_price(const commodity_t& source, DEBUG("history.find", "price is = " << price.unrounded()); } - last_reftime = reftime; // invalidate the FGraph cache - last_oldest = oldest; - if (price.is_null()) { DEBUG("history.find", "there is no final price"); return none; @@ -204,18 +189,15 @@ optional commodity_history_t::find_price(const commodity_t& source, const commodity_t& target, const datetime_t& moment, - const optional& _oldest) + const optional& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); - reftime = moment; - oldest = _oldest; - FGraph fg(price_graph, recent_edge_weight (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)); + moment, oldest)); FNameMap namemap(get(vertex_name, fg)); @@ -289,9 +271,6 @@ commodity_history_t::find_price(const commodity_t& source, DEBUG("history.find", "last target now = " << last_target->symbol()); } - last_reftime = reftime; // invalidate the FGraph cache - last_oldest = oldest; - if (price.is_null()) { DEBUG("history.find", "there is no final price"); return none; @@ -321,18 +300,10 @@ void commodity_history_t::print_map(std::ostream& out, const optional& moment) { if (moment) { - reftime = *moment; - oldest = none; - FGraph fg(price_graph, recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)); - + (get(edge_weight, price_graph), pricemap, ratiomap, *moment)); write_graphviz(out, fg, label_writer(get(vertex_name, fg))); - - last_reftime = reftime; - last_oldest = none; } else { write_graphviz(out, price_graph, label_writer(get(vertex_name, price_graph))); diff --git a/src/history.h b/src/history.h index fc984c1b..16d552ab 100644 --- a/src/history.h +++ b/src/history.h @@ -70,39 +70,25 @@ public: PricePointMap price_point; PriceRatioMap ratios; - datetime_t * reftime; - optional * last_reftime; - optional * oldest; - optional * last_oldest; + datetime_t reftime; + optional oldest; recent_edge_weight() { } - recent_edge_weight(EdgeWeightMap _weight, - PricePointMap _price_point, - PriceRatioMap _ratios, - datetime_t * _reftime, - optional * _last_reftime, - optional * _oldest, - optional * _last_oldest) + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + datetime_t _reftime, + const optional& _oldest = none) : weight(_weight), price_point(_price_point), ratios(_ratios), - reftime(_reftime), last_reftime(_last_reftime), - oldest(_oldest), last_oldest(_last_oldest) { } + reftime(_reftime), oldest(_oldest) { } template bool operator()(const Edge& e) const { - DEBUG("history.find", " reftime = " << *reftime); - if (*last_reftime) - DEBUG("history.find", " last_reftime = " << **last_reftime); - if (*oldest) - DEBUG("history.find", " oldest = " << **oldest); - if (*last_oldest) - DEBUG("history.find", " last_oldest = " << **last_oldest); - -#if 0 - if (*last_reftime && *reftime == **last_reftime && - *oldest == *last_oldest) { - DEBUG("history.find", " using previous reftime"); - return get(weight, e) != std::numeric_limits::max(); +#if defined(DEBUG_ON) + DEBUG("history.find", " reftime = " << reftime); + if (oldest) { + DEBUG("history.find", " oldest = " << *oldest); } #endif @@ -113,22 +99,22 @@ public: return false; } - price_map_t::const_iterator low = prices.upper_bound(*reftime); + price_map_t::const_iterator low = prices.upper_bound(reftime); if (low != prices.end() && low == prices.begin()) { DEBUG("history.find", " don't use this edge"); put(weight, e, std::numeric_limits::max()); return false; } else { --low; - assert(((*low).first <= *reftime)); + assert(((*low).first <= reftime)); - if (*oldest && (*low).first < **oldest) { + if (oldest && (*low).first < *oldest) { DEBUG("history.find", " edge is out of range"); put(weight, e, std::numeric_limits::max()); return false; } - long secs = (*reftime - (*low).first).total_seconds(); + long secs = (reftime - (*low).first).total_seconds(); assert(secs >= 0); put(weight, e, secs); @@ -191,12 +177,6 @@ public: PriceRatioMap> > FGraph; typedef property_map::type FNameMap; - // jww (2012-03-05): Prevents threading - mutable datetime_t reftime; - mutable optional last_reftime; - mutable optional oldest; - mutable optional last_oldest; - commodity_history_t() : indexmap(get(vertex_index, price_graph)), pricemap(get(edge_price_point, price_graph)), -- cgit v1.2.3 From 9ea5b608991ee1769e18d95c2b2dce5a6f4202d5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 00:32:56 -0600 Subject: Whitespace fixes --- src/textual.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 258a4d76..866a99d6 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -71,22 +71,22 @@ namespace { class instance_t : public noncopyable, public scope_t { - public: - parse_context_stack_t& context_stack; - parse_context_t& context; - std::istream& in; - instance_t * parent; - + parse_context_stack_t& context_stack; + parse_context_t& context; + std::istream& in; + instance_t * parent; std::list apply_stack; #if defined(TIMELOG_SUPPORT) - time_log_t timelog; + time_log_t timelog; #endif instance_t(parse_context_stack_t& _context_stack, - parse_context_t& _context, instance_t * _parent = NULL) + parse_context_t& _context, + instance_t * _parent = NULL) : context_stack(_context_stack), context(_context), - in(*context.stream.get()), parent(_parent), timelog(context) {} + in(*context.stream.get()), parent(_parent), + timelog(context) {} virtual string description() { return _("textual parser"); -- cgit v1.2.3 From 023033f182d1cc0418d1856f544669a79f7bac6b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 00:33:24 -0600 Subject: Added a few comments --- src/journal.cc | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/journal.cc b/src/journal.cc index 55c89dd9..71492ed2 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -122,14 +122,21 @@ account_t * journal_t::register_account(const string& name, post_t * post, { account_t * result = NULL; + // If there any account aliases, substitute before creating an account + // object. if (account_aliases.size() > 0) { accounts_map::const_iterator i = account_aliases.find(name); if (i != account_aliases.end()) result = (*i).second; } + + // Create the account object and associate it with the journal; this + // is registering the account. if (! result) result = master_account->find_account(name); + // If the account name being registered is "Unknown", check whether + // the payee indicates an account that should be used. if (result->name == _("Unknown")) { foreach (account_mapping_t& value, payees_for_unknown_accounts) { if (value.first.match(post->xact->payee)) { @@ -139,6 +146,8 @@ account_t * journal_t::register_account(const string& name, post_t * post, } } + // Now that we have an account, make certain that the account is + // "known", if the user has requested validation of that fact. if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { if (! result->has_flags(ACCOUNT_KNOWN)) { if (! post) { -- cgit v1.2.3 From c86bbd45471ab4ef5d0dd133f6923596230975da Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 02:49:03 -0600 Subject: Whitespace fixes --- src/annotate.h | 1 - src/global.h | 1 + src/scope.cc | 4 ++-- src/scope.h | 8 +++----- 4 files changed, 6 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/annotate.h b/src/annotate.h index 38553752..c1f52a62 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -97,7 +97,6 @@ struct annotation_t : public supports_flags<>, } void parse(std::istream& in); - void print(std::ostream& out, bool keep_base = false, bool no_computed_annotations = false) const; diff --git a/src/global.h b/src/global.h index 2cb7842e..ce0534b0 100644 --- a/src/global.h +++ b/src/global.h @@ -82,6 +82,7 @@ public: void pop_report() { assert(! report_stack.empty()); report_stack.pop_front(); + // There should always be the "default report" waiting on the stack. assert(! report_stack.empty()); scope_t::default_scope = &report(); diff --git a/src/scope.cc b/src/scope.cc index b2a7b17b..160a97c9 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -53,8 +53,8 @@ void symbol_scope_t::define(const symbol_t::kind_t kind, assert(i != symbols->end()); symbols->erase(i); - result = symbols->insert(symbol_map::value_type(symbol_t(kind, name, def), - def)); + result = symbols->insert(symbol_map::value_type + (symbol_t(kind, name, def), def)); if (! result.second) throw_(compile_error, _("Redefinition of '%1' in the same scope") << name); diff --git a/src/scope.h b/src/scope.h index e5127dbd..7a50c423 100644 --- a/src/scope.h +++ b/src/scope.h @@ -144,8 +144,7 @@ public: explicit child_scope_t() : parent(NULL) { TRACE_CTOR(child_scope_t, ""); } - explicit child_scope_t(scope_t& _parent) - : parent(&_parent) { + explicit child_scope_t(scope_t& _parent) : parent(&_parent) { TRACE_CTOR(child_scope_t, "scope_t&"); } virtual ~child_scope_t() { @@ -276,7 +275,7 @@ class symbol_scope_t : public child_scope_t optional symbols; public: - explicit symbol_scope_t() { + explicit symbol_scope_t() : child_scope_t() { TRACE_CTOR(symbol_scope_t, ""); } explicit symbol_scope_t(scope_t& _parent) : child_scope_t(_parent) { @@ -388,8 +387,7 @@ public: : context_scope_t(_parent, _parent.type_context(), _parent.type_required()), ptr(NULL), locus(_locus), depth(_depth) { - TRACE_CTOR(call_scope_t, - "scope_t&, value_t::type_t, bool, expr_t::ptr_op_t *, int"); + TRACE_CTOR(call_scope_t, "scope_t&, expr_t::ptr_op_t *, const int"); } virtual ~call_scope_t() { TRACE_DTOR(call_scope_t); -- cgit v1.2.3 From 150ca971add9377d8afc2b88dd8efb840a19cb9a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 03:38:24 -0600 Subject: Call boost::regex, not boost::make_regex --- src/utils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/utils.h b/src/utils.h index e37f37aa..b2298ce6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -353,8 +353,8 @@ inline bool category_matches(const char * cat) { boost::make_u32regex(_log_category->c_str(), boost::regex::perl | boost::regex::icase); #else - boost::make_regex(_log_category->c_str(), - boost::regex::perl | boost::regex::icase); + boost::regex(_log_category->c_str(), + boost::regex::perl | boost::regex::icase); #endif } #if defined(HAVE_BOOST_REGEX_UNICODE) -- cgit v1.2.3 From c19b7099b2f135611192818bb968a7f8abb78493 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:16:05 -0600 Subject: Fix parsing of metadata values containing spaces --- src/item.cc | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 3a2b0b60..ef64e154 100644 --- a/src/item.cc +++ b/src/item.cc @@ -172,19 +172,7 @@ void item_t::parse_tags(const char * p, q = std::strtok(NULL, " \t")) { const string::size_type len = std::strlen(q); if (len < 2) continue; - if (! tag.empty()) { - string_map::iterator i; - string field(p + (q - buf.get())); - if (by_value) { - bind_scope_t bound_scope(scope, *this); - i = set_tag(tag, expr_t(field).calc(bound_scope), overwrite_existing); - } else { - i = set_tag(tag, string_value(field), overwrite_existing); - } - (*i).second.second = true; - break; - } - else if (q[0] == ':' && q[len - 1] == ':') { // a series of tags + if (q[0] == ':' && q[len - 1] == ':') { // a series of tags for (char * r = std::strtok(q + 1, ":"); r; r = std::strtok(NULL, ":")) { @@ -199,6 +187,24 @@ void item_t::parse_tags(const char * p, index = 2; } tag = string(q, len - index); + + string_map::iterator i; + const string::size_type plen = std::strlen(p); + const char * v = p + (q - p) + (len - index) + 1; + while (*v == '\0' && v < p + plen) + ++v; + + if (v < p + plen) { + string field(v); + if (by_value) { + bind_scope_t bound_scope(scope, *this); + i = set_tag(tag, expr_t(field).calc(bound_scope), overwrite_existing); + } else { + i = set_tag(tag, string_value(field), overwrite_existing); + } + (*i).second.second = true; + break; + } } first = false; } -- cgit v1.2.3 From 2b108c66ccb7750660611a1a9fe5eb6cf63a686b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:16:34 -0600 Subject: Added an empty_scope_t utility class --- src/scope.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index 7a50c423..1b2566d5 100644 --- a/src/scope.h +++ b/src/scope.h @@ -136,6 +136,17 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; +class empty_scope_t : public scope_t +{ +public: + virtual string description() { + return _(""); + } + virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t, const string&) { + return NULL; + } +}; + class child_scope_t : public noncopyable, public scope_t { public: -- cgit v1.2.3 From 7eb1eddcf6a0454a68af8650ab9194da829aa41f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:18:33 -0600 Subject: Removed unused function expr_t::op_t::wrap_scope --- src/op.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src') diff --git a/src/op.h b/src/op.h index 1807cdd7..8b914311 100644 --- a/src/op.h +++ b/src/op.h @@ -355,13 +355,6 @@ expr_t::op_t::wrap_functor(expr_t::func_t fobj) { return temp; } -inline expr_t::ptr_op_t -expr_t::op_t::wrap_scope(shared_ptr sobj) { - ptr_op_t temp(new op_t(op_t::SCOPE)); - temp->set_scope(sobj); - return temp; -} - #define MAKE_FUNCTOR(x) expr_t::op_t::wrap_functor(bind(&x, this, _1)) #define WRAP_FUNCTOR(x) expr_t::op_t::wrap_functor(x) -- cgit v1.2.3 From 3ae4a38e4df38ed857dac8ffa26dfb1387f2b62d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:19:21 -0600 Subject: Fixed parsing of "deferred notes" in auto xacts --- src/textual.cc | 7 +------ src/xact.h | 14 ++++++++------ 2 files changed, 9 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 866a99d6..fa416d06 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -559,11 +559,6 @@ void instance_t::automated_xact_directive(char * line) item->add_flags(ITEM_NOTE_ON_NEXT_LINE); item->pos->end_pos = context.curr_pos; item->pos->end_line++; - - // If there was no last_post yet, then deferred notes get applied to - // the matched posting. Other notes get applied to the auto-generated - // posting. - ae->deferred_notes->back().apply_to_post = last_post; } else if ((remlen > 7 && *p == 'a' && std::strncmp(p, "assert", 6) == 0 && std::isspace(p[6])) || @@ -591,7 +586,7 @@ void instance_t::automated_xact_directive(char * line) parse_post(p, len - (p - line), top_account(), NULL, true)) { reveal_context = true; ae->add_post(post); - last_post = post; + ae->active_post = last_post = post; } reveal_context = true; } diff --git a/src/xact.h b/src/xact.h index 7d7fb826..df82258d 100644 --- a/src/xact.h +++ b/src/xact.h @@ -173,17 +173,19 @@ public: typedef std::list deferred_notes_list; optional deferred_notes; + post_t * active_post; - auto_xact_t() : try_quick_match(true) { + auto_xact_t() : try_quick_match(true), active_post(NULL) { TRACE_CTOR(auto_xact_t, ""); } auto_xact_t(const auto_xact_t& other) : xact_base_t(), predicate(other.predicate), - try_quick_match(other.try_quick_match) { + try_quick_match(other.try_quick_match), + active_post(other.active_post) { TRACE_CTOR(auto_xact_t, "copy"); } auto_xact_t(const predicate_t& _predicate) - : predicate(_predicate), try_quick_match(true) + : predicate(_predicate), try_quick_match(true), active_post(NULL) { TRACE_CTOR(auto_xact_t, "const predicate_t&"); } @@ -202,12 +204,12 @@ public: } } - virtual void parse_tags(const char * p, - scope_t&, - bool overwrite_existing = true) { + virtual void parse_tags(const char * p, scope_t&, + bool overwrite_existing = true) { if (! deferred_notes) deferred_notes = deferred_notes_list(); deferred_notes->push_back(deferred_tag_data_t(p, overwrite_existing)); + deferred_notes->back().apply_to_post = active_post; } virtual void extend_xact(xact_base_t& xact, parse_context_t& context); -- cgit v1.2.3 From 69b25d367ed2317a537aefa9744ca315b5422fa5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:19:46 -0600 Subject: Call register_account for posts created by auto xacts --- src/xact.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index 5c43558d..082eacc8 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -775,9 +775,10 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) post_t * new_post = new post_t(account, amt); new_post->copy_details(*post); new_post->add_flags(ITEM_GENERATED); + new_post->account = + journal->register_account(account->fullname(), new_post, + journal->master); - // jww (2012-02-27): Do account directive assertions get applied - // to postings generated from automated transactions? xact.add_post(new_post); new_post->account->add_post(new_post); -- cgit v1.2.3 From 76f97a63da2deb8b6f9f8129bb11f5608d4a5518 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:20:42 -0600 Subject: Added parsing support for the many value directives --- src/account.h | 1 + src/journal.cc | 6 ++++-- src/journal.h | 7 ++++--- src/post.cc | 38 ++++++++++++++++++++++++++++++++++++++ src/post.h | 3 +++ src/textual.cc | 22 ++++++++++++++++++++++ src/xact.cc | 2 ++ 7 files changed, 74 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/account.h b/src/account.h index 8f0f915f..95e04079 100644 --- a/src/account.h +++ b/src/account.h @@ -67,6 +67,7 @@ public: unsigned short depth; accounts_map accounts; posts_list posts; + optional value_expr; mutable string _fullname; diff --git a/src/journal.cc b/src/journal.cc index 71492ed2..37eacdaf 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -322,10 +322,12 @@ bool journal_t::add_xact(xact_t * xact) } extend_xact(xact); - check_all_metadata(*this, xact); - foreach (post_t * post, xact->posts) + + foreach (post_t * post, xact->posts) { + extend_post(*post, *this); check_all_metadata(*this, post); + } // If a transaction with this UUID has already been seen, simply do // not add this one to the journal. However, all automated checks diff --git a/src/journal.h b/src/journal.h index 8b750993..ca73c415 100644 --- a/src/journal.h +++ b/src/journal.h @@ -127,16 +127,17 @@ public: bool fixed_payees; bool fixed_commodities; bool fixed_metadata; + bool was_loaded; + bool force_checking; + bool check_payees; payee_mappings_t payee_mappings; account_mappings_t account_mappings; accounts_map account_aliases; account_mappings_t payees_for_unknown_accounts; checksum_map_t checksum_map; tag_check_exprs_map tag_check_exprs; + optional value_expr; parse_context_t * current_context; - bool was_loaded; - bool force_checking; - bool check_payees; enum checking_style_t { CHECK_PERMISSIVE, diff --git a/src/post.cc b/src/post.cc index 191a9142..7a6667f2 100644 --- a/src/post.cc +++ b/src/post.cc @@ -36,6 +36,7 @@ #include "account.h" #include "journal.h" #include "format.h" +#include "pool.h" namespace ledger { @@ -638,6 +639,43 @@ void post_t::set_reported_account(account_t * acct) acct->xdata().reported_posts.push_back(this); } +void extend_post(post_t& post, journal_t& journal) +{ + commodity_t& comm(post.amount.commodity()); + + annotation_t * details = + (comm.has_annotation() ? + &as_annotated_commodity(comm).details : NULL); + + if (! details || ! details->value_expr) { + optional value_expr; + + if (optional data = post.get_tag(_("Value"))) + value_expr = expr_t(data->to_string()); + + if (! value_expr) + value_expr = post.account->value_expr; + + if (! value_expr) + value_expr = post.amount.commodity().value_expr(); + + if (! value_expr) + value_expr = journal.value_expr; + + if (value_expr) { + if (! details) { + annotation_t new_details; + new_details.value_expr = value_expr; + commodity_t * new_comm = + commodity_pool_t::current_pool->find_or_create(comm, new_details); + post.amount.set_commodity(*new_comm); + } else { + details->value_expr = value_expr; + } + } + } +} + void to_xml(std::ostream& out, const post_t& post) { push_xml x(out, "posting", true); diff --git a/src/post.h b/src/post.h index ce33fefc..217f5509 100644 --- a/src/post.h +++ b/src/post.h @@ -259,6 +259,9 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; +class journal_t; +void extend_post(post_t& post, journal_t& journal); + void to_xml(std::ostream& out, const post_t& post); } // namespace ledger diff --git a/src/textual.cc b/src/textual.cc index fa416d06..1d1835e3 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -126,6 +126,7 @@ namespace { void account_directive(char * line); void account_alias_directive(account_t * account, string alias); void account_payee_directive(account_t * account, string payee); + void account_value_directive(account_t * account, string expr_str); void account_default_directive(account_t * account); void default_account_directive(char * line); @@ -166,6 +167,7 @@ namespace { void eval_directive(char * line); void assert_directive(char * line); void check_directive(char * line); + void value_directive(char * line); void import_directive(char * line); void python_directive(char * line); @@ -887,6 +889,9 @@ void instance_t::account_directive(char * line) else if (keyword == "payee") { account_payee_directive(account, b); } + else if (keyword == "value") { + account_value_directive(account, b); + } else if (keyword == "default") { account_default_directive(account); } @@ -974,6 +979,11 @@ void instance_t::account_default_directive(account_t * account) context.journal->bucket = account; } +void instance_t::account_value_directive(account_t * account, string expr_str) +{ + account->value_expr = expr_t(expr_str); +} + void instance_t::payee_directive(char * line) { string payee = context.journal->register_payee(line, NULL); @@ -1108,6 +1118,11 @@ void instance_t::check_directive(char * line) context.warning(STR(_("Check failed: %1") << line)); } +void instance_t::value_directive(char * line) +{ + context.journal->value_expr = expr_t(line); +} + void instance_t::comment_directive(char * line) { while (in.good() && ! in.eof()) { @@ -1288,6 +1303,13 @@ bool instance_t::general_directive(char * line) return true; } break; + + case 'v': + if (std::strcmp(p, "value") == 0) { + value_directive(arg); + return true; + } + break; } if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) { diff --git a/src/xact.cc b/src/xact.cc index 082eacc8..5e4b4c64 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -779,6 +779,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) journal->register_account(account->fullname(), new_post, journal->master); + extend_post(*new_post, *journal); + xact.add_post(new_post); new_post->account->add_post(new_post); -- cgit v1.2.3 From 65835a89ccbcb9c0a0297b4f7c6adbfd768dca84 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:21:40 -0600 Subject: Dump IDENT referral values in op_t::dump --- src/op.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index 1889f2aa..b8893bc9 100644 --- a/src/op.cc +++ b/src/op.cc @@ -818,7 +818,7 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const // An identifier is a special non-terminal, in that its left() can // hold the compiled definition of the identifier. - if (kind > TERMINALS || is_scope()) { + if (kind > TERMINALS || is_scope() || is_ident()) { if (left()) { left()->dump(out, depth + 1); if (kind > UNARY_OPERATORS && has_right()) -- cgit v1.2.3 From 89e0472b50364bed2a5a2068c0121b64b3b0321b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:37:38 -0600 Subject: Another fix to metadata value parsing --- src/item.cc | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index ef64e154..4ddbe913 100644 --- a/src/item.cc +++ b/src/item.cc @@ -189,22 +189,16 @@ void item_t::parse_tags(const char * p, tag = string(q, len - index); string_map::iterator i; - const string::size_type plen = std::strlen(p); - const char * v = p + (q - p) + (len - index) + 1; - while (*v == '\0' && v < p + plen) - ++v; - - if (v < p + plen) { - string field(v); - if (by_value) { - bind_scope_t bound_scope(scope, *this); - i = set_tag(tag, expr_t(field).calc(bound_scope), overwrite_existing); - } else { - i = set_tag(tag, string_value(field), overwrite_existing); - } - (*i).second.second = true; - break; + string field(p + len + index); + trim(field); + if (by_value) { + bind_scope_t bound_scope(scope, *this); + i = set_tag(tag, expr_t(field).calc(bound_scope), overwrite_existing); + } else { + i = set_tag(tag, string_value(field), overwrite_existing); } + (*i).second.second = true; + break; } first = false; } -- cgit v1.2.3 From 1e18536a8c6e3e83257687ec4588ddba5fcb890b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:37:49 -0600 Subject: Deferred notes were being processed too late --- src/xact.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index 5e4b4c64..4ec40f69 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -779,6 +779,14 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) journal->register_account(account->fullname(), new_post, journal->master); + if (deferred_notes) { + foreach (deferred_tag_data_t& data, *deferred_notes) { + if (! data.apply_to_post || data.apply_to_post == post) + new_post->parse_tags(data.tag_data.c_str(), bound_scope, + data.overwrite_existing); + } + } + extend_post(*new_post, *journal); xact.add_post(new_post); @@ -786,14 +794,6 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) if (new_post->must_balance()) needs_further_verification = true; - - if (deferred_notes) { - foreach (deferred_tag_data_t& data, *deferred_notes) { - if (data.apply_to_post == post) - new_post->parse_tags(data.tag_data.c_str(), bound_scope, - data.overwrite_existing); - } - } } } } -- cgit v1.2.3 From b17b6cd9d42eda5db0ecf69d7fdef7f4e64d6cb1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:38:13 -0600 Subject: Fixed parameter and argument scoping for O_CALL nodes --- src/op.cc | 65 ++++++++++++++++++++++++++++++++++++++++++++++++--------------- src/op.h | 4 +++- 2 files changed, 53 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index b8893bc9..a8b71468 100644 --- a/src/op.cc +++ b/src/op.cc @@ -86,7 +86,8 @@ namespace { } } -expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) +expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, + scope_t * param_scope) { scope_t * scope_ptr = &scope; expr_t::ptr_op_t result; @@ -103,7 +104,12 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) if (is_ident()) { DEBUG("expr.compile", "Lookup: " << as_ident() << " in " << scope_ptr); - if (ptr_op_t def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident())) { + ptr_op_t def; + if (param_scope) + def = param_scope->lookup(symbol_t::FUNCTION, as_ident()); + if (! def) + def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident()); + if (def) { // Identifier references are first looked up at the point of // definition, and then at the point of every use if they could // not be found there. @@ -133,7 +139,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) else if (kind == O_DEFINE) { switch (left()->kind) { case IDENT: { - ptr_op_t node(right()->compile(*scope_ptr, depth + 1)); + ptr_op_t node(right()->compile(*scope_ptr, depth + 1, param_scope)); DEBUG("expr.compile", "Defining " << left()->as_ident() << " in " << scope_ptr); @@ -146,12 +152,28 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) ptr_op_t node(new op_t(op_t::O_LAMBDA)); node->set_left(left()->right()); node->set_right(right()); - node = node->compile(*scope_ptr, depth + 1); + + empty_scope_t empty_scope; + symbol_scope_t params(param_scope ? *param_scope : empty_scope); + for (ptr_op_t sym = node->left(); + sym; + sym = sym->has_right() ? sym->right() : NULL) { + ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; + if (! varname->is_ident()) { + throw_(calc_error, _("Invalid function definition")); + } else { + DEBUG("expr.compile", + "Defining function parameter " << varname->as_ident()); + params.define(symbol_t::FUNCTION, varname->as_ident(), + new op_t(op_t::PLUG)); + } + } + + node = node->compile(*scope_ptr, depth + 1, ¶ms); DEBUG("expr.compile", "Defining " << left()->left()->as_ident() << " in " << scope_ptr); - scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(), - node); + scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(), node); break; } // fall through... @@ -163,10 +185,10 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) } if (! result) { - ptr_op_t lhs(left()->compile(*scope_ptr, depth + 1)); + ptr_op_t lhs(left()->compile(*scope_ptr, depth + 1, param_scope)); ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ? (kind == O_LOOKUP ? right() : - right()->compile(*scope_ptr, depth + 1)) : NULL); + right()->compile(*scope_ptr, depth + 1, param_scope)) : NULL); if (lhs == left() && (! rhs || rhs == right())) { result = this; @@ -220,7 +242,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) ptr_op_t definition = left(); // If no definition was pre-compiled for this identifier, look it up // in the current scope. - if (! definition) { + if (! definition || definition->kind == PLUG) { DEBUG("scope.symbols", "Looking for IDENT '" << as_ident() << "'"); definition = scope.lookup(symbol_t::FUNCTION, as_ident()); } @@ -308,7 +330,8 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) call_scope_t& call_args(find_scope(scope, true)); std::size_t args_count(call_args.size()); std::size_t args_index(0); - symbol_scope_t call_scope(call_args); + empty_scope_t empty_scope; + symbol_scope_t args_scope(empty_scope); for (ptr_op_t sym = left(); sym; @@ -318,13 +341,15 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) throw_(calc_error, _("Invalid function definition")); } else if (args_index == args_count) { - call_scope.define(symbol_t::FUNCTION, varname->as_ident(), + DEBUG("expr.compile", "Defining function argument as null: " + << varname->as_ident()); + args_scope.define(symbol_t::FUNCTION, varname->as_ident(), wrap_value(NULL_VALUE)); } else { - DEBUG("expr.compile", - "Defining function parameter " << varname->as_ident()); - call_scope.define(symbol_t::FUNCTION, varname->as_ident(), + DEBUG("expr.compile", "Defining function argument from call_args: " + << varname->as_ident()); + args_scope.define(symbol_t::FUNCTION, varname->as_ident(), wrap_value(call_args[args_index++])); } } @@ -334,7 +359,13 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) _("Too few arguments in function call (saw %1, wanted %2)") << args_count << args_index); - result = right()->calc(call_scope, locus, depth + 1); + if (right()->is_scope()) { + bind_scope_t outer_scope(scope, *right()->as_scope()); + bind_scope_t bound_scope(outer_scope, args_scope); + result = right()->left()->calc(bound_scope, locus, depth + 1); + } else { + result = right()->calc(args_scope, locus, depth + 1); + } break; } @@ -758,6 +789,10 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const out << " "; switch (kind) { + case PLUG: + out << "PLUG"; + break; + case VALUE: out << "VALUE: "; as_value().dump(out); diff --git a/src/op.h b/src/op.h index 8b914311..c93f218b 100644 --- a/src/op.h +++ b/src/op.h @@ -69,6 +69,7 @@ private: public: enum kind_t { // Constants + PLUG, VALUE, IDENT, @@ -274,7 +275,8 @@ public: static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL, ptr_op_t _right = NULL); - ptr_op_t compile(scope_t& scope, const int depth = 0); + ptr_op_t compile(scope_t& scope, const int depth = 0, + scope_t * param_scope = NULL); value_t calc(scope_t& scope, ptr_op_t * locus = NULL, const int depth = 0); -- cgit v1.2.3 From 7bb83173dae617b8e754fff8e52390ff9664873b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 08:34:15 -0600 Subject: Added some missing DEBUG #if's --- src/account.cc | 2 ++ src/filters.cc | 2 ++ src/pool.cc | 4 ++++ src/textual.cc | 2 ++ src/xact.cc | 2 ++ 5 files changed, 12 insertions(+) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index 29c05719..d772368c 100644 --- a/src/account.cc +++ b/src/account.cc @@ -93,7 +93,9 @@ account_t * account_t::find_account(const string& acct_name, std::pair result = #endif accounts.insert(accounts_map::value_type(first, account)); +#if defined(DEBUG_ON) assert(result.second); +#endif } else { account = (*i).second; } diff --git a/src/filters.cc b/src/filters.cc index 34df2056..3a975920 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -891,7 +891,9 @@ void subtotal_posts::operator()(post_t& post) std::pair result = #endif values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp))); +#if defined(DEBUG_ON) assert(result.second); +#endif } else { post.add_to_value((*i).second.value, amount_expr); } diff --git a/src/pool.cc b/src/pool.cc index a6ae0919..fd661fe1 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -75,7 +75,9 @@ commodity_t * commodity_pool_t::create(const string& symbol) #endif commodities.insert(commodities_map::value_type (commodity->base_symbol(), commodity)); +#if defined(DEBUG_ON) assert(result.second); +#endif commodity_price_history.add_commodity(*commodity.get()); @@ -219,7 +221,9 @@ commodity_pool_t::create(commodity_t& comm, annotated_commodities.insert(annotated_commodities_map::value_type (annotated_commodities_map::key_type (comm.symbol(), details), commodity)); +#if defined(DEBUG_ON) assert(result.second); +#endif return commodity.get(); } diff --git a/src/textual.cc b/src/textual.cc index 1d1835e3..66972fb3 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -950,7 +950,9 @@ void instance_t::account_alias_directive(account_t * account, string alias) #endif context.journal->account_aliases.insert (accounts_map::value_type(alias, account)); +#if defined(DEBUG_ON) assert(result.second); +#endif } void instance_t::alias_directive(char * line) diff --git a/src/xact.cc b/src/xact.cc index 4ec40f69..4e43e680 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -346,7 +346,9 @@ bool xact_base_t::finalize() as_annotated_commodity(*pair.first).details : annotation_t()), pair.second)); +#if defined(DEBUG_ON) assert(result.second); +#endif } bool first = true; -- cgit v1.2.3 From b1107f85ae07a85124e58a0e379ec2d9ab47d119 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 10:32:24 -0600 Subject: Removed value_t::price and balance_t::price --- src/balance.cc | 10 ---------- src/balance.h | 2 -- src/py_balance.cc | 2 -- src/py_value.cc | 3 +-- src/report.cc | 7 ------- src/report.h | 1 - src/value.cc | 12 ------------ src/value.h | 2 -- 8 files changed, 1 insertion(+), 38 deletions(-) (limited to 'src') diff --git a/src/balance.cc b/src/balance.cc index 4fba7344..08368dd8 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -202,16 +202,6 @@ balance_t::value(const optional& moment, return resolved ? temp : optional(); } -balance_t balance_t::price() const -{ - balance_t temp; - - foreach (const amounts_map::value_type& pair, amounts) - temp += pair.second.price(); - - return temp; -} - optional balance_t::commodity_amount(const optional& commodity) const { diff --git a/src/balance.h b/src/balance.h index 57e6ace4..921f87ef 100644 --- a/src/balance.h +++ b/src/balance.h @@ -387,8 +387,6 @@ public: value(const optional& moment = none, const optional& in_terms_of = none) const; - balance_t price() const; - /** * Truth tests. An balance may be truth test in two ways: * diff --git a/src/py_balance.cc b/src/py_balance.cc index 6c9ccb24..38941832 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -201,8 +201,6 @@ void export_balance() .def("value", py_value_1, args("in_terms_of")) .def("value", py_value_2, args("in_terms_of", "moment")) - .def("price", &balance_t::price) - .def("__nonzero__", &balance_t::is_nonzero) .def("is_nonzero", &balance_t::is_nonzero) .def("is_zero", &balance_t::is_zero) diff --git a/src/py_value.cc b/src/py_value.cc index 949f2a49..78301acd 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -266,8 +266,7 @@ void export_value() .def("value", py_value_1, args("in_terms_of")) .def("value", py_value_2, args("in_terms_of", "moment")) - .def("value", &value_t::value, value_overloads()) - .def("price", &value_t::price) + //.def("value", &value_t::value, value_overloads()) .def("exchange_commodities", &value_t::exchange_commodities, exchange_commodities_overloads()) diff --git a/src/report.cc b/src/report.cc index b86f77eb..52e8c888 100644 --- a/src/report.cc +++ b/src/report.cc @@ -731,11 +731,6 @@ value_t report_t::fn_percent(call_scope_t& args) (args.get(0) / args.get(1)).number()); } -value_t report_t::fn_price(call_scope_t& args) -{ - return args[0].price(); -} - value_t report_t::fn_commodity(call_scope_t& args) { return string_value(args.get(0).commodity().symbol()); @@ -1278,8 +1273,6 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(fn_false); else if (is_eq(p, "percent")) return MAKE_FUNCTOR(report_t::fn_percent); - else if (is_eq(p, "price")) - return MAKE_FUNCTOR(report_t::fn_price); else if (is_eq(p, "print")) return MAKE_FUNCTOR(report_t::fn_print); break; diff --git a/src/report.h b/src/report.h index 03eee78b..9541da43 100644 --- a/src/report.h +++ b/src/report.h @@ -169,7 +169,6 @@ public: value_t fn_format_date(call_scope_t& scope); value_t fn_ansify_if(call_scope_t& scope); value_t fn_percent(call_scope_t& scope); - value_t fn_price(call_scope_t& scope); value_t fn_commodity(call_scope_t& scope); value_t fn_lot_date(call_scope_t& scope); value_t fn_lot_price(call_scope_t& scope); diff --git a/src/value.cc b/src/value.cc index 2446c97a..de491e6c 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1427,18 +1427,6 @@ value_t value_t::value(const optional& moment, return NULL_VALUE; } -value_t value_t::price() const -{ - switch (type()) { - case AMOUNT: - return as_amount().price(); - case BALANCE: - return as_balance().price(); - default: - return *this; - } -} - value_t value_t::exchange_commodities(const std::string& commodities, const bool add_prices, const optional& moment) diff --git a/src/value.h b/src/value.h index 1e4d0ce9..df075843 100644 --- a/src/value.h +++ b/src/value.h @@ -480,8 +480,6 @@ public: value_t value(const optional& moment = none, const optional& in_terms_of = none) const; - value_t price() const; - value_t exchange_commodities(const std::string& commodities, const bool add_prices = false, const optional& moment = none); -- cgit v1.2.3 From ec1f15a4b2ae706dd99aff0d506977486ffef08b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 10:32:35 -0600 Subject: Made amount_t::price return an optional --- src/amount.cc | 4 ++-- src/amount.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 313f8d27..c80ca14e 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -787,7 +787,7 @@ amount_t::value(const optional& moment, return none; } -amount_t amount_t::price() const +optional amount_t::price() const { if (has_annotation() && annotation().price) { amount_t tmp(*annotation().price); @@ -795,7 +795,7 @@ amount_t amount_t::price() const DEBUG("amount.price", "Returning price of " << *this << " = " << tmp); return tmp; } - return *this; + return none; } diff --git a/src/amount.h b/src/amount.h index 1db59b7e..09256b5d 100644 --- a/src/amount.h +++ b/src/amount.h @@ -407,7 +407,7 @@ public: value(const optional& moment = none, const optional& in_terms_of = none) const; - amount_t price() const; + optional price() const; /*@}*/ -- cgit v1.2.3 From dd6bcf347e2389cc6dce7cc161c8d2188160e582 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 10:32:46 -0600 Subject: Removed an unnecessary assert --- src/chain.cc | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/chain.cc b/src/chain.cc index fc1be5bd..400b8f26 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -115,7 +115,6 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, predicate_t only_predicate; display_filter_posts * display_filter = NULL; - assert(report.HANDLED(amount_)); expr_t& expr(report.HANDLER(amount_).expr); expr.set_context(&report); -- cgit v1.2.3 From 755495ee8a019e458f43ea03449e7cd21d3f277f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 10:33:06 -0600 Subject: Changed scope of two DEBUG statements --- src/exprbase.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/exprbase.h b/src/exprbase.h index 56900dbb..bea25320 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -163,7 +163,7 @@ public: } #endif // defined(DEBUG_ON) - DEBUG("expr.calc.when", "Compiling: " << str); + DEBUG("expr.compile", "Compiling: " << str); compile(scope); #if defined(DEBUG_ON) @@ -174,7 +174,7 @@ public: #endif // defined(DEBUG_ON) } - DEBUG("expr.calc.when", "Calculating: " << str); + DEBUG("expr.calc", "Calculating: " << str); return real_calc(scope); } -- cgit v1.2.3 From a19578a6072d85857e0a9c837db3a8801f83577c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 10:33:32 -0600 Subject: Added "price" value expression for posts --- src/post.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/post.cc b/src/post.cc index 7a6667f2..babb1292 100644 --- a/src/post.cc +++ b/src/post.cc @@ -240,6 +240,15 @@ namespace { return post.amount; } + value_t get_price(post_t& post) { + if (post.amount.is_null()) + return 0L; + if (post.amount.has_annotation() && post.amount.annotation().price) + return *post.amount.price(); + else + return get_cost(post); + } + value_t get_total(post_t& post) { if (post.xdata_ && ! post.xdata_->total.is_null()) return post.xdata_->total; @@ -475,6 +484,8 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_payee>); else if (name == "primary") return WRAP_FUNCTOR(get_wrapper<&get_commodity_is_primary>); + else if (name == "price") + return WRAP_FUNCTOR(get_wrapper<&get_price>); else if (name == "parent") return WRAP_FUNCTOR(get_wrapper<&get_xact>); break; -- cgit v1.2.3 From 90029d9925f2662c655d72fab1f46ee343765ba7 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 10:33:46 -0600 Subject: Allow identifier names to begin with _ --- src/token.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/token.cc b/src/token.cc index 57b97eca..e5d6b218 100644 --- a/src/token.cc +++ b/src/token.cc @@ -421,7 +421,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags) throw_(parse_error, _("Failed to reset input stream")); c = static_cast(in.peek()); - if (! std::isalpha(c)) + if (! std::isalpha(c) && c != '_') expected('\0', c); parse_ident(in); -- cgit v1.2.3 From be778e387967e56c711ed66e5aafda2d6d7bbc11 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 10:35:01 -0600 Subject: Changed definition of the DECL1 macro --- src/option.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/option.h b/src/option.h index dc1099db..38431f9d 100644 --- a/src/option.h +++ b/src/option.h @@ -213,7 +213,7 @@ public: name ## option_t() : option_t(#name), base #define DECL1(type, name, vartype, var, value) \ vartype var ; \ - name ## option_t() : option_t(#name), var(value) + name ## option_t() : option_t(#name), var value #define DO() virtual void handler_thunk(call_scope_t&) #define DO_(var) virtual void handler_thunk(call_scope_t& var) -- cgit v1.2.3 From c33d7480a60515e4d8b184e01e683e9e74fe7c21 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 10:35:32 -0600 Subject: Created merged_expr_t class for chained expressions --- src/expr.cc | 41 ++++++++++++++++++++++++++++++++++++++++ src/expr.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/expr.cc b/src/expr.cc index 74d16ecc..8c8e995a 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -163,6 +163,47 @@ void expr_t::dump(std::ostream& out) const if (ptr) ptr->dump(out, 0); } +bool merged_expr_t::check_for_single_identifier(const string& expr) +{ + bool single_identifier = true; + for (const char * p = expr.c_str(); *p; ++p) + if (! std::isalnum(*p) || *p == '_') { + single_identifier = false; + break; + } + + if (single_identifier) { + set_base_expr(expr); + exprs.clear(); + return true; + } else { + return false; + } +} + +void merged_expr_t::compile(scope_t& scope) +{ + if (exprs.empty()) { + parse(base_expr); + } else { + std::ostringstream buf; + + buf << "__tmp_" << term << "=(" << term << "=(" << base_expr << ")"; + foreach (const string& expr, exprs) { + if (merge_operator == ";") + buf << merge_operator << term << "=" << expr; + else + buf << merge_operator << "(" << expr << ")"; + } + buf << ";" << term << ");__tmp_" << term; + + DEBUG("expr.merged.compile", "Compiled expr: " << buf.str()); + parse(buf.str()); + } + + expr_t::compile(scope); +} + value_t source_command(call_scope_t& args) { std::istream * in = NULL; diff --git a/src/expr.h b/src/expr.h index ab3487fe..5dc6430a 100644 --- a/src/expr.h +++ b/src/expr.h @@ -172,8 +172,68 @@ inline value_t expr_value(expr_t::ptr_op_t op) { return temp; } -class call_scope_t; +// A merged expression allows one to set an expression term, "foo", and +// a base expression, "bar", and then merge in later expressions that +// utilize foo. For example: +// +// foo: bar +// merge: foo * 10 +// merge: foo + 20 +// +// When this expression is finally compiled, the base and merged +// elements are written into this: +// +// __tmp=(foo=bar; foo=foo*10; foo=foo+20);__tmp +// +// This allows users to select flags like -O, -B or -I at any time, and +// also combine flags such as -V and -A. + +class merged_expr_t : public expr_t +{ +public: + string term; + string base_expr; + string merge_operator; + std::list exprs; + + merged_expr_t(const string& _term, const string& expr, + const string& merge_op = ";") + : expr_t(), term(_term), base_expr(expr), merge_operator(merge_op) { + TRACE_CTOR(merged_expr_t, "string, string, string"); + } + + virtual ~merged_expr_t() { + TRACE_DTOR(merged_expr_t); + } + + void set_term(const string& _term) { + term = _term; + } + void set_base_expr(const string& expr) { + base_expr = expr; + } + void set_merge_operator(const string& merge_op) { + merge_operator = merge_op; + } + + bool check_for_single_identifier(const string& expr); + void prepend(const string& expr) { + if (! check_for_single_identifier(expr)) + exprs.push_front(expr); + } + void append(const string& expr) { + if (! check_for_single_identifier(expr)) + exprs.push_back(expr); + } + void remove(const string& expr) { + exprs.remove(expr); + } + + virtual void compile(scope_t& scope); +}; + +class call_scope_t; value_t source_command(call_scope_t& scope); } // namespace ledger -- cgit v1.2.3 From 0b8d024efc71444209bdb9f66287c4dbe2db43c0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 11:30:45 -0600 Subject: Removed unused file series.h --- src/series.h | 135 ------------------------------------------------------ tools/Makefile.am | 1 - 2 files changed, 136 deletions(-) delete mode 100644 src/series.h (limited to 'src') diff --git a/src/series.h b/src/series.h deleted file mode 100644 index 75b98194..00000000 --- a/src/series.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2003-2012, 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 series.h - * @author John Wiegley - * - * @ingroup expr - */ -#ifndef _SERIES_H -#define _SERIES_H - -#include "scope.h" - -namespace ledger { - -class expr_series_t -{ -protected: - scope_t * context; - -public: - optional > exprs; - expr_t default_expr; - std::string variable; - - expr_series_t(const std::string& _variable) - : context(NULL), default_expr(_variable), variable(_variable) { - TRACE_CTOR(expr_series_t, "std::string"); - } - expr_series_t(const expr_t& expr, const std::string& _variable) - : context(const_cast(expr).get_context()), - default_expr(expr), variable(_variable) { - TRACE_CTOR(expr_series_t, "expr_t, std::string"); - } - expr_series_t(const expr_series_t& other) - : context(other.context), exprs(other.exprs), - default_expr(other.default_expr), variable(other.variable) { - TRACE_CTOR(expr_series_t, "copy"); - } - virtual ~expr_series_t() { - TRACE_DTOR(expr_series_t); - } - - scope_t * get_context() { - return context; - } - void set_context(scope_t * scope) { - context = scope; - } - - bool empty() const { - return ! exprs || exprs->empty(); - } - - void push_back(const expr_t& expr) { - if (! exprs) - exprs = std::list(); - exprs->push_back(expr); - } - void pop_back() { - assert(exprs); - exprs->pop_back(); - } - - void mark_uncompiled() { - if (exprs) - foreach (expr_t& expr, *exprs) - expr.mark_uncompiled(); - else - default_expr.mark_uncompiled(); - } - - void compile(scope_t& scope) { - if (exprs) - foreach (expr_t& expr, *exprs) - expr.compile(scope); - else - default_expr.compile(scope); - } - - value_t calc(scope_t& scope) { - if (exprs) { - value_t result; - symbol_scope_t sym_scope(scope); - std::size_t len(exprs->size()); - - foreach (expr_t& expr, *exprs) { - result = expr.calc(sym_scope); - if (--len > 0) - sym_scope.define(symbol_t::FUNCTION, variable, - expr_t::op_t::wrap_value(result)); - } - return result; - } else { - return default_expr.calc(scope); - } - } -}; - -} // namespace ledger - -#endif // _SERIES_H diff --git a/tools/Makefile.am b/tools/Makefile.am index a598966a..6c01936a 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -114,7 +114,6 @@ pkginclude_HEADERS = \ src/query.h \ src/format.h \ src/option.h \ - src/series.h \ \ src/item.h \ src/post.h \ -- cgit v1.2.3 From f525dabf391903936da36a34d8f3045dc3b58efd Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 11:31:02 -0600 Subject: Removed unnecessary method definition --- src/scope.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index 1b2566d5..84dfd3ea 100644 --- a/src/scope.h +++ b/src/scope.h @@ -404,10 +404,6 @@ public: TRACE_DTOR(call_scope_t); } - virtual string description() { - return context_scope_t::description(); - } - void set_args(const value_t& _args) { args = _args; } -- cgit v1.2.3 From 28b3453f5036c2352904c3eafd745d5b9c35c219 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 11:31:08 -0600 Subject: Added a DEBUG statement --- src/scope.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index 84dfd3ea..75dc2c0f 100644 --- a/src/scope.h +++ b/src/scope.h @@ -241,6 +241,8 @@ private: template T * search_scope(scope_t * ptr, bool prefer_direct_parents = false) { + DEBUG("scope.search", "Searching scope " << ptr->description()); + if (T * sought = dynamic_cast(ptr)) return sought; -- cgit v1.2.3 From 77292ac3cda2f1052559991b9d53f1ad7c1fec9a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 11:31:15 -0600 Subject: Whitespace fix --- src/expr.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/expr.h b/src/expr.h index 5dc6430a..cad2e90b 100644 --- a/src/expr.h +++ b/src/expr.h @@ -194,6 +194,7 @@ public: string term; string base_expr; string merge_operator; + std::list exprs; merged_expr_t(const string& _term, const string& expr, -- cgit v1.2.3 From 7e250696e02e0392bc865f66570da296ced124ab Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 12:46:46 -0600 Subject: Many options now have additive effect For example, -A and -V used to override each other, whereas now: -A report the average amount -V report all amounts at current value -AV report the current value of the average -VA report the average of all current values --- src/chain.cc | 4 ++ src/filters.h | 12 +++--- src/global.cc | 1 + src/global.h | 1 + src/op.cc | 17 +++++---- src/report.h | 82 ++++++++++++++++++---------------------- src/scope.cc | 3 +- src/scope.h | 5 ++- test/baseline/opt-deviation.test | 2 +- test/baseline/opt-unround.test | 2 +- 10 files changed, 65 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/chain.cc b/src/chain.cc index 400b8f26..f8f0aeff 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -118,6 +118,10 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, expr_t& expr(report.HANDLER(amount_).expr); expr.set_context(&report); + report.HANDLER(total_).expr.set_context(&report); + report.HANDLER(display_amount_).expr.set_context(&report); + report.HANDLER(display_total_).expr.set_context(&report); + if (! for_accounts_report) { // Make sure only forecast postings which match are allowed through if (report.HANDLED(forecast_while_)) { diff --git a/src/filters.h b/src/filters.h index 22f2d2cb..7be3acb9 100644 --- a/src/filters.h +++ b/src/filters.h @@ -65,14 +65,14 @@ protected: value_to_posts_map posts_map; post_handler_ptr post_chain; report_t& report; - expr_t group_by_expr; + expr_t& group_by_expr; custom_flusher_t preflush_func; optional postflush_func; public: post_splitter(post_handler_ptr _post_chain, report_t& _report, - expr_t _group_by_expr) + expr_t& _group_by_expr) : post_chain(_post_chain), report(_report), group_by_expr(_group_by_expr) { TRACE_CTOR(post_splitter, "scope_t&, post_handler_ptr, expr_t"); @@ -521,8 +521,8 @@ class display_filter_posts : public item_handler // later in the chain. report_t& report; - expr_t display_amount_expr; - expr_t display_total_expr; + expr_t& display_amount_expr; + expr_t& display_total_expr; bool show_rounding; value_t last_display_total; temporaries_t temps; @@ -569,8 +569,8 @@ class changed_value_posts : public item_handler // later in the chain. report_t& report; - expr_t total_expr; - expr_t display_total_expr; + expr_t& total_expr; + expr_t& display_total_expr; bool changed_values_only; bool for_accounts_report; bool show_unrealized; diff --git a/src/global.cc b/src/global.cc index 5b7bb1c1..cc14c99e 100644 --- a/src/global.cc +++ b/src/global.cc @@ -70,6 +70,7 @@ global_scope_t::global_scope_t(char ** envp) // generated. report_stack.push_front(new report_t(*session_ptr)); scope_t::default_scope = &report(); + scope_t::empty_scope = &empty_scope; // Read the user's options, in the following order: // diff --git a/src/global.h b/src/global.h index ce0534b0..7429b1f5 100644 --- a/src/global.h +++ b/src/global.h @@ -50,6 +50,7 @@ class global_scope_t : public noncopyable, public scope_t { shared_ptr session_ptr; ptr_list report_stack; + empty_scope_t empty_scope; public: global_scope_t(char ** envp); diff --git a/src/op.cc b/src/op.cc index a8b71468..a5db1690 100644 --- a/src/op.cc +++ b/src/op.cc @@ -89,8 +89,9 @@ namespace { expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, scope_t * param_scope) { - scope_t * scope_ptr = &scope; - expr_t::ptr_op_t result; + scope_t * scope_ptr = &scope; + unique_ptr bound_scope; + expr_t::ptr_op_t result; #if defined(DEBUG_ON) if (SHOW_DEBUG("expr.compile")) { @@ -129,9 +130,10 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, } } else if (is_scope()) { - shared_ptr subscope(new symbol_scope_t(scope)); + shared_ptr subscope(new symbol_scope_t(*scope_t::empty_scope)); set_scope(subscope); - scope_ptr = subscope.get(); + bound_scope.reset(new bind_scope_t(*scope_ptr, *subscope.get())); + scope_ptr = bound_scope.get(); } else if (kind < TERMINALS) { result = this; @@ -153,8 +155,8 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, node->set_left(left()->right()); node->set_right(right()); - empty_scope_t empty_scope; - symbol_scope_t params(param_scope ? *param_scope : empty_scope); + symbol_scope_t params(param_scope ? + *param_scope : *scope_t::empty_scope); for (ptr_op_t sym = node->left(); sym; sym = sym->has_right() ? sym->right() : NULL) { @@ -330,8 +332,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) call_scope_t& call_args(find_scope(scope, true)); std::size_t args_count(call_args.size()); std::size_t args_index(0); - empty_scope_t empty_scope; - symbol_scope_t args_scope(empty_scope); + symbol_scope_t args_scope(*scope_t::empty_scope); for (ptr_op_t sym = left(); sym; diff --git a/src/report.h b/src/report.h index 9541da43..f50bdc28 100644 --- a/src/report.h +++ b/src/report.h @@ -366,12 +366,9 @@ public: OPTION__ (report_t, amount_, // -t - expr_t expr; - CTOR(report_t, amount_) { - set_expr(none, "amount"); - } + DECL1(report_t, amount_, merged_expr_t, expr, ("amount_expr", "amount")) {} void set_expr(const optional& whence, const string& str) { - expr = str; + expr.append(str); on(whence, str); } DO_(args) { @@ -384,7 +381,7 @@ public: OPTION_(report_t, average, DO() { // -A parent->HANDLER(display_total_) - .set_expr(string("--average"), "count>0?(total_expr/count):0"); + .set_expr(string("--average"), "count>0?(display_total/count):0"); }); OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) { @@ -405,7 +402,7 @@ public: OPTION_(report_t, basis, DO() { // -B parent->HANDLER(revalued).on_only(string("--basis")); - parent->HANDLER(amount_).set_expr(string("--basis"), "rounded(cost)"); + parent->HANDLER(amount_).expr.set_base_expr("rounded(cost)"); }); OPTION_(report_t, begin_, DO_(args) { // -b @@ -438,20 +435,20 @@ public: OPTION__(report_t, budget_format_, CTOR(report_t, budget_format_) { on(none, - "%(justify(scrub(get_at(total_expr, 0)), 12, -1, true, color))" - " %(justify(-scrub(get_at(total_expr, 1)), 12, " + "%(justify(scrub(get_at(display_total, 0)), 12, -1, true, color))" + " %(justify(-scrub(get_at(display_total, 1)), 12, " " 12 + 1 + 12, true, color))" - " %(justify(scrub(get_at(total_expr, 1) + " - " get_at(total_expr, 0)), 12, " + " %(justify(scrub(get_at(display_total, 1) + " + " get_at(display_total, 0)), 12, " " 12 + 1 + 12 + 1 + 12, true, color))" " %(ansify_if(" - " justify((get_at(total_expr, 1) ? " - " (100% * scrub(get_at(total_expr, 0))) / " - " -scrub(get_at(total_expr, 1)) : 0), " + " justify((get_at(display_total, 1) ? " + " (100% * scrub(get_at(display_total, 0))) / " + " -scrub(get_at(display_total, 1)) : 0), " " 5, -1, true, false)," - " magenta if (color and get_at(total_expr, 1) and " - " (abs(quantity(scrub(get_at(total_expr, 0))) / " - " quantity(scrub(get_at(total_expr, 1)))) >= 1))))" + " magenta if (color and get_at(display_total, 1) and " + " (abs(quantity(scrub(get_at(display_total, 0))) / " + " quantity(scrub(get_at(display_total, 1)))) >= 1))))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n" "%/%$1 %$2 %$3 %$4\n%/" @@ -467,8 +464,8 @@ public: OPTION__(report_t, cleared_format_, CTOR(report_t, cleared_format_) { on(none, - "%(justify(scrub(get_at(total_expr, 0)), 16, 16 + prepend_width, " - " true, color)) %(justify(scrub(get_at(total_expr, 1)), 18, " + "%(justify(scrub(get_at(display_total, 0)), 16, 16 + prepend_width, " + " true, color)) %(justify(scrub(get_at(display_total, 1)), 18, " " 36 + prepend_width, true, color))" " %(latest_cleared ? format_date(latest_cleared) : \" \")" " %(!options.flat ? depth_spacer : \"\")" @@ -524,7 +521,7 @@ public: OPTION_(report_t, deviation, DO() { parent->HANDLER(display_total_) - .set_expr(string("--deviation"), "amount_expr-total_expr/count"); + .set_expr(string("--deviation"), "display_amount-display_total"); }); OPTION__ @@ -541,12 +538,10 @@ public: OPTION__ (report_t, display_amount_, - expr_t expr; - CTOR(report_t, display_amount_) { - set_expr(none, "amount_expr"); - } + DECL1(report_t, display_amount_, merged_expr_t, expr, + ("display_amount", "amount_expr")) {} void set_expr(const optional& whence, const string& str) { - expr = str; + expr.append(str); on(whence, str); } DO_(args) { @@ -555,12 +550,10 @@ public: OPTION__ (report_t, display_total_, - expr_t expr; - CTOR(report_t, display_total_) { - set_expr(none, "total_expr"); - } + DECL1(report_t, display_total_, merged_expr_t, expr, + ("display_total", "total_expr")) {} void set_expr(const optional& whence, const string& str) { - expr = str; + expr.append(str); on(whence, str); } DO_(args) { @@ -608,7 +601,10 @@ public: OPTION_(report_t, gain, DO() { // -G parent->HANDLER(revalued).on_only(string("--gain")); - parent->HANDLER(amount_).set_expr(string("--gain"), "(amount, cost)"); + + parent->HANDLER(amount_).expr.set_base_expr("(amount, cost)"); + parent->HANDLER(total_).expr.set_base_expr("total"); + // Since we are displaying the amounts of revalued postings, they // will end up being composite totals, and hence a pair of pairs. parent->HANDLER(display_amount_) @@ -676,10 +672,10 @@ public: parent->HANDLER(revalued).on_only(string("--market")); parent->HANDLER(display_amount_) .set_expr(string("--market"), - "market(amount_expr, value_date, exchange)"); + "market(display_amount, value_date, exchange)"); parent->HANDLER(display_total_) .set_expr(string("--market"), - "market(total_expr, value_date, exchange)"); + "market(display_total, value_date, exchange)"); }); OPTION(report_t, meta_); @@ -802,10 +798,7 @@ public: }); OPTION_(report_t, price, DO() { // -I - parent->HANDLER(display_amount_) - .set_expr(string("--price"), "price(amount_expr)"); - parent->HANDLER(display_total_) - .set_expr(string("--price"), "price(total_expr)"); + parent->HANDLER(amount_).expr.set_base_expr("price"); }); OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) { @@ -823,8 +816,8 @@ public: OPTION_(report_t, quantity, DO() { // -O parent->HANDLER(revalued).off(); - parent->HANDLER(amount_).set_expr(string("--quantity"), "amount"); - parent->HANDLER(total_).set_expr(string("--quantity"), "total"); + parent->HANDLER(amount_).expr.set_base_expr("amount"); + parent->HANDLER(total_).expr.set_base_expr("total"); }); OPTION_(report_t, quarterly, DO() { @@ -919,12 +912,9 @@ public: OPTION__ (report_t, total_, // -T - expr_t expr; - CTOR(report_t, total_) { - set_expr(none, "total"); - } + DECL1(report_t, total_, merged_expr_t, expr, ("total_expr", "total")) {} void set_expr(const optional& whence, const string& str) { - expr = str; + expr.append(str); on(whence, str); } DO_(args) { @@ -961,9 +951,9 @@ public: OPTION(report_t, unrealized_losses_); OPTION_(report_t, unround, DO() { - parent->HANDLER(display_amount_) + parent->HANDLER(amount_) .set_expr(string("--unround"), "unrounded(amount_expr)"); - parent->HANDLER(display_total_) + parent->HANDLER(total_) .set_expr(string("--unround"), "unrounded(total_expr)"); }); diff --git a/src/scope.cc b/src/scope.cc index 160a97c9..00327159 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -35,7 +35,8 @@ namespace ledger { -scope_t * scope_t::default_scope = NULL; +scope_t * scope_t::default_scope = NULL; +empty_scope_t * scope_t::empty_scope = NULL; void symbol_scope_t::define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) diff --git a/src/scope.h b/src/scope.h index 75dc2c0f..31ef61cb 100644 --- a/src/scope.h +++ b/src/scope.h @@ -99,10 +99,13 @@ private: #endif // HAVE_BOOST_SERIALIZATION }; +class empty_scope_t; + class scope_t { public: - static scope_t * default_scope; + static scope_t * default_scope; + static empty_scope_t * empty_scope; explicit scope_t() { TRACE_CTOR(scope_t, ""); diff --git a/test/baseline/opt-deviation.test b/test/baseline/opt-deviation.test index df216b9c..a677ff6e 100644 --- a/test/baseline/opt-deviation.test +++ b/test/baseline/opt-deviation.test @@ -190,7 +190,7 @@ Expenses:Books $120.00 Assets:Cash -test reg --deviation books +test reg -A --deviation books 08-Jan-01 January Expenses:Books $10.00 0 08-Jan-31 End of January Expenses:Books $10.00 0 08-Feb-01 February Expenses:Books $20.00 $6.67 diff --git a/test/baseline/opt-unround.test b/test/baseline/opt-unround.test index cef212ae..755bb62c 100644 --- a/test/baseline/opt-unround.test +++ b/test/baseline/opt-unround.test @@ -82,7 +82,7 @@ Expenses:Travel:Passport $127.00 Assets:Checking -test bal --unround --percent +test bal --percent --unround 100.00% Assets:Checking 100.00% Expenses:Travel 92.14958953% Airfare -- cgit v1.2.3 From e7de77d8dfc764fd3764dc45d397d5f8454414be Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 14:30:10 -0600 Subject: Added #if's for building optimized with Clang --- src/pyinterp.cc | 2 ++ src/query.h | 2 ++ src/times.h | 4 ++++ 3 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/pyinterp.cc b/src/pyinterp.cc index d1f46580..8d9c8c84 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -535,7 +535,9 @@ namespace { case value_t::ANY: // a pointer to an arbitrary object return object(val); } +#if !defined(__clang__) return object(); +#endif } } diff --git a/src/query.h b/src/query.h index 7286e89b..c694d099 100644 --- a/src/query.h +++ b/src/query.h @@ -186,7 +186,9 @@ public: assert(false); return ""; } +#if !defined(__clang__) return ""; +#endif } void unexpected(); diff --git a/src/times.h b/src/times.h index edc6d8b8..e3134665 100644 --- a/src/times.h +++ b/src/times.h @@ -218,7 +218,9 @@ struct date_duration_t case YEARS: return date + gregorian::years(length); } +#if !defined(__clang__) return date_t(); +#endif } date_t subtract(const date_t& date) const { @@ -234,7 +236,9 @@ struct date_duration_t case YEARS: return date - gregorian::years(length); } +#if !defined(__clang__) return date_t(); +#endif } string to_string() const { -- cgit v1.2.3 From ae4ef7a88ddd39ed544383e65d3c55ba97f4f8c1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 21:22:07 -0600 Subject: More work done on proper evaluation of lambdas --- src/op.cc | 42 +++++++++++++++++++++++++----------------- src/parser.cc | 4 +++- 2 files changed, 28 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index a5db1690..6a1a8f54 100644 --- a/src/op.cc +++ b/src/op.cc @@ -155,23 +155,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, node->set_left(left()->right()); node->set_right(right()); - symbol_scope_t params(param_scope ? - *param_scope : *scope_t::empty_scope); - for (ptr_op_t sym = node->left(); - sym; - sym = sym->has_right() ? sym->right() : NULL) { - ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; - if (! varname->is_ident()) { - throw_(calc_error, _("Invalid function definition")); - } else { - DEBUG("expr.compile", - "Defining function parameter " << varname->as_ident()); - params.define(symbol_t::FUNCTION, varname->as_ident(), - new op_t(op_t::PLUG)); - } - } - - node = node->compile(*scope_ptr, depth + 1, ¶ms); + node = node->compile(*scope_ptr, depth + 1, param_scope); DEBUG("expr.compile", "Defining " << left()->left()->as_ident() << " in " << scope_ptr); @@ -185,6 +169,30 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, } result = wrap_value(NULL_VALUE); } + else if (kind == O_LAMBDA) { + symbol_scope_t params(param_scope ? *param_scope : *scope_t::empty_scope); + + for (ptr_op_t sym = left(); + sym; + sym = sym->has_right() ? sym->right() : NULL) { + ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; + + if (! varname->is_ident()) { + throw_(calc_error, _("Invalid function or lambda parameter")); + } else { + DEBUG("expr.compile", + "Defining function parameter " << varname->as_ident()); + params.define(symbol_t::FUNCTION, varname->as_ident(), + new op_t(PLUG)); + } + } + + ptr_op_t rhs(right()->compile(*scope_ptr, depth + 1, ¶ms)); + if (rhs == right()) + result = this; + else + result = copy(left(), rhs); + } if (! result) { ptr_op_t lhs(left()->compile(*scope_ptr, depth + 1, param_scope)); diff --git a/src/parser.cc b/src/parser.cc index b3f50e41..ce70a49e 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -485,7 +485,9 @@ expr_t::parser_t::parse_lambda_expr(std::istream& in, ptr_op_t prev(node); node = new op_t(op_t::O_LAMBDA); node->set_left(prev); - node->set_right(parse_querycolon_expr(in, tflags)); + ptr_op_t scope(new op_t(op_t::SCOPE)); + scope->set_left(parse_querycolon_expr(in, tflags)); + node->set_right(scope); } else { push_token(tok); } -- cgit v1.2.3 From 4d011434003262c9a49b0b4636b2ac479a84e058 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 8 Mar 2012 00:44:27 -0600 Subject: Many improvements to lambdas and function calling --- src/op.cc | 340 ++++++++++++++++++++++++++---------------- src/op.h | 8 + src/parser.cc | 3 - test/baseline/cmd-script.test | 4 + 4 files changed, 223 insertions(+), 132 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index 6a1a8f54..0773c093 100644 --- a/src/op.cc +++ b/src/op.cc @@ -178,7 +178,10 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; if (! varname->is_ident()) { - throw_(calc_error, _("Invalid function or lambda parameter")); + std::ostringstream buf; + varname->dump(buf, 0); + throw_(calc_error, + _("Invalid function or lambda parameter: %1") << buf.str()); } else { DEBUG("expr.compile", "Defining function parameter " << varname->as_ident()); @@ -224,6 +227,23 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, return result; } +namespace { + expr_t::ptr_op_t lookup_ident(expr_t::ptr_op_t op, scope_t& scope) + { + expr_t::ptr_op_t def = op->left(); + + // If no definition was pre-compiled for this identifier, look it up + // in the current scope. + if (! def || def->kind == expr_t::op_t::PLUG) { + DEBUG("scope.symbols", "Looking for IDENT '" << op->as_ident() << "'"); + def = scope.lookup(symbol_t::FUNCTION, op->as_ident()); + } + if (! def) + throw_(calc_error, _("Unknown identifier '%1'") << op->as_ident()); + return def; + } +} + value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) { try { @@ -248,23 +268,14 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) result = NULL_VALUE; break; - case IDENT: { - ptr_op_t definition = left(); - // If no definition was pre-compiled for this identifier, look it up - // in the current scope. - if (! definition || definition->kind == PLUG) { - DEBUG("scope.symbols", "Looking for IDENT '" << as_ident() << "'"); - definition = scope.lookup(symbol_t::FUNCTION, as_ident()); + case IDENT: + if (ptr_op_t definition = lookup_ident(this, scope)) { + // Evaluating an identifier is the same as calling its definition + // directly + result = definition->calc(scope, locus, depth + 1); + check_type_context(scope, result); } - if (! definition) - throw_(calc_error, _("Unknown identifier '%1'") << as_ident()); - - // Evaluating an identifier is the same as calling its definition - // directly - result = definition->calc(scope, locus, depth + 1); - check_type_context(scope, result); break; - } case FUNCTION: { // Evaluating a FUNCTION is the same as calling it directly; this @@ -302,81 +313,14 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; } - case O_CALL: { - ptr_op_t func = left(); - string name; - - if (func->is_ident()) { - name = func->as_ident(); - func = func->left(); - if (! func) - func = scope.lookup(symbol_t::FUNCTION, name); - if (! func) - throw_(calc_error, _("Calling unknown function '%1'") << name); - } else { - name = ""; - } - - call_scope_t call_args(scope, locus, depth + 1); - if (has_right()) - call_args.set_args(split_cons_expr(right())); - - try { - if (func->is_function()) - result = func->as_function()(call_args); - else - result = func->calc(call_args, locus, depth + 1); - } - catch (const std::exception&) { - add_error_context(_("While calling function '%1':" << name)); - throw; - } - + case O_CALL: + result = calc_call(scope, locus, depth); check_type_context(scope, result); break; - } - - case O_LAMBDA: { - call_scope_t& call_args(find_scope(scope, true)); - std::size_t args_count(call_args.size()); - std::size_t args_index(0); - symbol_scope_t args_scope(*scope_t::empty_scope); - - for (ptr_op_t sym = left(); - sym; - sym = sym->has_right() ? sym->right() : NULL) { - ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; - if (! varname->is_ident()) { - throw_(calc_error, _("Invalid function definition")); - } - else if (args_index == args_count) { - DEBUG("expr.compile", "Defining function argument as null: " - << varname->as_ident()); - args_scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(NULL_VALUE)); - } - else { - DEBUG("expr.compile", "Defining function argument from call_args: " - << varname->as_ident()); - args_scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(call_args[args_index++])); - } - } - if (args_index < args_count) - throw_(calc_error, - _("Too few arguments in function call (saw %1, wanted %2)") - << args_count << args_index); - - if (right()->is_scope()) { - bind_scope_t outer_scope(scope, *right()->as_scope()); - bind_scope_t bound_scope(outer_scope, args_scope); - result = right()->left()->calc(bound_scope, locus, depth + 1); - } else { - result = right()->calc(args_scope, locus, depth + 1); - } + case O_LAMBDA: + result = expr_value(this); break; - } case O_MATCH: result = (right()->calc(scope, locus, depth + 1).as_mask() @@ -457,51 +401,12 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; case O_CONS: - result = left()->calc(scope, locus, depth + 1); - if (has_right()) { - value_t temp; - temp.push_back(result); - - ptr_op_t next = right(); - while (next) { - ptr_op_t value_op; - if (next->kind == O_CONS) { - value_op = next->left(); - next = next->has_right() ? next->right() : NULL; - } else { - value_op = next; - next = NULL; - } - temp.push_back(value_op->calc(scope, locus, depth + 1)); - } - result = temp; - } + result = calc_cons(scope, locus, depth); break; - case O_SEQ: { - // An O_SEQ is very similar to an O_CONS except that only the last - // result value in the series is kept. O_CONS builds up a list. - // - // Another feature of O_SEQ is that it pushes a new symbol scope - // onto the stack. We evaluate the left side here to catch any - // side-effects, such as definitions in the case of 'x = 1; x'. - result = left()->calc(scope, locus, depth + 1); - if (has_right()) { - ptr_op_t next = right(); - while (next) { - ptr_op_t value_op; - if (next->kind == O_SEQ) { - value_op = next->left(); - next = next->right(); - } else { - value_op = next; - next = NULL; - } - result = value_op->calc(scope, locus, depth + 1); - } - } + case O_SEQ: + result = calc_seq(scope, locus, depth); break; - } default: throw_(calc_error, _("Unexpected expr node '%1'") << op_context(this)); @@ -527,6 +432,183 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) } } +namespace { + expr_t::ptr_op_t find_definition(expr_t::ptr_op_t op, scope_t& scope, + expr_t::ptr_op_t * locus, const int depth, + int recursion_depth = 0) + { + // If the object we are apply call notation to is a FUNCTION value + // or a O_LAMBDA expression, then this is the object we want to + // call. + if (op->is_function() || op->kind == expr_t::op_t::O_LAMBDA) + return op; + + if (recursion_depth > 256) + throw_(value_error, _("Function recursion_depth too deep (> 256)")); + + // If it's an identifier, look up its definition and see if it's a + // function. + if (op->is_ident()) + return find_definition(lookup_ident(op, scope), scope, + locus, depth, recursion_depth + 1); + + // Value objects might be callable if they contain an expression. + if (op->is_value()) { + value_t def(op->as_value()); + if (is_expr(def)) + return find_definition(as_expr(def), scope, locus, depth, + recursion_depth + 1); + else + throw_(value_error, _("Cannot call %1 as a function") << def.label()); + } + + // Resolve ordinary expressions. + return find_definition(expr_t::op_t::wrap_value(op->calc(scope, locus, + depth + 1)), + scope, locus, depth + 1, recursion_depth + 1); + } + + value_t call_lambda(expr_t::ptr_op_t func, scope_t& scope, + call_scope_t& call_args, expr_t::ptr_op_t * locus, + const int depth) + { + std::size_t args_index(0); + std::size_t args_count(call_args.size()); + + symbol_scope_t args_scope(*scope_t::empty_scope); + + for (expr_t::ptr_op_t sym = func->left(); + sym; + sym = sym->has_right() ? sym->right() : NULL) { + expr_t::ptr_op_t varname = + sym->kind == expr_t::op_t::O_CONS ? sym->left() : sym; + if (! varname->is_ident()) { + throw_(calc_error, _("Invalid function definition")); + } + else if (args_index == args_count) { + DEBUG("expr.calc", "Defining function argument as null: " + << varname->as_ident()); + args_scope.define(symbol_t::FUNCTION, varname->as_ident(), + expr_t::op_t::wrap_value(NULL_VALUE)); + } + else { + DEBUG("expr.calc", "Defining function argument from call_args: " + << varname->as_ident()); + args_scope.define(symbol_t::FUNCTION, varname->as_ident(), + expr_t::op_t::wrap_value(call_args[args_index++])); + } + } + + if (args_index < args_count) + throw_(calc_error, + _("Too few arguments in function call (saw %1, wanted %2)") + << args_count << args_index); + + if (func->right()->is_scope()) { + bind_scope_t outer_scope(scope, *func->right()->as_scope()); + bind_scope_t bound_scope(outer_scope, args_scope); + + return func->right()->left()->calc(bound_scope, locus, depth + 1); + } else { + return func->right()->calc(args_scope, locus, depth + 1); + } + } +} + + +value_t expr_t::op_t::call(const value_t& args, scope_t& scope, + ptr_op_t * locus, const int depth) +{ + call_scope_t call_args(scope, locus, depth + 1); + call_args.set_args(args); + + if (is_function()) + return as_function()(call_args); + else if (kind == O_LAMBDA) + return call_lambda(this, scope, call_args, locus, depth); + else + return find_definition(this, scope, locus, depth) + ->calc(call_args, locus, depth); +} + +value_t expr_t::op_t::calc_call(scope_t& scope, ptr_op_t * locus, + const int depth) +{ + ptr_op_t func = left(); + string name = func->is_ident() ? func->as_ident() : ""; + + try { + func = find_definition(func, scope, locus, depth); + + call_scope_t call_args(scope, locus, depth + 1); + if (has_right()) + call_args.set_args(split_cons_expr(right())); + + if (func->is_function()) { + return func->as_function()(call_args); + } else { + assert(func->kind == O_LAMBDA); + return call_lambda(func, scope, call_args, locus, depth); + } + } + catch (const std::exception&) { + add_error_context(_("While calling function '%1':" << name)); + throw; + } +} + +value_t expr_t::op_t::calc_cons(scope_t& scope, ptr_op_t * locus, + const int depth) +{ + value_t result = left()->calc(scope, locus, depth + 1); + if (has_right()) { + value_t temp; + temp.push_back(result); + + ptr_op_t next = right(); + while (next) { + ptr_op_t value_op; + if (next->kind == O_CONS) { + value_op = next->left(); + next = next->has_right() ? next->right() : NULL; + } else { + value_op = next; + next = NULL; + } + temp.push_back(value_op->calc(scope, locus, depth + 1)); + } + result = temp; + } + return result; +} + +value_t expr_t::op_t::calc_seq(scope_t& scope, ptr_op_t * locus, + const int depth) +{ + // An O_SEQ is very similar to an O_CONS except that only the last + // result value in the series is kept. O_CONS builds up a list. + // + // Another feature of O_SEQ is that it pushes a new symbol scope onto + // the stack. We evaluate the left side here to catch any + // side-effects, such as definitions in the case of 'x = 1; x'. + value_t result = left()->calc(scope, locus, depth + 1); + if (has_right()) { + ptr_op_t next = right(); + while (next) { + ptr_op_t value_op; + if (next->kind == O_SEQ) { + value_op = next->left(); + next = next->right(); + } else { + value_op = next; + next = NULL; + } + result = value_op->calc(scope, locus, depth + 1); + } + } + return result; +} + namespace { bool print_cons(std::ostream& out, const expr_t::const_ptr_op_t op, const expr_t::op_t::context_t& context) diff --git a/src/op.h b/src/op.h index c93f218b..03fcf816 100644 --- a/src/op.h +++ b/src/op.h @@ -280,6 +280,9 @@ public: value_t calc(scope_t& scope, ptr_op_t * locus = NULL, const int depth = 0); + value_t call(const value_t& args, scope_t& scope, + ptr_op_t * locus = NULL, const int depth = 0); + struct context_t { ptr_op_t expr_op; @@ -307,6 +310,11 @@ public: static ptr_op_t wrap_functor(expr_t::func_t fobj); static ptr_op_t wrap_scope(shared_ptr sobj); +private: + value_t calc_call(scope_t& scope, ptr_op_t * locus, const int depth); + value_t calc_cons(scope_t& scope, ptr_op_t * locus, const int depth); + value_t calc_seq(scope_t& scope, ptr_op_t * locus, const int depth); + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ diff --git a/src/parser.cc b/src/parser.cc index ce70a49e..360ac93d 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -87,9 +87,6 @@ expr_t::parser_t::parse_call_expr(std::istream& in, node->set_left(prev); push_token(tok); // let the parser see the '(' again node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE))); - if (! node->right()) - throw_(parse_error, - _("%1 operator not followed by argument") << tok.symbol); } else { push_token(tok); break; diff --git a/test/baseline/cmd-script.test b/test/baseline/cmd-script.test index ce920ebb..b33dd82d 100644 --- a/test/baseline/cmd-script.test +++ b/test/baseline/cmd-script.test @@ -15,3 +15,7 @@ test eval 'foo = x, y, z -> print(x, y, z); foo(1, 2, 3)' 123 1 end test + +test eval 'foo(x,y)=y(1, 2, 3);foo(amount_expr, (s,d,t -> t))' +3 +end test -- cgit v1.2.3 From c86bff270712bcf5aaf296d2f867a856be8b6ff9 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 8 Mar 2012 00:44:48 -0600 Subject: Whitespace fix --- src/value.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/value.cc b/src/value.cc index de491e6c..9f69016d 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1266,8 +1266,8 @@ void value_t::in_place_cast(type_t cast_type) } add_error_context(_("While converting %1:") << *this); - throw_(value_error, _("Cannot convert %1 to %2") - << label() << label(cast_type)); + throw_(value_error, + _("Cannot convert %1 to %2") << label() << label(cast_type)); } void value_t::in_place_negate() -- cgit v1.2.3 From 17a84642fbb4684d5a8415371003f12ccb760d99 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 8 Mar 2012 00:54:19 -0600 Subject: Corrected calculation of market valuation expressions --- src/commodity.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index 0543c973..24d54695 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -97,14 +97,20 @@ commodity_t::find_price_from_expr(expr_t& expr, DEBUG("commodity.price.find", ""); } #endif - call_scope_t call_args(*scope_t::default_scope); + value_t result(expr.calc(*scope_t::default_scope)); - call_args.push_back(string_value(base_symbol())); - call_args.push_back(moment); - if (commodity) - call_args.push_back(string_value(commodity->symbol())); + if (is_expr(result)) { + value_t call_args; + + call_args.push_back(string_value(base_symbol())); + call_args.push_back(moment); + if (commodity) + call_args.push_back(string_value(commodity->symbol())); + + result = as_expr(result)->call(call_args, *scope_t::default_scope); + } - return price_point_t(moment, expr.calc(call_args).to_amount()); + return price_point_t(moment, result.to_amount()); } optional -- cgit v1.2.3 From 21e8b7f6f0a182f67899c5cd3689a5e646d8ed4a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 8 Mar 2012 00:55:06 -0600 Subject: Added nail_down() for pinning market value exprs --- src/commodity.cc | 9 +++++++++ src/commodity.h | 2 ++ src/report.cc | 20 ++++++++++++++++++++ src/report.h | 1 + 4 files changed, 32 insertions(+) (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index 24d54695..dd1b2743 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -217,6 +217,15 @@ commodity_t::check_for_updated_price(const optional& point, return point; } +commodity_t& commodity_t::nail_down(const expr_t& expr) +{ + annotation_t new_details; + new_details.value_expr = expr; + commodity_t * new_comm = + commodity_pool_t::current_pool->find_or_create(symbol(), new_details); + return *new_comm; +} + commodity_t::operator bool() const { return this != pool().null_commodity; diff --git a/src/commodity.h b/src/commodity.h index 3d36e35e..3f493f8b 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -291,6 +291,8 @@ public: const optional& moment, const optional& in_terms_of); + commodity_t& nail_down(const expr_t& expr); + // Methods related to parsing, reading, writing, etc., the commodity // itself. diff --git a/src/report.cc b/src/report.cc index 52e8c888..ba70d1d3 100644 --- a/src/report.cc +++ b/src/report.cc @@ -736,6 +736,24 @@ value_t report_t::fn_commodity(call_scope_t& args) return string_value(args.get(0).commodity().symbol()); } +value_t report_t::fn_nail_down(call_scope_t& args) +{ + value_t arg0(args[0]); + switch (arg0.type()) { + case value_t::AMOUNT: { + amount_t tmp(arg0.as_amount()); + if (tmp.has_commodity()) + tmp.set_commodity(tmp.commodity() + .nail_down(args[1].as_any())); + return tmp; + } + + default: + throw_(std::runtime_error, _("Attempting to nail down %1") + << args[0].label()); + } +} + value_t report_t::fn_lot_date(call_scope_t& args) { if (args[0].has_annotation()) { @@ -1261,6 +1279,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(fn_null); else if (is_eq(p, "now")) return MAKE_FUNCTOR(report_t::fn_now); + else if (is_eq(p, "nail_down")) + return MAKE_FUNCTOR(report_t::fn_nail_down); break; case 'o': diff --git a/src/report.h b/src/report.h index f50bdc28..515b14c2 100644 --- a/src/report.h +++ b/src/report.h @@ -170,6 +170,7 @@ public: value_t fn_ansify_if(call_scope_t& scope); value_t fn_percent(call_scope_t& scope); value_t fn_commodity(call_scope_t& scope); + value_t fn_nail_down(call_scope_t& scope); value_t fn_lot_date(call_scope_t& scope); value_t fn_lot_price(call_scope_t& scope); value_t fn_lot_tag(call_scope_t& scope); -- cgit v1.2.3 From 113fb0ee6a520dccdcd18a634f7ee73acd8ee488 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 8 Mar 2012 01:00:16 -0600 Subject: Added --historical option --- doc/ledger.1 | 3 +- src/report.cc | 3 +- src/report.h | 8 +++++ test/baseline/opt-historical.test | 61 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 test/baseline/opt-historical.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 4829034f..2fb074cb 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd February 28, 2012 +.Dd March 7, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -163,6 +163,7 @@ Show any gains (or losses) in commodity values over time. Only show the top .Ar number postings. +.It Fl \-historical Pq Fl H .It Fl \-invert Invert the value of amounts shown. .It Fl \-market Pq Fl V diff --git a/src/report.cc b/src/report.cc index ba70d1d3..26e6da0c 100644 --- a/src/report.cc +++ b/src/report.cc @@ -924,11 +924,9 @@ option_t * report_t::lookup_option(const char * p) case 'G': OPT_CH(gain); break; -#if 0 case 'H': OPT_CH(historical); break; -#endif case 'I': OPT_CH(price); break; @@ -1044,6 +1042,7 @@ option_t * report_t::lookup_option(const char * p) break; case 'h': OPT(head_); + else OPT(historical); break; case 'i': OPT(invert); diff --git a/src/report.h b/src/report.h index 515b14c2..d68d1f75 100644 --- a/src/report.h +++ b/src/report.h @@ -645,6 +645,14 @@ public: }); OPTION(report_t, head_); + + OPTION_(report_t, historical, DO() { // -H + parent->HANDLER(amount_) + .set_expr(string("--historical"), + "nail_down(amount_expr, (s,d,t -> market(s,value_date,t)))"); + }); + + OPTION(report_t, inject_); OPTION_(report_t, invert, DO() { diff --git a/test/baseline/opt-historical.test b/test/baseline/opt-historical.test new file mode 100644 index 00000000..1cd141ab --- /dev/null +++ b/test/baseline/opt-historical.test @@ -0,0 +1,61 @@ +2012-03-01 Broker + Assets:Stocks 10 APPL {$1} @ $1 + Equity + +2012-03-02 Broker + Assets:Stocks 10 APPL {$1} @ $2 + Equity + +2012-03-03 Broker + Assets:Stocks 10 APPL {$1} @ $3 + Equity + +2012-03-04 Broker + Assets:Stocks 10 APPL {$1} @ $4 + Equity + +2012-03-05 Broker + Assets:Stocks 10 APPL {$1} @ $5 + Equity + +test reg stocks -O +12-Mar-01 Broker Assets:Stocks 10 APPL 10 APPL +12-Mar-02 Broker Assets:Stocks 10 APPL 20 APPL +12-Mar-03 Broker Assets:Stocks 10 APPL 30 APPL +12-Mar-04 Broker Assets:Stocks 10 APPL 40 APPL +12-Mar-05 Broker Assets:Stocks 10 APPL 50 APPL +end test + +test reg stocks -B +12-Mar-01 Broker Assets:Stocks $10 $10 +12-Mar-02 Broker Assets:Stocks $20 $30 +12-Mar-03 Broker Assets:Stocks $30 $60 +12-Mar-04 Broker Assets:Stocks $40 $100 +12-Mar-05 Broker Assets:Stocks $50 $150 +end test + +test reg stocks -I +12-Mar-01 Broker Assets:Stocks $10 $10 +12-Mar-02 Broker Assets:Stocks $10 $20 +12-Mar-03 Broker Assets:Stocks $10 $30 +12-Mar-04 Broker Assets:Stocks $10 $40 +12-Mar-05 Broker Assets:Stocks $10 $50 +end test + +test reg stocks -O -H +end test + +test reg stocks -B -H +end test + +test reg stocks -I -H +end test + +test reg stocks -O -V +end test + +test reg stocks -B -V +end test + +test reg stocks -I -V +end test -- cgit v1.2.3 From 9f81c798ee0854cab038714670123bff6676a5d5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 8 Mar 2012 01:08:08 -0600 Subject: fn_market can now accept a commodity name for arg 0 --- src/report.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 26e6da0c..02fd7c18 100644 --- a/src/report.cc +++ b/src/report.cc @@ -517,11 +517,21 @@ value_t report_t::fn_market(call_scope_t& args) args.get(1) : optional()); value_t result; + value_t arg0 = args[0]; + + if (arg0.is_string()) { + amount_t tmp(1L); + commodity_t * commodity = + commodity_pool_t::current_pool->find_or_create(arg0.as_string()); + tmp.set_commodity(*commodity); + arg0 = tmp; + } + if (args.has(2)) - result = args[0].exchange_commodities(args.get(2), - /* add_prices= */ false, moment); + result = arg0.exchange_commodities(args.get(2), + /* add_prices= */ false, moment); else - result = args[0].value(moment); + result = arg0.value(moment); if (! result.is_null()) return result; -- cgit v1.2.3 From c9f7195936090ecbe2d5b4ec88c196660ae24f81 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 01:28:20 -0600 Subject: Added the notion of "virtual costs" --- src/annotate.cc | 7 ++++++- src/pool.cc | 1 + src/pool.h | 1 + src/post.h | 3 ++- src/py_commodity.cc | 7 ++++--- src/textual.cc | 11 +++++++++-- src/xact.cc | 2 +- 7 files changed, 24 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/annotate.cc b/src/annotate.cc index d2e4976e..1140bf0a 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -122,7 +122,12 @@ void annotation_t::parse(std::istream& in) else if (c == '(') { in.get(c); c = static_cast(in.peek()); - if (c == '(') { + if (c == '@') { + in.clear(); + in.seekg(pos, std::ios::beg); + break; + } + else if (c == '(') { if (value_expr) throw_(amount_error, _("Commodity specifies more than one valuation expresion")); diff --git a/src/pool.cc b/src/pool.cc index fd661fe1..1dd91e99 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -246,6 +246,7 @@ cost_breakdown_t commodity_pool_t::exchange(const amount_t& amount, const amount_t& cost, const bool is_per_unit, + const bool add_price, const optional& moment, const optional& tag) { diff --git a/src/pool.h b/src/pool.h index 7203bc20..b7921f59 100644 --- a/src/pool.h +++ b/src/pool.h @@ -119,6 +119,7 @@ public: cost_breakdown_t exchange(const amount_t& amount, const amount_t& cost, const bool is_per_unit = false, + const bool add_price = true, const optional& moment = none, const optional& tag = none); diff --git a/src/post.h b/src/post.h index 217f5509..7d93b1cc 100644 --- a/src/post.h +++ b/src/post.h @@ -58,7 +58,8 @@ public: #define POST_COST_CALCULATED 0x0080 // posting's cost was calculated #define POST_COST_IN_FULL 0x0100 // cost specified using @@ #define POST_COST_FIXATED 0x0200 // cost is fixed using = indicator -#define POST_ANONYMIZED 0x0400 // a temporary, anonymous posting +#define POST_COST_VIRTUAL 0x0400 // cost is virtualized: (@) +#define POST_ANONYMIZED 0x0800 // a temporary, anonymous posting xact_t * xact; // only set for posts of regular xacts account_t * account; diff --git a/src/py_commodity.cc b/src/py_commodity.cc index 25e5b918..c75b5e64 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -96,14 +96,15 @@ namespace { pool.exchange(commodity, per_unit_cost, moment); } - cost_breakdown_t py_exchange_5(commodity_pool_t& pool, + cost_breakdown_t py_exchange_7(commodity_pool_t& pool, const amount_t& amount, const amount_t& cost, const bool is_per_unit, + const bool add_prices, const boost::optional& moment, const boost::optional& tag) { - return pool.exchange(amount, cost, is_per_unit, moment, tag); + return pool.exchange(amount, cost, is_per_unit, add_prices, moment, tag); } commodity_t * py_pool_getitem(commodity_pool_t& pool, const string& symbol) @@ -280,7 +281,7 @@ void export_commodity() .def("exchange", py_exchange_2, with_custodian_and_ward<1, 2>()) .def("exchange", py_exchange_3, with_custodian_and_ward<1, 2>()) - .def("exchange", py_exchange_5) + .def("exchange", py_exchange_7) .def("parse_price_directive", &commodity_pool_t::parse_price_directive) .def("parse_price_expression", &commodity_pool_t::parse_price_expression, diff --git a/src/textual.cc b/src/textual.cc index 66972fb3..a8cb844b 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1447,12 +1447,16 @@ post_t * instance_t::parse_post(char * line, // Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST) - if (*next == '@') { + if (*next == '@' || (*next == '(' && *(next + 1) == '@')) { DEBUG("textual.parse", "line " << context.linenum << ": " << "Found a price indicator"); - bool per_unit = true; + if (*next == '(') { + post->add_flags(POST_COST_VIRTUAL); + ++next; + } + bool per_unit = true; if (*++next == '@') { per_unit = false; post->add_flags(POST_COST_IN_FULL); @@ -1460,6 +1464,9 @@ post_t * instance_t::parse_post(char * line, << "And it's for a total price"); } + if (post->has_flags(POST_COST_VIRTUAL) && *(next + 1) == ')') + ++next; + beg = static_cast(++next - line); p = skip_ws(next); diff --git a/src/xact.cc b/src/xact.cc index 4e43e680..4e8e56fa 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -269,7 +269,7 @@ bool xact_base_t::finalize() cost_breakdown_t breakdown = commodity_pool_t::current_pool->exchange - (post->amount, *post->cost, false, + (post->amount, *post->cost, false, ! post->has_flags(POST_COST_VIRTUAL), datetime_t(date(), time_duration(0, 0, 0, 0))); if (post->amount.has_annotation() && post->amount.annotation().price) { -- cgit v1.2.3 From 77484f1c17aacd29808b0b43a2b6599ff4526d3b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 01:28:34 -0600 Subject: Whitespace fix --- src/global.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/global.h b/src/global.h index 7429b1f5..392b03a9 100644 --- a/src/global.h +++ b/src/global.h @@ -141,7 +141,6 @@ See LICENSE file included with the distribution for details and disclaimer."); OPTION__ (global_scope_t, init_file_, // -i - CTOR(global_scope_t, init_file_) { if (const char * home_var = std::getenv("HOME")) on(none, (path(home_var) / ".ledgerrc").string()); -- cgit v1.2.3 From c62ceeef5a577731eb1921bd860e2c4c0b715eca Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 01:28:51 -0600 Subject: Added to_string() and operator+(const char *, string) --- src/utils.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/utils.h b/src/utils.h index b2298ce6..1fe2b378 100644 --- a/src/utils.h +++ b/src/utils.h @@ -278,6 +278,16 @@ extern string empty_string; strings_list split_arguments(const char * line); +inline string to_string(long num) { + std::ostringstream buf; + buf << num; + return buf.str(); +} + +inline string operator+(const char * left, const string& right) { + return string(left) + right; +} + } // namespace ledger /*@}*/ -- cgit v1.2.3 From b0cf90ab50ac42d3184d70cb1906778df034ddaa Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 01:29:11 -0600 Subject: Added int and str value expression functions --- src/session.cc | 19 +++++++++++++++++++ src/session.h | 2 ++ 2 files changed, 21 insertions(+) (limited to 'src') diff --git a/src/session.cc b/src/session.cc index 3e7cdb3d..7c546b82 100644 --- a/src/session.cc +++ b/src/session.cc @@ -268,6 +268,15 @@ value_t session_t::fn_max(call_scope_t& args) return args[1] > args[0] ? args[1] : args[0]; } +value_t session_t::fn_int(call_scope_t& args) +{ + return args[0].to_long(); +} +value_t session_t::fn_str(call_scope_t& args) +{ + return string_value(args[0].to_string()); +} + value_t session_t::fn_lot_price(call_scope_t& args) { amount_t amt(args.get(1, false)); @@ -360,6 +369,11 @@ expr_t::ptr_op_t session_t::lookup(const symbol_t::kind_t kind, return MAKE_FUNCTOR(session_t::fn_lot_tag); break; + case 'i': + if (is_eq(p, "int")) + return MAKE_FUNCTOR(session_t::fn_int); + break; + case 'm': if (is_eq(p, "min")) return MAKE_FUNCTOR(session_t::fn_min); @@ -367,6 +381,11 @@ expr_t::ptr_op_t session_t::lookup(const symbol_t::kind_t kind, return MAKE_FUNCTOR(session_t::fn_max); break; + case 's': + if (is_eq(p, "str")) + return MAKE_FUNCTOR(session_t::fn_str); + break; + default: break; } diff --git a/src/session.h b/src/session.h index cfeced39..b06c4a42 100644 --- a/src/session.h +++ b/src/session.h @@ -86,6 +86,8 @@ public: value_t fn_account(call_scope_t& scope); value_t fn_min(call_scope_t& scope); value_t fn_max(call_scope_t& scope); + value_t fn_int(call_scope_t& scope); + value_t fn_str(call_scope_t& scope); value_t fn_lot_price(call_scope_t& scope); value_t fn_lot_date(call_scope_t& scope); value_t fn_lot_tag(call_scope_t& scope); -- cgit v1.2.3 From 628875b33c4f1cd202091c9347ef0176f8b688fa Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 03:19:25 -0600 Subject: Use Boost.Tuple --- src/commodity.cc | 14 +++++++------- src/commodity.h | 8 +++----- src/system.hh.in | 5 ++++- 3 files changed, 14 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index dd1b2743..963fb646 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -129,16 +129,16 @@ commodity_t::find_price(const optional& commodity, if (target && *this == *target) return none; - optional pair = - base_t::time_and_commodity_t(base_t::optional_time_pair_t(moment, oldest), - commodity ? &(*commodity) : NULL); + optional + entry(base_t::memoized_price_entry(moment, oldest, + commodity ? &(*commodity) : NULL)); DEBUG("commodity.price.find", "looking for memoized args: " << (moment ? format_datetime(*moment) : "NONE") << ", " << (oldest ? format_datetime(*oldest) : "NONE") << ", " << (commodity ? commodity->symbol() : "NONE")); { - base_t::memoized_price_map::iterator i = base->price_map.find(*pair); + base_t::memoized_price_map::iterator i = base->price_map.find(*entry); if (i != base->price_map.end()) { DEBUG("commodity.price.find", "found! returning: " << ((*i).second ? (*i).second->price : amount_t(0L))); @@ -162,7 +162,7 @@ commodity_t::find_price(const optional& commodity, pool().commodity_price_history.find_price(*this, *target, when, oldest) : pool().commodity_price_history.find_price(*this, when, oldest); - if (pair) { + if (entry) { if (base->price_map.size() > base_t::max_price_map_size) { DEBUG("history.find", "price map has grown too large, clearing it by half"); @@ -172,9 +172,9 @@ commodity_t::find_price(const optional& commodity, DEBUG("history.find", "remembered: " << (point ? point->price : amount_t(0L))); - base->price_map.insert - (base_t::memoized_price_map::value_type(*pair, point)); + base->price_map.insert(base_t::memoized_price_map::value_type(*entry, point)); } + return point; } diff --git a/src/commodity.h b/src/commodity.h index 3f493f8b..1358966e 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -117,11 +117,9 @@ protected: optional larger; optional value_expr; - typedef std::pair, - optional > optional_time_pair_t; - typedef std::pair time_and_commodity_t; - typedef std::map, + optional, commodity_t *> memoized_price_entry; + typedef std::map > memoized_price_map; static const std::size_t max_price_map_size = 16; diff --git a/src/system.hh.in b/src/system.hh.in index 5e5a0c1d..fcc8e2ce 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -183,8 +183,11 @@ typedef std::ostream::pos_type ostream_pos_type; #include #else #include - #endif // HAVE_BOOST_REGEX_UNICODE + +#include +#include + #include #include -- cgit v1.2.3 From ca8f702a1b18f2f114cd580abe59f03fb85e4803 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 03:19:38 -0600 Subject: Added amount_t::commodity_ptr() --- src/amount.cc | 9 +++++++-- src/amount.h | 1 + test/regress/25A099C9.test | 12 ++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index c80ca14e..d1aa1655 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -867,10 +867,15 @@ bool amount_t::fits_in_long() const return mpfr_fits_slong_p(tempf, GMP_RNDN); } -commodity_t& amount_t::commodity() const +commodity_t * amount_t::commodity_ptr() const { return (has_commodity() ? - *commodity_ : *commodity_pool_t::current_pool->null_commodity); + commodity_ : commodity_pool_t::current_pool->null_commodity); +} + +commodity_t& amount_t::commodity() const +{ + return *commodity_ptr(); } bool amount_t::has_commodity() const diff --git a/src/amount.h b/src/amount.h index 09256b5d..7bf4fe51 100644 --- a/src/amount.h +++ b/src/amount.h @@ -533,6 +533,7 @@ public: number() returns a commodity-less version of an amount. This is useful for accessing just the numeric portion of an amount. */ + commodity_t * commodity_ptr() const; commodity_t& commodity() const; bool has_commodity() const; diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index 48b6814e..c43deb21 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -2,16 +2,16 @@ test -f $sourcepath/src/amount.h reg -> 7 __ERROR__ While parsing file "$sourcepath/src/amount.h", line 66: Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 731: +While parsing file "$sourcepath/src/amount.h", line 732: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 737: +While parsing file "$sourcepath/src/amount.h", line 738: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 743: +While parsing file "$sourcepath/src/amount.h", line 744: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 749: +While parsing file "$sourcepath/src/amount.h", line 750: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 755: +While parsing file "$sourcepath/src/amount.h", line 756: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 762: +While parsing file "$sourcepath/src/amount.h", line 763: Error: Invalid date/time: line std::istream& end test -- cgit v1.2.3 From ef478079e7836a9817992a8f8982b40ce97eef55 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 03:26:11 -0600 Subject: Defend against Dijkstra returning reverse paths --- src/history.cc | 49 ++++++++++++++++++++++++++++++++++++------------- src/history.h | 13 +++++++------ 2 files changed, 43 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/history.cc b/src/history.cc index 27ba42bd..7c49b343 100644 --- a/src/history.cc +++ b/src/history.cc @@ -204,11 +204,13 @@ commodity_history_t::find_price(const commodity_t& source, DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); DEBUG("history.find", "tv commodity = " << get(namemap, tv)->symbol()); - std::vector predecessors(num_vertices(fg)); - std::vector distances(num_vertices(fg)); - - PredecessorMap predecessorMap(&predecessors[0]); - DistanceMap distanceMap(&distances[0]); + std::size_t vector_len(num_vertices(fg)); + std::vector predecessors(vector_len); + std::vector distances(vector_len); + + FIndexMap indexMap(get(vertex_index, fg)); + FPredecessorMap predecessorMap(&predecessors[0], indexMap); + FDistanceMap distanceMap(&distances[0], indexMap); dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) @@ -217,10 +219,15 @@ commodity_history_t::find_price(const commodity_t& source, // Extract the shortest path and performance the calculations datetime_t least_recent = moment; - amount_t price; + amount_t price; const commodity_t * last_target = ⌖ + typedef tuple results_tuple; + std::vector results; + bool results_reversed = false; + vertex_descriptor v = tv; for (vertex_descriptor u = predecessorMap[v]; u != v; @@ -229,8 +236,24 @@ commodity_history_t::find_price(const commodity_t& source, std::pair edgePair = edge(u, v, fg); Graph::edge_descriptor edge = edgePair.first; + const commodity_t * u_comm = get(namemap, u); + const commodity_t * v_comm = get(namemap, v); const price_point_t& point(get(pricemap, edge)); + if (v == tv && u_comm != last_target && v_comm != last_target) + results_reversed = true; + + results.push_back(results_tuple(u_comm, v_comm, &point)); + } + + if (results_reversed) + std::reverse(results.begin(), results.end()); + + foreach (const results_tuple& edge, results) { + const commodity_t * u_comm = edge.get<0>(); + const commodity_t * v_comm = edge.get<1>(); + const price_point_t& point(*edge.get<2>()); + bool first_run = false; if (price.is_null()) { least_recent = point.when; @@ -240,8 +263,8 @@ commodity_history_t::find_price(const commodity_t& source, least_recent = point.when; } - DEBUG("history.find", "u commodity = " << get(namemap, u)->symbol()); - DEBUG("history.find", "v commodity = " << get(namemap, v)->symbol()); + DEBUG("history.find", "u commodity = " << u_comm->symbol()); + DEBUG("history.find", "v commodity = " << v_comm->symbol()); DEBUG("history.find", "last target = " << last_target->symbol()); // Determine which direction we are converting in @@ -250,12 +273,12 @@ commodity_history_t::find_price(const commodity_t& source, if (! first_run) { DEBUG("history.find", "price was = " << price.unrounded()); - if (pprice.commodity() != *last_target) + if (pprice.commodity_ptr() != last_target) price *= pprice.inverted(); else price *= pprice; } - else if (pprice.commodity() != *last_target) { + else if (pprice.commodity_ptr() != last_target) { price = pprice.inverted(); } else { @@ -263,10 +286,10 @@ commodity_history_t::find_price(const commodity_t& source, } DEBUG("history.find", "price is = " << price.unrounded()); - if (*last_target == *get(namemap, v)) - last_target = get(namemap, u); + if (last_target == v_comm) + last_target = u_comm; else - last_target = get(namemap, v); + last_target = v_comm; DEBUG("history.find", "last target now = " << last_target->symbol()); } diff --git a/src/history.h b/src/history.h index 16d552ab..63550ff5 100644 --- a/src/history.h +++ b/src/history.h @@ -156,13 +156,8 @@ public: typedef graph_traits::vertex_descriptor vertex_descriptor; typedef graph_traits::edge_descriptor edge_descriptor; - typedef property_map::type IndexMap; typedef property_map::type NameMap; - - typedef iterator_property_map PredecessorMap; - typedef iterator_property_map DistanceMap; + typedef property_map::type IndexMap; typedef property_map::type EdgeWeightMap; typedef property_map::type PricePointMap; @@ -175,7 +170,13 @@ public: typedef filtered_graph > FGraph; + typedef property_map::type FNameMap; + typedef property_map::type FIndexMap; + typedef iterator_property_map FPredecessorMap; + typedef iterator_property_map FDistanceMap; commodity_history_t() : indexmap(get(vertex_index, price_graph)), -- cgit v1.2.3 From 59f5ebe2dfe7cc93e36377f0251691e4de7b83b4 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 03:51:53 -0600 Subject: Reworked the way that options are handled --- doc/ledger.1 | 3 +- src/chain.cc | 11 +- src/draft.cc | 2 +- src/option.h | 77 ++--- src/print.cc | 17 +- src/pyinterp.h | 4 +- src/report.cc | 240 ++++++++------- src/report.h | 635 ++++++++++++++++++---------------------- src/session.h | 18 +- test/baseline/opt-no-pager.test | 0 10 files changed, 468 insertions(+), 539 deletions(-) create mode 100644 test/baseline/opt-no-pager.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 2fb074cb..a948d5a6 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd March 7, 2012 +.Dd March 9, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -359,6 +359,7 @@ See .It Fl \-meta-width Ar INT .It Fl \-monthly Pq Fl M .It Fl \-no-color +.It Fl \-no-pager .It Fl \-no-rounding .It Fl \-no-titles .It Fl \-no-total diff --git a/src/chain.cc b/src/chain.cc index f8f0aeff..44b3db82 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -88,10 +88,9 @@ post_handler_ptr chain_pre_post_handlers(post_handler_ptr base_handler, predicate_t(report.HANDLER(forecast_while_).str(), report.what_to_keep()), report, - report.HANDLED(forecast_years_) ? - static_cast - (report.HANDLER(forecast_years_).value.to_long()) : - 5UL); + (report.HANDLED(forecast_years_) ? + lexical_cast + (report.HANDLER(forecast_years_).value) : 5UL)); forecast_handler->add_period_xacts(report.session.journal->period_xacts); handler.reset(forecast_handler); @@ -137,9 +136,9 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, handler.reset (new truncate_xacts(handler, report.HANDLED(head_) ? - report.HANDLER(head_).value.to_int() : 0, + lexical_cast(report.HANDLER(head_).value) : 0, report.HANDLED(tail_) ? - report.HANDLER(tail_).value.to_int() : 0)); + lexical_cast(report.HANDLER(tail_).value) : 0)); // display_filter_posts adds virtual posts to the list to account // for changes in value of commodities, which otherwise would affect diff --git a/src/draft.cc b/src/draft.cc index 9abc769e..74a6f4d2 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -522,7 +522,7 @@ value_t xact_command(call_scope_t& args) xact_t * new_xact = draft.insert(*report.session.journal.get()); // Only consider actual postings for the "xact" command - report.HANDLER(limit_).on(string("#xact"), "actual"); + report.HANDLER(limit_).on("#xact", "actual"); if (new_xact) report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact); diff --git a/src/option.h b/src/option.h index 38431f9d..f892b00e 100644 --- a/src/option.h +++ b/src/option.h @@ -61,9 +61,9 @@ protected: option_t& operator=(const option_t&); public: - T * parent; - value_t value; - bool wants_arg; + T * parent; + string value; + bool wants_arg; option_t(const char * _name, const char _ch = '\0') : name(_name), name_len(std::strlen(name)), ch(_ch), @@ -94,7 +94,8 @@ public: out << std::right << desc(); if (wants_arg) { out << " = "; - value.print(out, 42); + out.width(42); + out << value; } else { out.width(45); out << ' '; @@ -123,43 +124,49 @@ public: return handled; } - string& str() { + string str() const { assert(handled); - if (! value) + if (value.empty()) throw_(std::runtime_error, _("No argument provided for %1") << desc()); - return value.as_string_lval(); + return value; } - string str() const { - assert(handled); - if (! value) - throw_(std::runtime_error, _("No argument provided for %1") << desc()); - return value.as_string(); + void on(const char * whence) { + on(string(whence)); } + void on(const optional& whence) { + handler_thunk(whence); - void on_only(const optional& whence) { handled = true; source = whence; } - void on(const optional& whence, const string& str) { - on_with(whence, string_value(str)); + + void on(const char * whence, const string& str) { + on(string(whence), str); } - virtual void on_with(const optional& whence, - const value_t& val) { + void on(const optional& whence, const string& str) { + string before = value; + + handler_thunk(whence, str); + + if (value == before) + value = str; + handled = true; - value = val; source = whence; } void off() { handled = false; - value = value_t(); + value = ""; source = none; } - virtual void handler_thunk(call_scope_t&) {} + virtual void handler_thunk(const optional& whence) {} + virtual void handler_thunk(const optional& whence, + const string& str) {} - virtual void handler(call_scope_t& args) { + value_t handler(call_scope_t& args) { if (wants_arg) { if (args.size() < 2) throw_(std::runtime_error, _("No argument provided for %1") << desc()); @@ -167,7 +174,7 @@ public: throw_(std::runtime_error, _("To many arguments provided for %1") << desc()); else if (! args[0].is_string()) throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); - on_with(args.get(0), args[1]); + on(args.get(0), args.get(1)); } else if (args.size() < 1) { throw_(std::runtime_error, _("No argument provided for %1") << desc()); @@ -176,27 +183,18 @@ public: throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); } else { - on_only(args.get(0)); + on(args.get(0)); } - - handler_thunk(args); - } - - virtual value_t handler_wrapper(call_scope_t& args) { - handler(args); return true; } virtual value_t operator()(call_scope_t& args) { if (! args.empty()) { args.push_front(string_value("?expr")); - return handler_wrapper(args); + return handler(args); } else if (wants_arg) { - if (handled) - return value; - else - return NULL_VALUE; + return string_value(value); } else { return handled; @@ -215,15 +213,16 @@ public: vartype var ; \ name ## option_t() : option_t(#name), var value -#define DO() virtual void handler_thunk(call_scope_t&) -#define DO_(var) virtual void handler_thunk(call_scope_t& var) +#define DO() virtual void handler_thunk(const optional& whence) +#define DO_(var) virtual void handler_thunk(const optional& whence, \ + const string& var) #define END(name) name ## handler #define COPY_OPT(name, other) name ## handler(other.name ## handler) #define MAKE_OPT_HANDLER(type, x) \ - expr_t::op_t::wrap_functor(bind(&option_t::handler_wrapper, x, _1)) + expr_t::op_t::wrap_functor(bind(&option_t::handler, x, _1)) #define MAKE_OPT_FUNCTOR(type, x) \ expr_t::op_t::wrap_functor(bind(&option_t::operator(), x, _1)) @@ -284,6 +283,10 @@ inline bool is_eq(const char * p, const char * n) { } \ END(name) +#define OTHER(name) \ + parent->HANDLER(name).parent = parent; \ + parent->HANDLER(name) + bool process_option(const string& whence, const string& name, scope_t& scope, const char * arg, const string& varname); diff --git a/src/print.cc b/src/print.cc index c544c4e0..9e52ce95 100644 --- a/src/print.cc +++ b/src/print.cc @@ -133,7 +133,7 @@ namespace { std::size_t columns = (report.HANDLED(columns_) ? - static_cast(report.HANDLER(columns_).value.to_long()) : 80); + lexical_cast(report.HANDLER(columns_).str()) : 80); if (xact.note) print_note(out, *xact.note, xact.has_flags(ITEM_NOTE_ON_NEXT_LINE), @@ -191,8 +191,8 @@ namespace { unistring name(pbuf.str()); std::size_t account_width = - (report.HANDLER(account_width_).specified ? - static_cast(report.HANDLER(account_width_).value.to_long()) : 36); + (report.HANDLED(account_width_) ? + lexical_cast(report.HANDLER(account_width_).str()) : 36); if (account_width < name.length()) account_width = name.length(); @@ -218,13 +218,14 @@ namespace { // first. } else { - int amount_width = - (report.HANDLER(amount_width_).specified ? - report.HANDLER(amount_width_).value.to_int() : 12); + std::size_t amount_width = + (report.HANDLED(amount_width_) ? + lexical_cast(report.HANDLER(amount_width_).str()) : + 12); std::ostringstream amt_str; - value_t(post->amount).print(amt_str, amount_width, -1, - AMOUNT_PRINT_RIGHT_JUSTIFY | + value_t(post->amount).print(amt_str, static_cast(amount_width), + -1, AMOUNT_PRINT_RIGHT_JUSTIFY | AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS); amt = amt_str.str(); } diff --git a/src/pyinterp.h b/src/pyinterp.h index 8699f69d..556b1563 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -136,8 +136,8 @@ public: virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); - OPTION_(python_interpreter_t, import_, DO_(args) { - parent->import_option(args.get(1)); + OPTION_(python_interpreter_t, import_, DO_(str) { + parent->import_option(str); }); }; diff --git a/src/report.cc b/src/report.cc index 02fd7c18..110d33ee 100644 --- a/src/report.cc +++ b/src/report.cc @@ -59,7 +59,7 @@ void report_t::normalize_options(const string& verb) #ifdef HAVE_ISATTY if (! HANDLED(force_color)) { if (! HANDLED(no_color) && isatty(STDOUT_FILENO)) - HANDLER(color).on_only(string("?normalize")); + HANDLER(color).on("?normalize"); if (HANDLED(color) && ! isatty(STDOUT_FILENO)) HANDLER(color).off(); } @@ -83,7 +83,7 @@ void report_t::normalize_options(const string& verb) if (session.HANDLED(price_exp_)) commodity_pool_t::current_pool->quote_leeway = - session.HANDLER(price_exp_).value.as_long(); + lexical_cast(session.HANDLER(price_exp_).value) * 3600L; if (session.HANDLED(price_db_)) commodity_pool_t::current_pool->price_db = session.HANDLER(price_db_).str(); @@ -106,39 +106,35 @@ void report_t::normalize_options(const string& verb) if (! HANDLED(meta_width_)) { string::size_type i = HANDLER(meta_).str().find(':'); if (i != string::npos) { - HANDLED(meta_width_).on_with - (string("?normalize"), - lexical_cast(string(HANDLER(meta_).str(), i + 1))); - HANDLED(meta_).on(string("?normalize"), + HANDLED(meta_width_).on("?normalize", + string(HANDLER(meta_).str(), i + 1)); + HANDLED(meta_).on("?normalize", string(HANDLER(meta_).str(), 0, i)); } } if (HANDLED(meta_width_)) { - HANDLER(prepend_format_).on - (string("?normalize"), - string("%(justify(truncated(tag(\"") + - HANDLER(meta_).str() + "\"), " + - HANDLED(meta_width_).value.to_string() + " - 1), " + - HANDLED(meta_width_).value.to_string() + "))"); - meta_width = HANDLED(meta_width_).value.to_long(); + HANDLER(prepend_format_) + .on("?normalize", string("%(justify(truncated(tag(\"") + + HANDLER(meta_).str() + "\"), " + + HANDLED(meta_width_).value + " - 1), " + + HANDLED(meta_width_).value + "))"); + meta_width = lexical_cast(HANDLED(meta_width_).value); } else { - HANDLER(prepend_format_).on(string("?normalize"), string("%(tag(\"") + - HANDLER(meta_).str() + "\"))"); + HANDLER(prepend_format_) + .on("?normalize", string("%(tag(\"") + HANDLER(meta_).str() + "\"))"); } } - if (! HANDLED(prepend_width_)) - HANDLER(prepend_width_).on_with(string("?normalize"), static_cast(0)); if (verb == "print" || verb == "xact" || verb == "dump") { - HANDLER(related).on_only(string("?normalize")); - HANDLER(related_all).on_only(string("?normalize")); + HANDLER(related_all).parent = this; + HANDLER(related_all).on("?normalize"); } else if (verb == "equity") { - HANDLER(equity).on_only(string("?normalize")); + HANDLER(equity).on("?normalize"); } if (verb[0] != 'b' && verb[0] != 'r') - HANDLER(base).on_only(string("?normalize")); + HANDLER(base).on("?normalize"); // If a time period was specified with -p, check whether it also gave a // begin and/or end to the report period (though these can be overridden @@ -152,12 +148,10 @@ void report_t::normalize_options(const string& verb) // to avoid option ordering issues were we to have done it during the // initial parsing of the options. if (HANDLED(amount_data)) { - HANDLER(format_) - .on_with(string("?normalize"), HANDLER(plot_amount_format_).value); + HANDLER(format_).on("?normalize", HANDLER(plot_amount_format_).value); } else if (HANDLED(total_data)) { - HANDLER(format_) - .on_with(string("?normalize"), HANDLER(plot_total_format_).value); + HANDLER(format_).on("?normalize", HANDLER(plot_total_format_).value); } // If the --exchange (-X) option was used, parse out any final price @@ -170,7 +164,7 @@ void report_t::normalize_options(const string& verb) long cols = 0; if (HANDLED(columns_)) - cols = HANDLER(columns_).value.to_long(); + cols = lexical_cast(HANDLER(columns_).value); else if (const char * columns = std::getenv("COLUMNS")) cols = lexical_cast(columns); else @@ -182,23 +176,20 @@ void report_t::normalize_options(const string& verb) if (cols > 0) { DEBUG("auto.columns", "cols = " << cols); - if (! HANDLER(date_width_).specified) - HANDLER(date_width_) - .on_with(none, static_cast(format_date(CURRENT_DATE(), - FMT_PRINTED).length())); - - long date_width = HANDLER(date_width_).value.to_long(); - long payee_width = (HANDLER(payee_width_).specified ? - HANDLER(payee_width_).value.to_long() : - int(double(cols) * 0.263157)); - long account_width = (HANDLER(account_width_).specified ? - HANDLER(account_width_).value.to_long() : - int(double(cols) * 0.302631)); - long amount_width = (HANDLER(amount_width_).specified ? - HANDLER(amount_width_).value.to_long() : - int(double(cols) * 0.157894)); - long total_width = (HANDLER(total_width_).specified ? - HANDLER(total_width_).value.to_long() : + long date_width = (HANDLED(date_width_) ? + lexical_cast(HANDLER(date_width_).str()) : + format_date(CURRENT_DATE(),FMT_PRINTED).length()); + long payee_width = (HANDLED(payee_width_) ? + lexical_cast(HANDLER(payee_width_).str()) : + long(double(cols) * 0.263157)); + long account_width = (HANDLED(account_width_) ? + lexical_cast(HANDLER(account_width_).str()) : + long(double(cols) * 0.302631)); + long amount_width = (HANDLED(amount_width_) ? + lexical_cast(HANDLER(amount_width_).str()) : + long(double(cols) * 0.157894)); + long total_width = (HANDLED(total_width_) ? + lexical_cast(HANDLER(total_width_).str()) : amount_width); DEBUG("auto.columns", "date_width = " << date_width); @@ -207,11 +198,11 @@ void report_t::normalize_options(const string& verb) DEBUG("auto.columns", "amount_width = " << amount_width); DEBUG("auto.columns", "total_width = " << total_width); - if (! HANDLER(date_width_).specified && - ! HANDLER(payee_width_).specified && - ! HANDLER(account_width_).specified && - ! HANDLER(amount_width_).specified && - ! HANDLER(total_width_).specified) { + if (! HANDLED(date_width_) && + ! HANDLED(payee_width_) && + ! HANDLED(account_width_) && + ! HANDLED(amount_width_) && + ! HANDLED(total_width_)) { long total = (4 /* the spaces between */ + date_width + payee_width + account_width + amount_width + total_width); if (total > cols) { @@ -222,17 +213,19 @@ void report_t::normalize_options(const string& verb) } if (! HANDLED(meta_width_)) - HANDLER(meta_width_).on_with(string("?normalize"), 0L); - if (! HANDLER(date_width_).specified) - HANDLER(date_width_).on_with(string("?normalize"), date_width); - if (! HANDLER(payee_width_).specified) - HANDLER(payee_width_).on_with(string("?normalize"), payee_width); - if (! HANDLER(account_width_).specified) - HANDLER(account_width_).on_with(string("?normalize"), account_width); - if (! HANDLER(amount_width_).specified) - HANDLER(amount_width_).on_with(string("?normalize"), amount_width); - if (! HANDLER(total_width_).specified) - HANDLER(total_width_).on_with(string("?normalize"), total_width); + HANDLER(meta_width_).value = "0"; + if (! HANDLED(prepend_width_)) + HANDLER(prepend_width_).value = "0"; + if (! HANDLED(date_width_)) + HANDLER(date_width_).value = to_string(date_width); + if (! HANDLED(payee_width_)) + HANDLER(payee_width_).value = to_string(payee_width); + if (! HANDLED(account_width_)) + HANDLER(account_width_).value = to_string(account_width); + if (! HANDLED(amount_width_)) + HANDLER(amount_width_).value = to_string(amount_width); + if (! HANDLED(total_width_)) + HANDLER(total_width_).value = to_string(total_width); } } @@ -255,7 +248,7 @@ void report_t::normalize_period() if (! interval.duration) HANDLER(period_).off(); else if (! HANDLED(sort_all_)) - HANDLER(sort_xacts_).on_only(string("?normalize")); + HANDLER(sort_xacts_).on("?normalize"); } void report_t::parse_query_args(const value_t& args, const string& whence) @@ -278,7 +271,7 @@ void report_t::parse_query_args(const value_t& args, const string& whence) } if (query.has_query(query_t::QUERY_BOLD)) { - HANDLER(bold_if_).set_expr(whence, query.get_query(query_t::QUERY_BOLD)); + HANDLER(bold_if_).on(whence, query.get_query(query_t::QUERY_BOLD)); DEBUG("report.predicate", "Bolding predicate = " << HANDLER(bold_if_).str()); } @@ -329,9 +322,9 @@ void report_t::generate_report(post_handler_ptr handler) generate_posts_iterator walker (session, HANDLED(seed_) ? - static_cast(HANDLER(seed_).value.to_long()) : 0, + lexical_cast(HANDLER(seed_).str()) : 0, HANDLED(head_) ? - static_cast(HANDLER(head_).value.to_long()) : 50); + lexical_cast(HANDLER(head_).str()) : 50); pass_down_posts(handler, walker); } @@ -527,16 +520,17 @@ value_t report_t::fn_market(call_scope_t& args) arg0 = tmp; } + string target_commodity; if (args.has(2)) - result = arg0.exchange_commodities(args.get(2), + target_commodity = args.get(2); + + if (! target_commodity.empty()) + result = arg0.exchange_commodities(target_commodity, /* add_prices= */ false, moment); else result = arg0.value(moment); - if (! result.is_null()) - return result; - - return args[0]; + return ! result.is_null() ? result : arg0; } value_t report_t::fn_get_at(call_scope_t& args) @@ -1245,7 +1239,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, else if (is_eq(p, "display_total")) return MAKE_FUNCTOR(report_t::fn_display_total); else if (is_eq(p, "date")) - return MAKE_FUNCTOR(report_t::fn_now); + return MAKE_FUNCTOR(report_t::fn_today); break; case 'f': @@ -1404,85 +1398,98 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return MAKE_OPT_HANDLER(report_t, handler); break; -#define POSTS_REPORT(formatter) \ +#define POSTS_REPORTER(formatter) \ WRAP_FUNCTOR(reporter<>(post_handler_ptr(formatter), *this, \ - string("#") + p)); + string("#") + p)) // Can't use WRAP_FUNCTOR here because the template arguments // confuse the parser -#define POSTS_REPORT_(method, formatter) \ - expr_t::op_t::wrap_functor \ - (reporter \ - (post_handler_ptr(formatter), *this, string("#") + p)); - -#define ACCOUNTS_REPORT(formatter) \ +#define POSTS_REPORTER_(method, formatter) \ + expr_t::op_t::wrap_functor \ + (reporter \ + (post_handler_ptr(formatter), *this, string("#") + p)) + +#define FORMATTED_POSTS_REPORTER(format) \ + POSTS_REPORTER \ + (new format_posts \ + (*this, report_format(HANDLER(format)), \ + maybe_format(HANDLER(prepend_format_)), \ + HANDLED(prepend_width_) ? \ + lexical_cast(HANDLER(prepend_width_).str()) : 0)) + +#define FORMATTED_COMMODITIES_REPORTER(format) \ + POSTS_REPORTER_ \ + (&report_t::commodities_report, \ + new format_posts \ + (*this, report_format(HANDLER(format)), \ + maybe_format(HANDLER(prepend_format_)), \ + HANDLED(prepend_width_) ? \ + lexical_cast(HANDLER(prepend_width_).str()) : 0)) + +#define ACCOUNTS_REPORTER(formatter) \ expr_t::op_t::wrap_functor(reporter \ (acct_handler_ptr(formatter), *this, \ - string("#") + p)); + string("#") + p)) + +#define FORMATTED_ACCOUNTS_REPORTER(format) \ + ACCOUNTS_REPORTER \ + (new format_accounts \ + (*this, report_format(HANDLER(format)), \ + maybe_format(HANDLER(prepend_format_)), \ + HANDLED(prepend_width_) ? \ + lexical_cast(HANDLER(prepend_width_).str()) : 0)) case symbol_t::COMMAND: switch (*p) { case 'a': if (is_eq(p, "accounts")) { - return POSTS_REPORT(new report_accounts(*this)); + return POSTS_REPORTER(new report_accounts(*this)); } break; case 'b': if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) { - return ACCOUNTS_REPORT(new format_accounts - (*this, report_format(HANDLER(balance_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_ACCOUNTS_REPORTER(balance_format_); } else if (is_eq(p, "budget")) { - HANDLER(amount_).set_expr(string("#budget"), "(amount, 0)"); + HANDLER(amount_).on(string("#budget"), "(amount, 0)"); budget_flags |= BUDGET_WRAP_VALUES; if (! (budget_flags & ~BUDGET_WRAP_VALUES)) budget_flags |= BUDGET_BUDGETED; - return ACCOUNTS_REPORT(new format_accounts - (*this, report_format(HANDLER(budget_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_ACCOUNTS_REPORTER(budget_format_); } break; case 'c': if (is_eq(p, "csv")) { - return POSTS_REPORT(new format_posts - (*this, report_format(HANDLER(csv_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_POSTS_REPORTER(csv_format_); } else if (is_eq(p, "cleared")) { - HANDLER(amount_).set_expr(string("#cleared"), - "(amount, cleared ? amount : 0)"); - return ACCOUNTS_REPORT(new format_accounts - (*this, report_format(HANDLER(cleared_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + HANDLER(amount_).on(string("#cleared"), + "(amount, cleared ? amount : 0)"); + return FORMATTED_ACCOUNTS_REPORTER(cleared_format_); } else if (is_eq(p, "convert")) { return WRAP_FUNCTOR(convert_command); } else if (is_eq(p, "commodities")) { - return POSTS_REPORT(new report_commodities(*this)); + return POSTS_REPORTER(new report_commodities(*this)); } break; case 'e': if (is_eq(p, "equity")) { - HANDLER(generated).on_only(string("#equity")); - return POSTS_REPORT(new print_xacts(*this)); + HANDLER(generated).on("#equity"); + return POSTS_REPORTER(new print_xacts(*this)); } else if (is_eq(p, "entry")) { return WRAP_FUNCTOR(xact_command); } else if (is_eq(p, "emacs")) { - return POSTS_REPORT(new format_emacs_posts(output_stream)); + return POSTS_REPORTER(new format_emacs_posts(output_stream)); } else if (is_eq(p, "echo")) { return MAKE_FUNCTOR(report_t::echo_command); @@ -1491,43 +1498,32 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'o': if (is_eq(p, "org")) { - return POSTS_REPORT(new posts_to_org_table + return POSTS_REPORTER(new posts_to_org_table (*this, maybe_format(HANDLER(prepend_format_)))); } break; case 'p': if (*(p + 1) == '\0' || is_eq(p, "print")) { - return POSTS_REPORT(new print_xacts(*this, HANDLED(raw))); + return POSTS_REPORTER(new print_xacts(*this, HANDLED(raw))); } else if (is_eq(p, "prices")) { - return POSTS_REPORT_(&report_t::commodities_report, - new format_posts - (*this, report_format(HANDLER(prices_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_COMMODITIES_REPORTER(prices_format_); } else if (is_eq(p, "pricedb")) { - return POSTS_REPORT_(&report_t::commodities_report, - new format_posts - (*this, report_format(HANDLER(pricedb_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_COMMODITIES_REPORTER(pricedb_format_); } else if (is_eq(p, "pricemap")) { return MAKE_FUNCTOR(report_t::pricemap_command); } else if (is_eq(p, "payees")) { - return POSTS_REPORT(new report_payees(*this)); + return POSTS_REPORTER(new report_payees(*this)); } break; case 'r': if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) { - return POSTS_REPORT(new format_posts - (*this, report_format(HANDLER(register_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_POSTS_REPORTER(register_format_); } else if (is_eq(p, "reload")) { return MAKE_FUNCTOR(report_t::reload_command); @@ -1545,7 +1541,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (is_eq(p, "xact")) return WRAP_FUNCTOR(xact_command); else if (is_eq(p, "xml")) - return POSTS_REPORT(new format_xml(*this)); + return POSTS_REPORTER(new format_xml(*this)); break; } break; @@ -1568,8 +1564,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, break; case 'g': if (is_eq(p, "generate")) - return POSTS_REPORT_(&report_t::generate_report, - new print_xacts(*this)); + return POSTS_REPORTER_(&report_t::generate_report, + new print_xacts(*this)); break; case 'p': if (is_eq(p, "parse")) diff --git a/src/report.h b/src/report.h index d68d1f75..2ad53f01 100644 --- a/src/report.h +++ b/src/report.h @@ -353,12 +353,16 @@ public: * Option handlers */ - OPTION__(report_t, abbrev_len_, - CTOR(report_t, abbrev_len_) { on_with(none, 2L); }); + OPTION__ + (report_t, abbrev_len_, + CTOR(report_t, abbrev_len_) { + on(none, "2"); + }); + OPTION(report_t, account_); OPTION_(report_t, actual, DO() { // -L - parent->HANDLER(limit_).on(string("--actual"), "actual"); + OTHER(limit_).on(whence, "actual"); }); OPTION_(report_t, add_budget, DO() { @@ -368,12 +372,8 @@ public: OPTION__ (report_t, amount_, // -t DECL1(report_t, amount_, merged_expr_t, expr, ("amount_expr", "amount")) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr.append(str); - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION(report_t, amount_data); // -j @@ -381,216 +381,204 @@ public: OPTION(report_t, auto_match); OPTION_(report_t, average, DO() { // -A - parent->HANDLER(display_total_) - .set_expr(string("--average"), "count>0?(display_total/count):0"); + OTHER(display_total_) + .on(whence, "count>0?(display_total/count):0"); }); - OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) { - on(none, - "%(ansify_if(" - " justify(scrub(display_total), 20, 20 + prepend_width, true, color)," - " bold if should_bold))" - " %(!options.flat ? depth_spacer : \"\")" - "%-(ansify_if(" - " ansify_if(partial_account(options.flat), blue if color)," - " bold if should_bold))\n%/" - "%$1\n%/" - "%(prepend_width ? \" \" * prepend_width : \"\")" - "--------------------\n"); - }); + OPTION__ + (report_t, balance_format_, + CTOR(report_t, balance_format_) { + on(none, + "%(ansify_if(" + " justify(scrub(display_total), 20," + " 20 + int(prepend_width), true, color)," + " bold if should_bold))" + " %(!options.flat ? depth_spacer : \"\")" + "%-(ansify_if(" + " ansify_if(partial_account(options.flat), blue if color)," + " bold if should_bold))\n%/" + "%$1\n%/" + "%(prepend_width ? \" \" * int(prepend_width) : \"\")" + "--------------------\n"); + }); OPTION(report_t, base); OPTION_(report_t, basis, DO() { // -B - parent->HANDLER(revalued).on_only(string("--basis")); - parent->HANDLER(amount_).expr.set_base_expr("rounded(cost)"); + OTHER(revalued).on(whence); + OTHER(amount_).expr.set_base_expr("rounded(cost)"); }); - OPTION_(report_t, begin_, DO_(args) { // -b - date_interval_t interval(args.get(1)); - optional begin = interval.begin(); - if (! begin) + OPTION_(report_t, begin_, DO_(str) { // -b + date_interval_t interval(str); + if (optional begin = interval.begin()) { + string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; + OTHER(limit_).on(whence, predicate); + } else { throw_(std::invalid_argument, - _("Could not determine beginning of period '%1'") - << args.get(1)); - - string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; - parent->HANDLER(limit_).on(string("--begin"), predicate); + _("Could not determine beginning of period '%1'") << str); + } }); - OPTION__ + OPTION_ (report_t, bold_if_, expr_t expr; - CTOR(report_t, bold_if_) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr = str; - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION_(report_t, budget, DO() { parent->budget_flags |= BUDGET_BUDGETED; }); - OPTION__(report_t, budget_format_, CTOR(report_t, budget_format_) { - on(none, - "%(justify(scrub(get_at(display_total, 0)), 12, -1, true, color))" - " %(justify(-scrub(get_at(display_total, 1)), 12, " - " 12 + 1 + 12, true, color))" - " %(justify(scrub(get_at(display_total, 1) + " - " get_at(display_total, 0)), 12, " - " 12 + 1 + 12 + 1 + 12, true, color))" - " %(ansify_if(" - " justify((get_at(display_total, 1) ? " - " (100% * scrub(get_at(display_total, 0))) / " - " -scrub(get_at(display_total, 1)) : 0), " - " 5, -1, true, false)," - " magenta if (color and get_at(display_total, 1) and " - " (abs(quantity(scrub(get_at(display_total, 0))) / " - " quantity(scrub(get_at(display_total, 1)))) >= 1))))" - " %(!options.flat ? depth_spacer : \"\")" - "%-(ansify_if(partial_account(options.flat), blue if color))\n" - "%/%$1 %$2 %$3 %$4\n%/" - "%(prepend_width ? \" \" * prepend_width : \"\")" - "------------ ------------ ------------ -----\n"); - }); + OPTION__ + (report_t, budget_format_, + CTOR(report_t, budget_format_) { + on(none, + "%(justify(scrub(get_at(display_total, 0)), 12, -1, true, color))" + " %(justify(-scrub(get_at(display_total, 1)), 12, " + " 12 + 1 + 12, true, color))" + " %(justify(scrub(get_at(display_total, 1) + " + " get_at(display_total, 0)), 12, " + " 12 + 1 + 12 + 1 + 12, true, color))" + " %(ansify_if(" + " justify((get_at(display_total, 1) ? " + " (100% * scrub(get_at(display_total, 0))) / " + " -scrub(get_at(display_total, 1)) : 0), " + " 5, -1, true, false)," + " magenta if (color and get_at(display_total, 1) and " + " (abs(quantity(scrub(get_at(display_total, 0))) / " + " quantity(scrub(get_at(display_total, 1)))) >= 1))))" + " %(!options.flat ? depth_spacer : \"\")" + "%-(ansify_if(partial_account(options.flat), blue if color))\n" + "%/%$1 %$2 %$3 %$4\n%/" + "%(prepend_width ? \" \" * int(prepend_width) : \"\")" + "------------ ------------ ------------ -----\n"); + }); OPTION(report_t, by_payee); // -P OPTION_(report_t, cleared, DO() { // -C - parent->HANDLER(limit_).on(string("--cleared"), "cleared"); + OTHER(limit_).on(whence, "cleared"); }); - OPTION__(report_t, cleared_format_, CTOR(report_t, cleared_format_) { - on(none, - "%(justify(scrub(get_at(display_total, 0)), 16, 16 + prepend_width, " - " true, color)) %(justify(scrub(get_at(display_total, 1)), 18, " - " 36 + prepend_width, true, color))" - " %(latest_cleared ? format_date(latest_cleared) : \" \")" - " %(!options.flat ? depth_spacer : \"\")" - "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" - "%$1 %$2 %$3\n%/" - "%(prepend_width ? \" \" * prepend_width : \"\")" - "---------------- ---------------- ---------\n"); - }); + OPTION__ + (report_t, cleared_format_, + CTOR(report_t, cleared_format_) { + on(none, + "%(justify(scrub(get_at(display_total, 0)), 16, 16 + int(prepend_width), " + " true, color)) %(justify(scrub(get_at(display_total, 1)), 18, " + " 36 + int(prepend_width), true, color))" + " %(latest_cleared ? format_date(latest_cleared) : \" \")" + " %(!options.flat ? depth_spacer : \"\")" + "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" + "%$1 %$2 %$3\n%/" + "%(prepend_width ? \" \" * int(prepend_width) : \"\")" + "---------------- ---------------- ---------\n"); + }); OPTION(report_t, color); OPTION_(report_t, collapse, DO() { // -n // Make sure that balance reports are collapsed too, but only apply it // to account xacts - parent->HANDLER(display_).on(string("--collapse"), "post|depth<=1"); + OTHER(display_).on(whence, "post|depth<=1"); }); OPTION_(report_t, collapse_if_zero, DO() { - parent->HANDLER(collapse).on_only(string("--collapse-if-zero")); + OTHER(collapse).on(whence); }); OPTION(report_t, columns_); OPTION(report_t, count); - OPTION__(report_t, csv_format_, CTOR(report_t, csv_format_) { - on(none, - "%(quoted(date))," - "%(quoted(code))," - "%(quoted(payee))," - "%(quoted(display_account))," - "%(quoted(commodity))," - "%(quoted(quantity(scrub(display_amount))))," - "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," - "%(quoted(join(note | xact.note)))\n"); - }); + OPTION__ + (report_t, csv_format_, + CTOR(report_t, csv_format_) { + on(none, + "%(quoted(date))," + "%(quoted(code))," + "%(quoted(payee))," + "%(quoted(display_account))," + "%(quoted(commodity))," + "%(quoted(quantity(scrub(display_amount))))," + "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," + "%(quoted(join(note | xact.note)))\n"); + }); OPTION_(report_t, current, DO() { // -c - parent->HANDLER(limit_).on(string("--current"), "date<=today"); + OTHER(limit_).on(whence, "date<=today"); }); OPTION_(report_t, daily, DO() { // -D - parent->HANDLER(period_).on(string("--daily"), "daily"); + OTHER(period_).on(whence, "daily"); }); OPTION(report_t, date_); OPTION(report_t, date_format_); OPTION(report_t, datetime_format_); - OPTION_(report_t, depth_, DO_(args) { - parent->HANDLER(display_) - .on(string("--depth"), string("depth<=") + args.get(1)); + OPTION_(report_t, depth_, DO_(str) { + OTHER(display_).on(whence, string("depth<=") + str); }); OPTION_(report_t, deviation, DO() { - parent->HANDLER(display_total_) - .set_expr(string("--deviation"), "display_amount-display_total"); + OTHER(display_total_) + .on(whence, "display_amount-display_total"); }); - OPTION__ - (report_t, display_, // -d - CTOR(report_t, display_) {} - virtual void on_with(const optional& whence, const value_t& text) { - if (! handled) - option_t::on_with(whence, text); - else - option_t::on_with(whence, - string_value(string("(") + str() + ")&(" + - text.as_string() + ")")); + OPTION_ + (report_t, display_, + DO_(str) { // -d + if (handled) + value = string("(") + value + ")&(" + str + ")"; }); OPTION__ (report_t, display_amount_, DECL1(report_t, display_amount_, merged_expr_t, expr, ("display_amount", "amount_expr")) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr.append(str); - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION__ (report_t, display_total_, DECL1(report_t, display_total_, merged_expr_t, expr, ("display_total", "total_expr")) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr.append(str); - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION(report_t, dow); OPTION(report_t, aux_date); OPTION(report_t, empty); // -E - OPTION_(report_t, end_, DO_(args) { // -e - date_interval_t interval(args.get(1)); + OPTION_(report_t, end_, DO_(str) { // -e // Use begin() here so that if the user says --end=2008, we end on // 2008/01/01 instead of 2009/01/01 (which is what end() would // return). - optional end = interval.begin(); - if (! end) + date_interval_t interval(str); + if (optional end = interval.begin()) { + string predicate = "date<[" + to_iso_extended_string(*end) + "]"; + OTHER(limit_).on(whence, predicate); + + parent->terminus = datetime_t(*end); + } else { throw_(std::invalid_argument, _("Could not determine end of period '%1'") - << args.get(1)); - - string predicate = "date<[" + to_iso_extended_string(*end) + "]"; - parent->HANDLER(limit_).on(string("--end"), predicate); - - parent->terminus = datetime_t(*end); + << str); + } }); OPTION(report_t, equity); OPTION(report_t, exact); - OPTION_(report_t, exchange_, DO_(args) { // -X - on_with(args.get(0), args[1]); - call_scope_t no_args(*parent); - no_args.push_back(args[0]); - parent->HANDLER(market).parent = parent; - parent->HANDLER(market).handler(no_args); + OPTION_(report_t, exchange_, DO_() { // -X + // Using -X implies -V. The main difference is that now + // HANDLER(exchange_) contains the name of a commodity, which + // is accessed via the "exchange" value expression function. + OTHER(market).on(whence); }); OPTION(report_t, flat); @@ -601,74 +589,65 @@ public: OPTION(report_t, format_); // -F OPTION_(report_t, gain, DO() { // -G - parent->HANDLER(revalued).on_only(string("--gain")); + OTHER(revalued).on(whence); - parent->HANDLER(amount_).expr.set_base_expr("(amount, cost)"); - parent->HANDLER(total_).expr.set_base_expr("total"); + OTHER(amount_).expr.set_base_expr("(amount, cost)"); + OTHER(total_).expr.set_base_expr("total"); // Since we are displaying the amounts of revalued postings, they // will end up being composite totals, and hence a pair of pairs. - parent->HANDLER(display_amount_) - .set_expr(string("--gain"), - "use_direct_amount ? amount :" - " (is_seq(get_at(amount_expr, 0)) ?" - " get_at(get_at(amount_expr, 0), 0) :" - " market(get_at(amount_expr, 0), value_date, exchange)" - " - get_at(amount_expr, 1))"); - parent->HANDLER(revalued_total_) - .set_expr(string("--gain"), - "(market(get_at(total_expr, 0), value_date, exchange), " - "get_at(total_expr, 1))"); - parent->HANDLER(display_total_) - .set_expr(string("--gain"), - "use_direct_amount ? total_expr :" - " market(get_at(total_expr, 0), value_date, exchange)" - " - get_at(total_expr, 1)"); + OTHER(display_amount_) + .on(whence, + "use_direct_amount ? amount :" + " (is_seq(get_at(amount_expr, 0)) ?" + " get_at(get_at(amount_expr, 0), 0) :" + " market(get_at(amount_expr, 0), value_date, exchange)" + " - get_at(amount_expr, 1))"); + OTHER(revalued_total_) + .on(whence, + "(market(get_at(total_expr, 0), value_date, exchange), " + "get_at(total_expr, 1))"); + OTHER(display_total_) + .on(whence, + "use_direct_amount ? total_expr :" + " market(get_at(total_expr, 0), value_date, exchange)" + " - get_at(total_expr, 1)"); }); OPTION(report_t, generated); - OPTION__ + OPTION_ (report_t, group_by_, expr_t expr; - CTOR(report_t, group_by_) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr = str; - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); - OPTION__(report_t, group_title_format_, CTOR(report_t, group_title_format_) { - on(none, "%(value)\n"); - }); + OPTION__ + (report_t, group_title_format_, + CTOR(report_t, group_title_format_) { + on(none, "%(value)\n"); + }); OPTION(report_t, head_); OPTION_(report_t, historical, DO() { // -H - parent->HANDLER(amount_) - .set_expr(string("--historical"), - "nail_down(amount_expr, (s,d,t -> market(s,value_date,t)))"); + OTHER(amount_) + .on(whence, "nail_down(amount_expr, " + "market(amount_expr, value_date, exchange))"); }); - OPTION(report_t, inject_); OPTION_(report_t, invert, DO() { - parent->HANDLER(amount_).set_expr(string("--invert"), "-amount"); + OTHER(amount_).on(whence, "-amount"); }); - OPTION__ - (report_t, limit_, // -l - CTOR(report_t, limit_) {} - virtual void on_with(const optional& whence, const value_t& text) { - if (! handled) - option_t::on_with(whence, text); - else - option_t::on_with(whence, - string_value(string("(") + str() + ")&(" + - text.as_string() + ")")); + OPTION_ + (report_t, limit_, + DO_(str) { // -l + if (handled) + value = string("(") + value + ")&(" + str + ")"; }); OPTION(report_t, lot_dates); @@ -678,49 +657,44 @@ public: OPTION(report_t, lots_actual); OPTION_(report_t, market, DO() { // -V - parent->HANDLER(revalued).on_only(string("--market")); - parent->HANDLER(display_amount_) - .set_expr(string("--market"), - "market(display_amount, value_date, exchange)"); - parent->HANDLER(display_total_) - .set_expr(string("--market"), - "market(display_total, value_date, exchange)"); + OTHER(revalued).on(whence); + + OTHER(display_amount_) + .on(whence, "market(display_amount, value_date, exchange)"); + OTHER(display_total_) + .on(whence, "market(display_total, value_date, exchange)"); }); OPTION(report_t, meta_); OPTION_(report_t, monthly, DO() { // -M - parent->HANDLER(period_).on(string("--monthly"), "monthly"); + OTHER(period_).on(whence, "monthly"); }); OPTION_(report_t, no_color, DO() { - parent->HANDLER(color).off(); + OTHER(color).off(); }); OPTION(report_t, no_rounding); OPTION(report_t, no_titles); OPTION(report_t, no_total); - OPTION_(report_t, now_, DO_(args) { - date_interval_t interval(args.get(1)); - optional begin = interval.begin(); - if (! begin) + OPTION_(report_t, now_, DO_(str) { + date_interval_t interval(str); + if (optional begin = interval.begin()) { + ledger::epoch = parent->terminus = datetime_t(*begin); + } else { throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") - << args.get(1)); - ledger::epoch = parent->terminus = datetime_t(*begin); + << str); + } }); - OPTION__ + OPTION_ (report_t, only_, - CTOR(report_t, only_) {} - virtual void on_with(const optional& whence, const value_t& text) { - if (! handled) - option_t::on_with(whence, text); - else - option_t::on_with(whence, - string_value(string("(") + str() + ")&(" + - text.as_string() + ")")); + DO_(str) { + if (handled) + value = string("(") + value + ")&(" + str + ")"; }); OPTION(report_t, output_); // -o @@ -741,178 +715,162 @@ public: setenv("LESS", "-FRSX", 0); // don't overwrite } } - } - virtual void on_with(const optional& whence, const value_t& text) { - string cmd(text.to_string()); - if (cmd == "" || cmd == "false" || cmd == "off" || - cmd == "none" || cmd == "no" || cmd == "disable") - option_t::off(); - else - option_t::on_with(whence, text); }); #else // HAVE_ISATTY - OPTION__ - (report_t, pager_, - CTOR(report_t, pager_) { - } - virtual void on_with(const optional& whence, const value_t& text) { - string cmd(text.to_string()); - if (cmd == "" || cmd == "false" || cmd == "off" || - cmd == "none" || cmd == "no" || cmd == "disable") - option_t::off(); - else - option_t::on_with(whence, text); - }); + OPTION(report_t, pager_); #endif // HAVE_ISATTY + OPTION_(report_t, no_pager, DO() { + OTHER(pager_).off(); + }); + OPTION(report_t, payee_); OPTION_(report_t, pending, DO() { // -C - parent->HANDLER(limit_).on(string("--pending"), "pending"); + OTHER(limit_).on(whence, "pending"); }); OPTION_(report_t, percent, DO() { // -% - parent->HANDLER(total_) - .set_expr(string("--percent"), - "((is_account&parent&parent.total)?" - " percent(scrub(total), scrub(parent.total)):0)"); + OTHER(total_) + .on(whence, + "((is_account&parent&parent.total)?" + " percent(scrub(total), scrub(parent.total)):0)"); }); - OPTION__ - (report_t, period_, // -p - CTOR(report_t, period_) {} - virtual void on_with(const optional& whence, const value_t& text) { - if (! handled) - option_t::on_with(whence, text); - else - option_t::on_with(whence, - string_value(text.as_string() + " " + str())); + OPTION_ + (report_t, period_, + DO_(str) { // -p + if (handled) + value += string(" ") + str; }); OPTION(report_t, pivot_); - OPTION__(report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) { - on(none, - "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n"); - }); + OPTION__ + (report_t, plot_amount_format_, + CTOR(report_t, plot_amount_format_) { + on(none, + "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n"); + }); - OPTION__(report_t, plot_total_format_, CTOR(report_t, plot_total_format_) { - on(none, - "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n"); - }); + OPTION__ + (report_t, plot_total_format_, + CTOR(report_t, plot_total_format_) { + on(none, + "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n"); + }); OPTION(report_t, prepend_format_); - OPTION_(report_t, prepend_width_, DO_(args) { - value = args.get(1); - }); + OPTION(report_t, prepend_width_); OPTION_(report_t, price, DO() { // -I - parent->HANDLER(amount_).expr.set_base_expr("price"); + OTHER(amount_).expr.set_base_expr("price"); }); - OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) { - on(none, - "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, " - " 2 + 9 + 8 + 12, true, color))\n"); - }); + OPTION__ + (report_t, prices_format_, + CTOR(report_t, prices_format_) { + on(none, + "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, " + " 2 + 9 + 8 + 12, true, color))\n"); + }); - OPTION__(report_t, pricedb_format_, CTOR(report_t, pricedb_format_) { - on(none, - "P %(datetime) %(display_account) %(scrub(display_amount))\n"); - }); + OPTION__ + (report_t, pricedb_format_, + CTOR(report_t, pricedb_format_) { + on(none, + "P %(datetime) %(display_account) %(scrub(display_amount))\n"); + }); OPTION(report_t, primary_date); OPTION_(report_t, quantity, DO() { // -O - parent->HANDLER(revalued).off(); - parent->HANDLER(amount_).expr.set_base_expr("amount"); - parent->HANDLER(total_).expr.set_base_expr("total"); + OTHER(revalued).off(); + + OTHER(amount_).expr.set_base_expr("amount"); + OTHER(total_).expr.set_base_expr("total"); }); OPTION_(report_t, quarterly, DO() { - parent->HANDLER(period_).on(string("--quarterly"), "quarterly"); + OTHER(period_).on(whence, "quarterly"); }); OPTION(report_t, raw); OPTION_(report_t, real, DO() { // -R - parent->HANDLER(limit_).on(string("--real"), "real"); + OTHER(limit_).on(whence, "real"); }); - OPTION__(report_t, register_format_, CTOR(report_t, register_format_) { - on(none, - "%(ansify_if(" - " ansify_if(justify(format_date(date), date_width)," - " green if color and date > today)," - " bold if should_bold))" - " %(ansify_if(" - " ansify_if(justify(truncated(payee, payee_width), payee_width), " - " bold if color and !cleared and actual)," - " bold if should_bold))" - " %(ansify_if(" - " ansify_if(justify(truncated(display_account, account_width, " - " abbrev_len), account_width)," - " blue if color)," - " bold if should_bold))" - " %(ansify_if(" - " justify(scrub(display_amount), amount_width, " - " 3 + meta_width + date_width + payee_width" - " + account_width + amount_width + prepend_width," - " true, color)," - " bold if should_bold))" - " %(ansify_if(" - " justify(scrub(display_total), total_width, " - " 4 + meta_width + date_width + payee_width" - " + account_width + amount_width + total_width" - " + prepend_width, true, color)," - " bold if should_bold))\n%/" - "%(justify(\" \", date_width))" - " %(ansify_if(" - " justify(truncated(has_tag(\"Payee\") ? payee : \" \", " - " payee_width), payee_width)," - " bold if should_bold))" - " %$3 %$4 %$5\n"); - }); + OPTION__ + (report_t, register_format_, + CTOR(report_t, register_format_) { + on(none, + "%(ansify_if(" + " ansify_if(justify(format_date(date), int(date_width))," + " green if color and date > today)," + " bold if should_bold))" + " %(ansify_if(" + " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), " + " bold if color and !cleared and actual)," + " bold if should_bold))" + " %(ansify_if(" + " ansify_if(justify(truncated(display_account, int(account_width), " + " abbrev_len), int(account_width))," + " blue if color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(display_amount), int(amount_width), " + " 3 + int(meta_width) + int(date_width) + int(payee_width)" + " + int(account_width) + int(amount_width) + int(prepend_width)," + " true, color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(display_total), int(total_width), " + " 4 + int(meta_width) + int(date_width) + int(payee_width)" + " + int(account_width) + int(amount_width) + int(total_width)" + " + int(prepend_width), true, color)," + " bold if should_bold))\n%/" + "%(justify(\" \", int(date_width)))" + " %(ansify_if(" + " justify(truncated(has_tag(\"Payee\") ? payee : \" \", " + " int(payee_width)), int(payee_width))," + " bold if should_bold))" + " %$3 %$4 %$5\n"); + }); OPTION(report_t, related); // -r OPTION_(report_t, related_all, DO() { - parent->HANDLER(related).on_only(string("--related-all")); + OTHER(related).on(whence); }); OPTION(report_t, revalued); OPTION(report_t, revalued_only); - OPTION__ + OPTION_ (report_t, revalued_total_, expr_t expr; - CTOR(report_t, revalued_total_) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr = str; - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION(report_t, rich_data); OPTION(report_t, seed_); - OPTION_(report_t, sort_, DO_(args) { // -S - on_with(args.get(0), args[1]); - parent->HANDLER(sort_xacts_).off(); - parent->HANDLER(sort_all_).off(); + OPTION_(report_t, sort_, DO_(str) { // -S + OTHER(sort_xacts_).off(); + OTHER(sort_all_).off(); }); - OPTION_(report_t, sort_all_, DO_(args) { - parent->HANDLER(sort_).on_with(string("--sort-all"), args[1]); - parent->HANDLER(sort_xacts_).off(); + OPTION_(report_t, sort_all_, DO_(str) { + OTHER(sort_).on(whence, str); + OTHER(sort_xacts_).off(); }); - OPTION_(report_t, sort_xacts_, DO_(args) { - parent->HANDLER(sort_).on_with(string("--sort-xacts"), args[1]); - parent->HANDLER(sort_all_).off(); + OPTION_(report_t, sort_xacts_, DO_(str) { + OTHER(sort_).on(whence, str); + OTHER(sort_all_).off(); }); OPTION(report_t, start_of_week_); @@ -922,18 +880,13 @@ public: OPTION__ (report_t, total_, // -T DECL1(report_t, total_, merged_expr_t, expr, ("total_expr", "total")) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr.append(str); - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION(report_t, total_data); // -J - OPTION_(report_t, truncate_, DO_(args) { - string style(args.get(1)); + OPTION_(report_t, truncate_, DO_(style) { if (style == "leading") format_t::default_style = format_t::TRUNCATE_LEADING; else if (style == "middle") @@ -951,7 +904,7 @@ public: }); OPTION_(report_t, uncleared, DO() { // -U - parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending"); + OTHER(limit_).on(whence, "uncleared|pending"); }); OPTION(report_t, unrealized); @@ -960,48 +913,28 @@ public: OPTION(report_t, unrealized_losses_); OPTION_(report_t, unround, DO() { - parent->HANDLER(amount_) - .set_expr(string("--unround"), "unrounded(amount_expr)"); - parent->HANDLER(total_) - .set_expr(string("--unround"), "unrounded(total_expr)"); + OTHER(amount_).on(whence, "unrounded(amount_expr)"); + OTHER(total_).on(whence, "unrounded(total_expr)"); }); OPTION_(report_t, weekly, DO() { // -W - parent->HANDLER(period_).on(string("--weekly"), "weekly"); + OTHER(period_).on(whence, "weekly"); }); OPTION_(report_t, wide, DO() { // -w - parent->HANDLER(columns_).on_with(string("--wide"), 132L); + OTHER(columns_).on(whence, "132"); }); OPTION_(report_t, yearly, DO() { // -Y - parent->HANDLER(period_).on(string("--yearly"), "yearly"); + OTHER(period_).on(whence, "yearly"); }); - OPTION__(report_t, meta_width_, - bool specified; - CTOR(report_t, meta_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, date_width_, - bool specified; - CTOR(report_t, date_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, payee_width_, - bool specified; - CTOR(report_t, payee_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, account_width_, - bool specified; - CTOR(report_t, account_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, amount_width_, - bool specified; - CTOR(report_t, amount_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, total_width_, - bool specified; - CTOR(report_t, total_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); + OPTION(report_t, meta_width_); + OPTION(report_t, date_width_); + OPTION(report_t, payee_width_); + OPTION(report_t, account_width_); + OPTION(report_t, amount_width_); + OPTION(report_t, total_width_); }; diff --git a/src/session.h b/src/session.h index b06c4a42..cb981346 100644 --- a/src/session.h +++ b/src/session.h @@ -128,28 +128,24 @@ public: OPTION__ (session_t, price_exp_, // -Z - CTOR(session_t, price_exp_) { value = 24L * 3600L; } - DO_(args) { - value = args.get(1) * 60L; - }); + CTOR(session_t, price_exp_) { value = "24"; }); OPTION__ (session_t, file_, // -f std::list data_files; CTOR(session_t, file_) {} - DO_(args) { - assert(args.size() == 2); + DO_(str) { if (parent->flush_on_next_data_file) { data_files.clear(); parent->flush_on_next_data_file = false; } - data_files.push_back(args.get(1)); + data_files.push_back(str); }); - OPTION_(session_t, input_date_format_, DO_(args) { - // This changes static variables inside times.h, which affects the basic - // date parser. - set_input_date_format(args.get(1).c_str()); + OPTION_(session_t, input_date_format_, DO_(str) { + // This changes static variables inside times.h, which affects the + // basic date parser. + set_input_date_format(str.c_str()); }); OPTION(session_t, explicit); diff --git a/test/baseline/opt-no-pager.test b/test/baseline/opt-no-pager.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 9fd1fc1c228eeba8324972d0d034027ad9de6f41 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 05:06:40 -0600 Subject: Simplified some code in commodity_pool_t --- src/pool.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pool.cc b/src/pool.cc index 1dd91e99..0118a97d 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -67,14 +67,12 @@ commodity_t * commodity_pool_t::create(const string& symbol) *commodity->qualified_symbol += "\""; } - DEBUG("pool.commodities", - "Creating commodity '" << commodity->symbol() << "'"); + DEBUG("pool.commodities", "Creating commodity '" << symbol << "'"); #if defined(DEBUG_ON) std::pair result = #endif - commodities.insert(commodities_map::value_type - (commodity->base_symbol(), commodity)); + commodities.insert(commodities_map::value_type(symbol, commodity)); #if defined(DEBUG_ON) assert(result.second); #endif -- cgit v1.2.3 From 1a6ec4e8b4b71ef36cf81bea6e42abbc8610f1ac Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 06:06:17 -0600 Subject: Fixed the way adjacency_list was being used --- src/history.cc | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++------- src/history.h | 90 +++--------------------------------------------- 2 files changed, 99 insertions(+), 98 deletions(-) (limited to 'src') diff --git a/src/history.cc b/src/history.cc index 7c49b343..83326728 100644 --- a/src/history.cc +++ b/src/history.cc @@ -42,13 +42,89 @@ struct f_max : public std::binary_function { namespace ledger { +template +class recent_edge_weight +{ +public: + EdgeWeightMap weight; + PricePointMap price_point; + PriceRatioMap ratios; + + datetime_t reftime; + optional oldest; + + recent_edge_weight() { } + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + datetime_t _reftime, + const optional& _oldest = none) + : weight(_weight), price_point(_price_point), ratios(_ratios), + reftime(_reftime), oldest(_oldest) { } + + template + bool operator()(const Edge& e) const + { +#if defined(DEBUG_ON) + DEBUG("history.find", " reftime = " << reftime); + if (oldest) { + DEBUG("history.find", " oldest = " << *oldest); + } +#endif + + const price_map_t& prices(get(ratios, e)); + if (prices.empty()) { + DEBUG("history.find", " prices map is empty for this edge"); + return false; + } + + price_map_t::const_iterator low = prices.upper_bound(reftime); + if (low != prices.end() && low == prices.begin()) { + DEBUG("history.find", " don't use this edge"); + return false; + } else { + --low; + assert(((*low).first <= reftime)); + + if (oldest && (*low).first < *oldest) { + DEBUG("history.find", " edge is out of range"); + return false; + } + + long secs = (reftime - (*low).first).total_seconds(); + assert(secs >= 0); + + put(weight, e, secs); + put(price_point, e, price_point_t((*low).first, (*low).second)); + + DEBUG("history.find", " using edge at price point " + << (*low).first << " " << (*low).second); + return true; + } + } +}; + +typedef filtered_graph + > FGraph; + +typedef property_map::type FNameMap; +typedef property_map::type FIndexMap; +typedef iterator_property_map + FPredecessorMap; +typedef iterator_property_map FDistanceMap; + void commodity_history_t::add_commodity(commodity_t& comm) { if (! comm.graph_index()) { - std::size_t index = num_vertices(price_graph); - comm.set_graph_index(index); - const vertex_descriptor vert = add_vertex(&comm, price_graph); - put(indexmap, vert, index); + comm.set_graph_index(num_vertices(price_graph)); + add_vertex(/* vertex_name= */ &comm, price_graph); } } @@ -59,7 +135,10 @@ void commodity_history_t::add_price(const commodity_t& source, vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*price.commodity().graph_index(), price_graph); - std::pair e1 = add_edge(sv, tv, 0, price_graph); + std::pair e1 = edge(sv, tv, price_graph); + if (! e1.second) + e1 = add_edge(sv, tv, price_graph); + price_map_t& prices(get(ratiomap, e1.first)); std::pair result = @@ -77,11 +156,16 @@ void commodity_history_t::remove_price(const commodity_t& source, vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); - std::pair e1 = add_edge(sv, tv, 0, price_graph); - price_map_t& prices(get(ratiomap, e1.first)); + std::pair e1 = edge(sv, tv, price_graph); + if (e1.second) { + price_map_t& prices(get(ratiomap, e1.first)); - // jww (2012-03-04): If it fails, should we give a warning? - prices.erase(date); + // jww (2012-03-04): If it fails, should we give a warning? + prices.erase(date); + + if (prices.empty()) + remove_edge(e1.first, price_graph); + } } void commodity_history_t::map_prices(function predecessors(vector_len); std::vector distances(vector_len); - FIndexMap indexMap(get(vertex_index, fg)); - FPredecessorMap predecessorMap(&predecessors[0], indexMap); - FDistanceMap distanceMap(&distances[0], indexMap); + FPredecessorMap predecessorMap(&predecessors[0]); + FDistanceMap distanceMap(&distances[0]); dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) diff --git a/src/history.h b/src/history.h index 63550ff5..920feec6 100644 --- a/src/history.h +++ b/src/history.h @@ -60,79 +60,12 @@ namespace ledger { typedef std::map price_map_t; -template -class recent_edge_weight -{ -public: - EdgeWeightMap weight; - PricePointMap price_point; - PriceRatioMap ratios; - - datetime_t reftime; - optional oldest; - - recent_edge_weight() { } - recent_edge_weight(EdgeWeightMap _weight, - PricePointMap _price_point, - PriceRatioMap _ratios, - datetime_t _reftime, - const optional& _oldest = none) - : weight(_weight), price_point(_price_point), ratios(_ratios), - reftime(_reftime), oldest(_oldest) { } - - template - bool operator()(const Edge& e) const - { -#if defined(DEBUG_ON) - DEBUG("history.find", " reftime = " << reftime); - if (oldest) { - DEBUG("history.find", " oldest = " << *oldest); - } -#endif - - const price_map_t& prices(get(ratios, e)); - if (prices.empty()) { - DEBUG("history.find", " prices map is empty for this edge"); - put(weight, e, std::numeric_limits::max()); - return false; - } - - price_map_t::const_iterator low = prices.upper_bound(reftime); - if (low != prices.end() && low == prices.begin()) { - DEBUG("history.find", " don't use this edge"); - put(weight, e, std::numeric_limits::max()); - return false; - } else { - --low; - assert(((*low).first <= reftime)); - - if (oldest && (*low).first < *oldest) { - DEBUG("history.find", " edge is out of range"); - put(weight, e, std::numeric_limits::max()); - return false; - } - - long secs = (reftime - (*low).first).total_seconds(); - assert(secs >= 0); - - put(weight, e, secs); - put(price_point, e, price_point_t((*low).first, (*low).second)); - - DEBUG("history.find", " using edge at price point " - << (*low).first << " " << (*low).second); - return true; - } - } -}; - class commodity_history_t : public noncopyable { public: typedef adjacency_list - ::vertex_descriptor vertex_descriptor; typedef graph_traits::edge_descriptor edge_descriptor; - typedef property_map::type NameMap; - typedef property_map::type IndexMap; - + typedef property_map::type NameMap; typedef property_map::type EdgeWeightMap; typedef property_map::type PricePointMap; typedef property_map::type PriceRatioMap; - IndexMap indexmap; PricePointMap pricemap; PriceRatioMap ratiomap; - typedef filtered_graph > FGraph; - - typedef property_map::type FNameMap; - typedef property_map::type FIndexMap; - typedef iterator_property_map FPredecessorMap; - typedef iterator_property_map FDistanceMap; - commodity_history_t() - : indexmap(get(vertex_index, price_graph)), - pricemap(get(edge_price_point, price_graph)), + : pricemap(get(edge_price_point, price_graph)), ratiomap(get(edge_price_ratio, price_graph)) {} void add_commodity(commodity_t& comm); -- cgit v1.2.3 From 605fcde2e7c7d9fa05046ca81bff5a75cf2baa94 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 06:51:15 -0600 Subject: Corrected value_expr annotation comparisons --- src/annotate.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/annotate.h b/src/annotate.h index c1f52a62..22c3d8ad 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -93,7 +93,9 @@ struct annotation_t : public supports_flags<>, return (price == rhs.price && date == rhs.date && tag == rhs.tag && - value_expr == rhs.value_expr); + (value_expr && rhs.value_expr ? + value_expr->text() == rhs.value_expr->text() : + value_expr == rhs.value_expr)); } void parse(std::istream& in); -- cgit v1.2.3 From 929a734a30fd9c2dc593a99cbce119e365ea1d7b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 07:02:34 -0600 Subject: -H now implies -V --- src/report.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/report.h b/src/report.h index 2ad53f01..ed3aae2e 100644 --- a/src/report.h +++ b/src/report.h @@ -632,6 +632,7 @@ public: OPTION(report_t, head_); OPTION_(report_t, historical, DO() { // -H + OTHER(market).on(whence); OTHER(amount_) .on(whence, "nail_down(amount_expr, " "market(amount_expr, value_date, exchange))"); -- cgit v1.2.3 From 318b5f49ff07bdd906eb7c976b5c36c2eeb45872 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 07:02:53 -0600 Subject: Fixed the behavior of fn_nail_down --- src/report.cc | 14 ++- test/baseline/feat-value-expr.test | 18 ++- test/baseline/opt-historical.test | 243 ++++++++++++++++++++++++++++++++----- 3 files changed, 234 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 110d33ee..27d8bb2a 100644 --- a/src/report.cc +++ b/src/report.cc @@ -743,12 +743,20 @@ value_t report_t::fn_commodity(call_scope_t& args) value_t report_t::fn_nail_down(call_scope_t& args) { value_t arg0(args[0]); + value_t arg1(args[1]); + switch (arg0.type()) { case value_t::AMOUNT: { amount_t tmp(arg0.as_amount()); - if (tmp.has_commodity()) - tmp.set_commodity(tmp.commodity() - .nail_down(args[1].as_any())); + if (tmp.has_commodity() && ! arg1.is_null()) { + expr_t value_expr(is_expr(arg1) ? + as_expr(arg1) : expr_t::op_t::wrap_value(arg1 / arg0)); + std::ostringstream buf; + value_expr.print(buf); + value_expr.set_text(buf.str()); + + tmp.set_commodity(tmp.commodity().nail_down(value_expr)); + } return tmp; } diff --git a/test/baseline/feat-value-expr.test b/test/baseline/feat-value-expr.test index 690febf9..01f9780b 100644 --- a/test/baseline/feat-value-expr.test +++ b/test/baseline/feat-value-expr.test @@ -89,15 +89,11 @@ account Expenses:Food5 test reg -V food 12-Mar-02 KFC Expenses:Food2 2 EUR 2 EUR -12-Mar-03 KFC -1 EUR 1 EUR - Expenses:Food3 3 EUR 4 EUR -12-Mar-04 KFC -2 EUR 2 EUR - Expenses:Food4 4 EUR 6 EUR -12-Mar-05 KFC -3 EUR 3 EUR - Expenses:Food5 5 EUR 8 EUR -12-Mar-06 KFC -4 EUR 4 EUR - Expenses:Food6 6 EUR 10 EUR -12-Mar-07 KFC Expenses:Food7 7 EUR 17 EUR -12-Mar-08 XACT Expenses:Food8 8 EUR 25 EUR -12-Mar-09 POST (Expenses:Food9) 9 EUR 34 EUR +12-Mar-03 KFC Expenses:Food3 3 EUR 5 EUR +12-Mar-04 KFC Expenses:Food4 4 EUR 9 EUR +12-Mar-05 KFC Expenses:Food5 5 EUR 14 EUR +12-Mar-06 KFC Expenses:Food6 6 EUR 20 EUR +12-Mar-07 KFC Expenses:Food7 7 EUR 27 EUR +12-Mar-08 XACT Expenses:Food8 8 EUR 35 EUR +12-Mar-09 POST (Expenses:Food9) 9 EUR 44 EUR end test diff --git a/test/baseline/opt-historical.test b/test/baseline/opt-historical.test index 1cd141ab..9eb3558b 100644 --- a/test/baseline/opt-historical.test +++ b/test/baseline/opt-historical.test @@ -1,61 +1,250 @@ -2012-03-01 Broker - Assets:Stocks 10 APPL {$1} @ $1 +D EUR 2.000,00 + +P 2011-12-15 $ EUR 2 +P 2011-12-15 AAPL $5.00 + +2012-01-01 Broker + Assets:Stocks 10 AAPL {$1} @ $10 Equity -2012-03-02 Broker - Assets:Stocks 10 APPL {$1} @ $2 +P 2012-01-15 AAPL $15.00 + +2012-02-02 Broker + Assets:Stocks 10 AAPL {$2} @ $20 Equity +P 2012-02-15 AAPL $25.00 + 2012-03-03 Broker - Assets:Stocks 10 APPL {$1} @ $3 + Assets:Stocks 10 AAPL {$3} @ $30 Equity -2012-03-04 Broker - Assets:Stocks 10 APPL {$1} @ $4 +P 2012-03-15 AAPL $35.00 + +2012-04-04 Broker + Assets:Stocks 10 AAPL {$4} @ $40 Equity -2012-03-05 Broker - Assets:Stocks 10 APPL {$1} @ $5 +P 2012-04-15 AAPL $45.00 + +2012-05-05 Broker + Assets:Stocks 10 AAPL {$5} @ $50 Equity +P 2012-5-15 AAPL $55.00 + +test reg stocks +12-Jan-01 Broker Assets:Stocks 10 AAPL 10 AAPL +12-Feb-02 Broker Assets:Stocks 10 AAPL 20 AAPL +12-Mar-03 Broker Assets:Stocks 10 AAPL 30 AAPL +12-Apr-04 Broker Assets:Stocks 10 AAPL 40 AAPL +12-May-05 Broker Assets:Stocks 10 AAPL 50 AAPL +end test + test reg stocks -O -12-Mar-01 Broker Assets:Stocks 10 APPL 10 APPL -12-Mar-02 Broker Assets:Stocks 10 APPL 20 APPL -12-Mar-03 Broker Assets:Stocks 10 APPL 30 APPL -12-Mar-04 Broker Assets:Stocks 10 APPL 40 APPL -12-Mar-05 Broker Assets:Stocks 10 APPL 50 APPL +12-Jan-01 Broker Assets:Stocks 10 AAPL 10 AAPL +12-Feb-02 Broker Assets:Stocks 10 AAPL 20 AAPL +12-Mar-03 Broker Assets:Stocks 10 AAPL 30 AAPL +12-Apr-04 Broker Assets:Stocks 10 AAPL 40 AAPL +12-May-05 Broker Assets:Stocks 10 AAPL 50 AAPL end test test reg stocks -B -12-Mar-01 Broker Assets:Stocks $10 $10 -12-Mar-02 Broker Assets:Stocks $20 $30 -12-Mar-03 Broker Assets:Stocks $30 $60 -12-Mar-04 Broker Assets:Stocks $40 $100 -12-Mar-05 Broker Assets:Stocks $50 $150 +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Feb-02 Broker Assets:Stocks $200 $300 +12-Mar-03 Broker Assets:Stocks $300 $600 +12-Apr-04 Broker Assets:Stocks $400 $1000 +12-May-05 Broker Assets:Stocks $500 $1500 end test test reg stocks -I -12-Mar-01 Broker Assets:Stocks $10 $10 -12-Mar-02 Broker Assets:Stocks $10 $20 -12-Mar-03 Broker Assets:Stocks $10 $30 -12-Mar-04 Broker Assets:Stocks $10 $40 -12-Mar-05 Broker Assets:Stocks $10 $50 +12-Jan-01 Broker Assets:Stocks $10 $10 +12-Feb-02 Broker Assets:Stocks $20 $30 +12-Mar-03 Broker Assets:Stocks $30 $60 +12-Apr-04 Broker Assets:Stocks $40 $100 +12-May-05 Broker Assets:Stocks $50 $150 +end test + +test reg stocks -V +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Jan-15 Commodities revalued $50 $150 +12-Feb-02 Commodities revalued $50 $200 +12-Feb-02 Broker Assets:Stocks $200 $400 +12-Feb-15 Commodities revalued $100 $500 +12-Mar-03 Commodities revalued $100 $600 +12-Mar-03 Broker Assets:Stocks $300 $900 +12-Mar-15 Commodities revalued $150 $1050 +12-Apr-04 Commodities revalued $150 $1200 +12-Apr-04 Broker Assets:Stocks $400 $1600 +12-Apr-15 Commodities revalued $200 $1800 +12-May-05 Commodities revalued $200 $2000 +12-May-05 Broker Assets:Stocks $500 $2500 +end test + +test reg stocks -O -V +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Jan-15 Commodities revalued $50 $150 +12-Feb-02 Commodities revalued $50 $200 +12-Feb-02 Broker Assets:Stocks $200 $400 +12-Feb-15 Commodities revalued $100 $500 +12-Mar-03 Commodities revalued $100 $600 +12-Mar-03 Broker Assets:Stocks $300 $900 +12-Mar-15 Commodities revalued $150 $1050 +12-Apr-04 Commodities revalued $150 $1200 +12-Apr-04 Broker Assets:Stocks $400 $1600 +12-Apr-15 Commodities revalued $200 $1800 +12-May-05 Commodities revalued $200 $2000 +12-May-05 Broker Assets:Stocks $500 $2500 +end test + +test reg stocks -B -V +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Feb-02 Broker Assets:Stocks $200 $300 +12-Mar-03 Broker Assets:Stocks $300 $600 +12-Apr-04 Broker Assets:Stocks $400 $1000 +12-May-05 Broker Assets:Stocks $500 $1500 +end test + +test reg stocks -I -V +12-Jan-01 Broker Assets:Stocks $10 $10 +12-Feb-02 Broker Assets:Stocks $20 $30 +12-Mar-03 Broker Assets:Stocks $30 $60 +12-Apr-04 Broker Assets:Stocks $40 $100 +12-May-05 Broker Assets:Stocks $50 $150 +end test + +test reg stocks -X EUR +12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 +12-Feb-02 Commodities revalued EUR 200,00 EUR 400,00 +12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 800,00 +12-Mar-03 Commodities revalued EUR 400,00 EUR 1.200,00 +12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.800,00 +12-Apr-04 Commodities revalued EUR 600,00 EUR 2.400,00 +12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 3.200,00 +12-May-05 Commodities revalued EUR 800,00 EUR 4.000,00 +12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 5.000,00 +end test + +test reg stocks -O -X EUR +12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 +12-Feb-02 Commodities revalued EUR 200,00 EUR 400,00 +12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 800,00 +12-Mar-03 Commodities revalued EUR 400,00 EUR 1.200,00 +12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.800,00 +12-Apr-04 Commodities revalued EUR 600,00 EUR 2.400,00 +12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 3.200,00 +12-May-05 Commodities revalued EUR 800,00 EUR 4.000,00 +12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 5.000,00 +end test + +test reg stocks -B -X EUR +12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 +12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 600,00 +12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.200,00 +12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 2.000,00 +12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 3.000,00 +end test + +test reg stocks -I -X EUR +12-Jan-01 Broker Assets:Stocks EUR 20,00 EUR 20,00 +12-Feb-02 Broker Assets:Stocks EUR 40,00 EUR 60,00 +12-Mar-03 Broker Assets:Stocks EUR 60,00 EUR 120,00 +12-Apr-04 Broker Assets:Stocks EUR 80,00 EUR 200,00 +12-May-05 Broker Assets:Stocks EUR 100,00 EUR 300,00 +end test + +test reg stocks -H +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Feb-02 Broker Assets:Stocks $200 $300 +12-Mar-03 Broker Assets:Stocks $300 $600 +12-Apr-04 Broker Assets:Stocks $400 $1000 +12-May-05 Broker Assets:Stocks $500 $1500 end test test reg stocks -O -H +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Feb-02 Broker Assets:Stocks $200 $300 +12-Mar-03 Broker Assets:Stocks $300 $600 +12-Apr-04 Broker Assets:Stocks $400 $1000 +12-May-05 Broker Assets:Stocks $500 $1500 end test test reg stocks -B -H +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Feb-02 Broker Assets:Stocks $200 $300 +12-Mar-03 Broker Assets:Stocks $300 $600 +12-Apr-04 Broker Assets:Stocks $400 $1000 +12-May-05 Broker Assets:Stocks $500 $1500 end test test reg stocks -I -H +12-Jan-01 Broker Assets:Stocks $10 $10 +12-Feb-02 Broker Assets:Stocks $20 $30 +12-Mar-03 Broker Assets:Stocks $30 $60 +12-Apr-04 Broker Assets:Stocks $40 $100 +12-May-05 Broker Assets:Stocks $50 $150 end test -test reg stocks -O -V +test reg stocks -H -V +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Feb-02 Broker Assets:Stocks $200 $300 +12-Mar-03 Broker Assets:Stocks $300 $600 +12-Apr-04 Broker Assets:Stocks $400 $1000 +12-May-05 Broker Assets:Stocks $500 $1500 end test -test reg stocks -B -V +test reg stocks -O -H -V +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Feb-02 Broker Assets:Stocks $200 $300 +12-Mar-03 Broker Assets:Stocks $300 $600 +12-Apr-04 Broker Assets:Stocks $400 $1000 +12-May-05 Broker Assets:Stocks $500 $1500 end test -test reg stocks -I -V +test reg stocks -B -H -V +12-Jan-01 Broker Assets:Stocks $100 $100 +12-Feb-02 Broker Assets:Stocks $200 $300 +12-Mar-03 Broker Assets:Stocks $300 $600 +12-Apr-04 Broker Assets:Stocks $400 $1000 +12-May-05 Broker Assets:Stocks $500 $1500 +end test + +test reg stocks -I -H -V +12-Jan-01 Broker Assets:Stocks $10 $10 +12-Feb-02 Broker Assets:Stocks $20 $30 +12-Mar-03 Broker Assets:Stocks $30 $60 +12-Apr-04 Broker Assets:Stocks $40 $100 +12-May-05 Broker Assets:Stocks $50 $150 +end test + +test reg stocks -H -X EUR +12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 +12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 600,00 +12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.200,00 +12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 2.000,00 +12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 3.000,00 +end test + +test reg stocks -O -H -X EUR +12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 +12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 600,00 +12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.200,00 +12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 2.000,00 +12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 3.000,00 +end test + +test reg stocks -B -H -X EUR +12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 +12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 600,00 +12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.200,00 +12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 2.000,00 +12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 3.000,00 +end test + +test reg stocks -I -H -X EUR +12-Jan-01 Broker Assets:Stocks EUR 20,00 EUR 20,00 +12-Feb-02 Broker Assets:Stocks EUR 40,00 EUR 60,00 +12-Mar-03 Broker Assets:Stocks EUR 60,00 EUR 120,00 +12-Apr-04 Broker Assets:Stocks EUR 80,00 EUR 200,00 +12-May-05 Broker Assets:Stocks EUR 100,00 EUR 300,00 end test -- cgit v1.2.3 From 77e9e3bfb9b7342fb367723d18073200c311d420 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 14:11:13 -0600 Subject: Allow --budget-format to be used --- src/report.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 27d8bb2a..2d37f19f 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1003,6 +1003,7 @@ option_t * report_t::lookup_option(const char * p) else OPT_(begin_); else OPT(bold_if_); else OPT(budget); + else OPT(budget_format_); else OPT(by_payee); break; case 'c': -- cgit v1.2.3 From 3886428d1db0a103a050b7394c133abb793eba06 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 14:47:11 -0600 Subject: Move expr_t's ctor/dtors into expr.cc Fixes #672 --- src/expr.cc | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/expr.h | 66 +++++++++++--------------------------------------------- 2 files changed, 84 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/expr.cc b/src/expr.cc index 8c8e995a..b22572f8 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -37,6 +37,59 @@ namespace ledger { +expr_t::expr_t() : base_type() +{ + TRACE_CTOR(expr_t, ""); +} + +expr_t::expr_t(const expr_t& other) : base_type(other), ptr(other.ptr) +{ + TRACE_CTOR(expr_t, "copy"); +} +expr_t::expr_t(ptr_op_t _ptr, scope_t * _context) + : base_type(_context), ptr(_ptr) +{ + TRACE_CTOR(expr_t, "const ptr_op_t&, scope_t *"); +} + +expr_t::expr_t(const string& _str, const parse_flags_t& flags) + : base_type() +{ + TRACE_CTOR(expr_t, "string, parse_flags_t"); + if (! _str.empty()) + parse(_str, flags); +} + +expr_t::expr_t(std::istream& in, const parse_flags_t& flags) + : base_type() +{ + TRACE_CTOR(expr_t, "std::istream&, parse_flags_t"); + parse(in, flags); +} + +expr_t::~expr_t() { + TRACE_DTOR(expr_t); +} + +expr_t& expr_t::operator=(const expr_t& _expr) +{ + if (this != &_expr) { + base_type::operator=(_expr); + ptr = _expr.ptr; + } + return *this; +} + +expr_t::operator bool() const throw() +{ + return ptr.get() != NULL; +} + +expr_t::ptr_op_t expr_t::get_op() throw() +{ + return ptr; +} + void expr_t::parse(std::istream& in, const parse_flags_t& flags, const optional& original_string) { @@ -204,6 +257,24 @@ void merged_expr_t::compile(scope_t& scope) expr_t::compile(scope); } +expr_t::ptr_op_t as_expr(const value_t& val) +{ + VERIFY(val.is_any()); + return val.as_any(); +} + +void set_expr(value_t& val, expr_t::ptr_op_t op) +{ + val.set_any(op); +} + +value_t expr_value(expr_t::ptr_op_t op) +{ + value_t temp; + temp.set_any(op); + return temp; +} + value_t source_command(call_scope_t& args) { std::istream * in = NULL; diff --git a/src/expr.h b/src/expr.h index cad2e90b..590bdc15 100644 --- a/src/expr.h +++ b/src/expr.h @@ -58,9 +58,6 @@ public: typedef intrusive_ptr ptr_op_t; typedef intrusive_ptr const_ptr_op_t; - friend void intrusive_ptr_add_ref(const op_t * op); - friend void intrusive_ptr_release(const op_t * op); - enum check_expr_kind_t { EXPR_GENERAL, EXPR_ASSERTION, @@ -74,49 +71,20 @@ protected: ptr_op_t ptr; public: - expr_t() : base_type() { - TRACE_CTOR(expr_t, ""); - } - expr_t(const expr_t& other) - : base_type(other), ptr(other.ptr) { - TRACE_CTOR(expr_t, "copy"); - } - expr_t(ptr_op_t _ptr, scope_t * _context = NULL) - : base_type(_context), ptr(_ptr) { - TRACE_CTOR(expr_t, "const ptr_op_t&, scope_t *"); - } + expr_t(); + expr_t(const expr_t& other); + expr_t(ptr_op_t _ptr, scope_t * _context = NULL); - expr_t(const string& _str, const parse_flags_t& flags = PARSE_DEFAULT) - : base_type() { - TRACE_CTOR(expr_t, "string, parse_flags_t"); - if (! _str.empty()) - parse(_str, flags); - } - expr_t(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT) - : base_type() { - TRACE_CTOR(expr_t, "std::istream&, parse_flags_t"); - parse(in, flags); - } + expr_t(const string& _str, const parse_flags_t& flags = PARSE_DEFAULT); + expr_t(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT); - virtual ~expr_t() { - TRACE_DTOR(expr_t); - } + virtual ~expr_t(); - expr_t& operator=(const expr_t& _expr) { - if (this != &_expr) { - base_type::operator=(_expr); - ptr = _expr.ptr; - } - return *this; - } + expr_t& operator=(const expr_t& _expr); - virtual operator bool() const throw() { - return ptr.get() != NULL; - } + virtual operator bool() const throw(); - ptr_op_t get_op() throw() { - return ptr; - } + ptr_op_t get_op() throw(); void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) { std::istringstream stream(str); @@ -159,18 +127,10 @@ private: inline bool is_expr(const value_t& val) { return val.is_any() && val.as_any().type() == typeid(expr_t::ptr_op_t); } -inline expr_t::ptr_op_t as_expr(const value_t& val) { - VERIFY(val.is_any()); - return val.as_any(); -} -inline void set_expr(value_t& val, expr_t::ptr_op_t op) { - val.set_any(op); -} -inline value_t expr_value(expr_t::ptr_op_t op) { - value_t temp; - temp.set_any(op); - return temp; -} + +expr_t::ptr_op_t as_expr(const value_t& val); +void set_expr(value_t& val, expr_t::ptr_op_t op); +value_t expr_value(expr_t::ptr_op_t op); // A merged expression allows one to set an expression term, "foo", and // a base expression, "bar", and then merge in later expressions that -- cgit v1.2.3 From cbc7bd337bf523e9424c789a87be1d4a360740b2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 16:21:19 -0600 Subject: Allow valuation of sequences --- src/value.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/value.cc b/src/value.cc index 9f69016d..6e1ed79d 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1407,17 +1407,22 @@ value_t value_t::value(const optional& moment, return NULL_VALUE; case AMOUNT: - if (optional val = - as_amount().value(moment, in_terms_of)) + if (optional val = as_amount().value(moment, in_terms_of)) return *val; return NULL_VALUE; case BALANCE: - if (optional bal = - as_balance().value(moment, in_terms_of)) + if (optional bal = as_balance().value(moment, in_terms_of)) return *bal; return NULL_VALUE; + case SEQUENCE: { + value_t temp; + foreach (const value_t& value, as_sequence()) + temp.push_back(value.value(moment, in_terms_of)); + return temp; + } + default: break; } -- cgit v1.2.3 From 2df8edc71c1e805fd54c2208b2b66bdde0460c59 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 20:02:53 -0600 Subject: Improved the behavior of -X --- src/system.hh.in | 2 + src/value.cc | 101 ++++++++++++++++++++++++++++++++++------ test/RegressTests.py | 2 + test/baseline/opt-exchange.test | 57 +++++++++++++++++++++++ test/regress/83B4A0E5.test | 43 +++++++++++++++++ 5 files changed, 192 insertions(+), 13 deletions(-) create mode 100644 test/regress/83B4A0E5.test (limited to 'src') diff --git a/src/system.hh.in b/src/system.hh.in index fcc8e2ce..a38deb1f 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -185,6 +185,8 @@ typedef std::ostream::pos_type ostream_pos_type; #include #endif // HAVE_BOOST_REGEX_UNICODE +#include + #include #include diff --git a/src/value.cc b/src/value.cc index 6e1ed79d..87b31cd6 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1436,21 +1436,96 @@ value_t value_t::exchange_commodities(const std::string& commodities, const bool add_prices, const optional& moment) { - scoped_array buf(new char[commodities.length() + 1]); - - std::strcpy(buf.get(), commodities.c_str()); - - for (char * p = std::strtok(buf.get(), ","); - p; - p = std::strtok(NULL, ",")) { - if (commodity_t * commodity = - commodity_pool_t::current_pool->parse_price_expression(p, add_prices, - moment)) { - value_t result = value(moment, *commodity); - if (! result.is_null()) - return result; + if (type() == SEQUENCE) { + value_t temp; + foreach (value_t& value, as_sequence_lval()) + temp.push_back(value.exchange_commodities(commodities, add_prices, moment)); + return temp; + } + + // If we are repricing to just a single commodity, with no price + // expression, skip the expensive logic below. + if (commodities.find(',') == string::npos && + commodities.find('=') == string::npos) + return value(moment, *commodity_pool_t::current_pool->find_or_create(commodities)); + + std::vector comms; + std::vector force; + + typedef tokenizer > tokenizer; + tokenizer tokens(commodities, char_separator(",")); + + foreach (const string& name, tokens) { + string::size_type name_len = name.length(); + + if (commodity_t * commodity = commodity_pool_t::current_pool + ->parse_price_expression(name[name_len - 1] == '!' ? + string(name, 0, name_len - 1) : + name, add_prices, moment)) { + DEBUG("commodity.exchange", "Pricing for commodity: " << commodity->symbol()); + comms.push_back(&commodity->referent()); + force.push_back(name[name_len - 1] == '!'); + } + } + + int index = 0; + foreach (commodity_t * comm, comms) { + switch (type()) { + case AMOUNT: + DEBUG("commodity.exchange", "We have an amount: " << as_amount_lval()); + if (! force[index] && + std::find(comms.begin(), comms.end(), + &as_amount_lval().commodity().referent()) != comms.end()) + break; + + DEBUG("commodity.exchange", "Referent doesn't match, pricing..."); + if (optional val = as_amount_lval().value(moment, *comm)) { + DEBUG("commodity.exchange", "Re-priced amount is: " << *val); + return *val; + } + DEBUG("commodity.exchange", "Was unable to find a price"); + break; + + case BALANCE: { + balance_t temp; + bool repriced = false; + + DEBUG("commodity.exchange", "We have a balance: " << as_balance_lval()); + foreach (const balance_t::amounts_map::value_type& pair, + as_balance_lval().amounts) { + DEBUG("commodity.exchange", "We have a balance amount of commodity: " + << pair.first->symbol() << " == " + << pair.second.commodity().symbol()); + if (! force[index] && + std::find(comms.begin(), comms.end(), + &pair.first->referent()) != comms.end()) { + temp += pair.second; + } else { + DEBUG("commodity.exchange", "Referent doesn't match, pricing..."); + if (optional val = pair.second.value(moment, *comm)) { + DEBUG("commodity.exchange", "Re-priced member amount is: " << *val); + temp += *val; + repriced = true; + } else { + DEBUG("commodity.exchange", "Was unable to find price"); + temp += pair.second; + } + } + } + + if (repriced) { + DEBUG("commodity.exchange", "Re-priced balance is: " << temp); + return temp; + } } + + default: + break; + } + + ++index; } + return *this; } diff --git a/test/RegressTests.py b/test/RegressTests.py index da5d92ca..a22e35bf 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -149,6 +149,8 @@ class RegressFile(object): harness.success() else: harness.failure(os.path.basename(self.filename)) + print "STDERR:" + print p.stderr.read() else: if success: print if test['exitcode']: diff --git a/test/baseline/opt-exchange.test b/test/baseline/opt-exchange.test index cfc48c3f..f5d73f78 100644 --- a/test/baseline/opt-exchange.test +++ b/test/baseline/opt-exchange.test @@ -47,6 +47,63 @@ Assets:Brokerage -155 A [2009/01/06] test reg --exchange=' C, A ' +09-Jan-01 January 1st, 2009 (1) Assets:Brokerage 100 A 100 A + Assets:Brokerage -50 A 50 A +09-Jan-01 January 1st, 2009 (2) Assets:Brokerage 100 A 150 A + Assets:Brokerage -75 A 75 A +09-Jan-01 January 1st, 2009 (3) Assets:Brokerage 100 A 175 A + Assets:Brokerage -100 A 75 A +09-Jan-02 Commodities revalued 225 A + -1800 C 300 A + -1800 C +09-Jan-02 January 2nd, 2009 Assets:Brokerage 500 C 300 A + -1300 C + Assets:Brokerage -500 C 300 A + -1800 C +09-Jan-03 January 3rd, 2009 Assets:Brokerage 600 C 300 A + -1200 C + Assets:Brokerage -600 C 300 A + -1800 C +09-Jan-04 January 4th, 2009 Assets:Brokerage 300 A 600 A + -1800 C + Assets:Brokerage -2400 C 600 A + -4200 C +09-Jan-05 January 5th, 2009 Assets:Brokerage 1280 C 600 A + -2920 C + Assets:Brokerage -1280 C 600 A + -4200 C +09-Jan-06 Commodities revalued 2040 C 600 A + -2160 C +09-Jan-06 January 6th, 2009 Assets:Brokerage 155 A 755 A + -2160 C + Assets:Brokerage -186 C 755 A + -2346 C +09-Jan-07 Commodities revalued -86 C 755 A + -2432 C +09-Jan-07 January 7th, 2009 Assets:Brokerage 155 A 910 A + -2432 C + Assets:Brokerage -200 C 910 A + -2632 C +09-Jan-08 Commodities revalued -5613 C 910 A + -8245 C +09-Jan-08 January 8th, 2009 Assets:Brokerage 155 A 1065 A + -8245 C + Assets:Brokerage -200 C 1065 A + -8445 C +09-Jan-09 Commodities revalued -2800 C 1065 A + -11245 C +09-Jan-09 January 9th, 2009 Assets:Brokerage 200 C 1065 A + -11045 C + Assets:Brokerage -155 A 910 A + -11045 C +09-Jan-10 January 10th, 2009 Assets:Brokerage 200 C 910 A + -10845 C + Assets:Brokerage -155 A 755 A + -10845 C +end test + + +test reg --exchange=' C!, A ' 09-Jan-01 January 1st, 2009 (1) Assets:Brokerage 100 A 100 A Assets:Brokerage -50 A 50 A 09-Jan-01 January 1st, 2009 (2) Assets:Brokerage 100 A 150 A diff --git a/test/regress/83B4A0E5.test b/test/regress/83B4A0E5.test new file mode 100644 index 00000000..f9402a2d --- /dev/null +++ b/test/regress/83B4A0E5.test @@ -0,0 +1,43 @@ +P 2012-03-01 EUR $2 +P 2012-03-01 GBP $2 + +2012-03-05 KFC + Expenses:Food 10 EUR + Assets:Cash + +2012-03-10 KFC + Expenses:Food 10 GBP + Assets:Cash + +test reg food +12-Mar-05 KFC Expenses:Food 10 EUR 10 EUR +12-Mar-10 KFC Expenses:Food 10 GBP 10 EUR + 10 GBP +end test + +test reg food -V +12-Mar-05 KFC Expenses:Food $20 $20 +12-Mar-10 KFC Expenses:Food $20 $40 +end test + +test reg food -X '$' +12-Mar-05 KFC Expenses:Food $20 $20 +12-Mar-10 KFC Expenses:Food $20 $40 +end test + +test reg food -X '$,GBP' +12-Mar-05 KFC Expenses:Food $20 $20 +12-Mar-10 KFC Expenses:Food 10 GBP $20 + 10 GBP +end test + +test reg food -X '$!,GBP' +12-Mar-05 KFC Expenses:Food $20 $20 +12-Mar-10 KFC Expenses:Food $20 $40 +end test + +test reg food -X '$,EUR' +12-Mar-05 KFC Expenses:Food 10 EUR 10 EUR +12-Mar-10 KFC Expenses:Food $20 $20 + 10 EUR +end test -- cgit v1.2.3 From 022059f2a856807cad7388ea230c2ad5bcbce75d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 20:11:38 -0600 Subject: Added --value-expr option --- doc/ledger.1 | 1 + src/session.cc | 2 ++ src/session.h | 3 +++ test/baseline/opt-value-expr.test | 0 4 files changed, 6 insertions(+) create mode 100644 test/baseline/opt-value-expr.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index a948d5a6..a8e5dcc4 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -424,6 +424,7 @@ appeared in the original journal file. .It Fl \-unrealized-gains .It Fl \-unrealized-losses .It Fl \-unround +.It Fl \-value-expr Ar EXPR .It Fl \-verbose .It Fl \-verify .It Fl \-version diff --git a/src/session.cc b/src/session.cc index 7c546b82..d0cd4573 100644 --- a/src/session.cc +++ b/src/session.cc @@ -112,6 +112,8 @@ std::size_t session_t::read_data(const string& master_account) journal->checking_style = journal_t::CHECK_ERROR; else if (HANDLED(strict)) journal->checking_style = journal_t::CHECK_WARNING; + else if (HANDLED(value_expr)) + journal->value_expr = HANDLER(value_expr).str(); #if defined(HAVE_BOOST_SERIALIZATION) optional cache; diff --git a/src/session.h b/src/session.h index cb981346..f2b1dbef 100644 --- a/src/session.h +++ b/src/session.h @@ -61,6 +61,7 @@ public: unique_ptr journal; parse_context_stack_t parsing_context; + optional value_expr; explicit session_t(); virtual ~session_t() { @@ -107,6 +108,7 @@ public: HANDLER(price_db_).report(out); HANDLER(price_exp_).report(out); HANDLER(strict).report(out); + HANDLER(value_expr).report(out); } option_t * lookup_option(const char * p); @@ -154,6 +156,7 @@ public: OPTION(session_t, permissive); OPTION(session_t, price_db_); OPTION(session_t, strict); + OPTION(session_t, value_expr); }; /** diff --git a/test/baseline/opt-value-expr.test b/test/baseline/opt-value-expr.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 5e0efb5e8b117fa7c41d7377b506f386e3bbfefe Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 20:16:44 -0600 Subject: Fixed options processing of --value-expr --- src/session.cc | 7 +++++-- src/session.h | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/session.cc b/src/session.cc index d0cd4573..76061de7 100644 --- a/src/session.cc +++ b/src/session.cc @@ -112,8 +112,8 @@ std::size_t session_t::read_data(const string& master_account) journal->checking_style = journal_t::CHECK_ERROR; else if (HANDLED(strict)) journal->checking_style = journal_t::CHECK_WARNING; - else if (HANDLED(value_expr)) - journal->value_expr = HANDLER(value_expr).str(); + else if (HANDLED(value_expr_)) + journal->value_expr = HANDLER(value_expr_).str(); #if defined(HAVE_BOOST_SERIALIZATION) optional cache; @@ -345,6 +345,9 @@ option_t * session_t::lookup_option(const char * p) case 's': OPT(strict); break; + case 'v': + OPT(value_expr_); + break; } return NULL; } diff --git a/src/session.h b/src/session.h index f2b1dbef..962664ef 100644 --- a/src/session.h +++ b/src/session.h @@ -108,7 +108,7 @@ public: HANDLER(price_db_).report(out); HANDLER(price_exp_).report(out); HANDLER(strict).report(out); - HANDLER(value_expr).report(out); + HANDLER(value_expr_).report(out); } option_t * lookup_option(const char * p); @@ -156,7 +156,7 @@ public: OPTION(session_t, permissive); OPTION(session_t, price_db_); OPTION(session_t, strict); - OPTION(session_t, value_expr); + OPTION(session_t, value_expr_); }; /** -- cgit v1.2.3 From 50f202c4e8faecf7398fb21ba5b31768ec46c826 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 10 Mar 2012 01:01:18 -0600 Subject: Make --lot-notes a synonym for --lot-tags --- doc/ledger.1 | 3 ++- src/report.cc | 2 +- test/baseline/opt-lot-notes.test | 0 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 test/baseline/opt-lot-notes.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index a8e5dcc4..2879cbe0 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd March 9, 2012 +.Dd March 10, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -349,6 +349,7 @@ See .It Fl \-leeway Ar INT Pq Fl Z .It Fl \-limit Ar EXPR Pq Fl l .It Fl \-lot-dates +.It Fl \-lot-notes .It Fl \-lot-prices .It Fl \-lot-tags .It Fl \-lots diff --git a/src/report.cc b/src/report.cc index 2d37f19f..c959e70a 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1068,7 +1068,7 @@ option_t * report_t::lookup_option(const char * p) OPT_(limit_); else OPT(lot_dates); else OPT(lot_prices); - else OPT(lot_tags); + else OPT_ALT(lot_tags, lot_notes); else OPT(lots); else OPT(lots_actual); else OPT_ALT(tail_, last_); diff --git a/test/baseline/opt-lot-notes.test b/test/baseline/opt-lot-notes.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 080c1d9a2d0f3013a4d26879a3e98cfa62ef3e48 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 10 Mar 2012 02:27:33 -0600 Subject: Added syntactic sugar for lot pricing: {{$500.00}} --- src/amount.cc | 12 ++++++++---- src/annotate.cc | 20 +++++++++++++++++--- src/annotate.h | 7 ++++--- test/baseline/feat-annotations.test | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 test/baseline/feat-annotations.test (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index d1aa1655..46eb5531 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -1065,10 +1065,6 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) if (! commodity_) commodity_ = commodity_pool_t::current_pool->create(symbol); assert(commodity_); - - if (details) - commodity_ = - commodity_pool_t::current_pool->find_or_create(*commodity_, details); } // Quickly scan through and verify the correctness of the amount's use of @@ -1204,6 +1200,14 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) if (! flags.has_flags(PARSE_NO_REDUCE)) in_place_reduce(); // will not throw an exception + if (commodity_ && details) { + if (details.has_flags(ANNOTATION_PRICE_NOT_PER_UNIT)) { + assert(details.price); + *details.price /= this->abs(); + } + set_commodity(*commodity_pool_t::current_pool->find_or_create(*commodity_, details)); + } + VERIFY(valid()); return true; diff --git a/src/annotate.cc b/src/annotate.cc index 1140bf0a..d2c7f983 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -88,6 +88,12 @@ void annotation_t::parse(std::istream& in) throw_(amount_error, _("Commodity specifies more than one price")); in.get(c); + c = static_cast(in.peek()); + if (c == '{') { + in.get(c); + add_flags(ANNOTATION_PRICE_NOT_PER_UNIT); + } + c = peek_next_nonws(in); if (c == '=') { in.get(c); @@ -95,10 +101,18 @@ void annotation_t::parse(std::istream& in) } READ_INTO(in, buf, 255, c, c != '}'); - if (c == '}') + if (c == '}') { in.get(c); - else - throw_(amount_error, _("Commodity price lacks closing brace")); + if (has_flags(ANNOTATION_PRICE_NOT_PER_UNIT)) { + c = static_cast(in.peek()); + if (c != '}') + throw_(amount_error, _("Commodity lot price lacks double closing brace")); + else + in.get(c); + } + } else { + throw_(amount_error, _("Commodity lot price lacks closing brace")); + } amount_t temp; temp.parse(buf, PARSE_NO_MIGRATE); diff --git a/src/annotate.h b/src/annotate.h index 22c3d8ad..606c6a60 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -55,9 +55,10 @@ struct annotation_t : public supports_flags<>, { #define ANNOTATION_PRICE_CALCULATED 0x01 #define ANNOTATION_PRICE_FIXATED 0x02 -#define ANNOTATION_DATE_CALCULATED 0x04 -#define ANNOTATION_TAG_CALCULATED 0x08 -#define ANNOTATION_VALUE_EXPR_CALCULATED 0x10 +#define ANNOTATION_PRICE_NOT_PER_UNIT 0x04 +#define ANNOTATION_DATE_CALCULATED 0x08 +#define ANNOTATION_TAG_CALCULATED 0x10 +#define ANNOTATION_VALUE_EXPR_CALCULATED 0x20 optional price; optional date; diff --git a/test/baseline/feat-annotations.test b/test/baseline/feat-annotations.test new file mode 100644 index 00000000..18f5d7d9 --- /dev/null +++ b/test/baseline/feat-annotations.test @@ -0,0 +1,37 @@ +2012-03-09 KFC + Expenses:Food 10 CHIK @ $50 + Assets:Cash + +2012-03-09 KFC + Assets:Cash $75 + Expenses:Food -10 CHIK {{$50}} @ $75 + Equity:Capital Gains $-25 + +2012-03-09 KFC + Expenses:Food 10 CHIK + Assets:Cash $-50 + +2012-03-09 KFC + Assets:Cash $75 + Expenses:Food -10 CHIK {{$50}} + Equity:Capital Gains $-25 + +test print +2012/03/09 KFC + Expenses:Food 10 CHIK @ $50 + Assets:Cash + +2012/03/09 KFC + Assets:Cash $75 + Expenses:Food -10 CHIK {$5} @ $75 + Equity:Capital Gains $-25 + +2012/03/09 KFC + Expenses:Food 10 CHIK + Assets:Cash $-50 + +2012/03/09 KFC + Assets:Cash $75 + Expenses:Food -10 CHIK {$5} + Equity:Capital Gains $-25 +end test -- cgit v1.2.3 From 02225a014a6e7b3d41cd6b64632b2dba06681c67 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 10 Mar 2012 21:32:31 -0600 Subject: Give a better error when sequences are mis-indexed --- src/report.cc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index c959e70a..4dd0bca4 100644 --- a/src/report.cc +++ b/src/report.cc @@ -539,13 +539,20 @@ value_t report_t::fn_get_at(call_scope_t& args) if (index == 0) { if (! args[0].is_sequence()) return args[0]; - } else { - if (! args[0].is_sequence()) - throw_(std::runtime_error, - _("Attempting to get argument at index %1 from %2") - << index << args[0].label()); } - return args[0].as_sequence()[index]; + else if (! args[0].is_sequence()) { + throw_(std::runtime_error, + _("Attempting to get argument at index %1 from %2") + << index << args[0].label()); + } + + value_t::sequence_t& seq(args[0].as_sequence_lval()); + if (index >= seq.size()) + throw_(std::runtime_error, + _("Attempting to get index %1 from %2 with %3 elements") + << index << args[0].label() << seq.size()); + + return seq[index]; } value_t report_t::fn_is_seq(call_scope_t& scope) -- cgit v1.2.3 From 7b45a664f2d188065dadb8506742f718982373eb Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 10 Mar 2012 21:32:48 -0600 Subject: Make --lot-notes the primary name, not --lot-tags --- doc/ledger.1 | 1 - src/report.cc | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 2879cbe0..20369a78 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -351,7 +351,6 @@ See .It Fl \-lot-dates .It Fl \-lot-notes .It Fl \-lot-prices -.It Fl \-lot-tags .It Fl \-lots .It Fl \-lots-actual .It Fl \-market Pq Fl V diff --git a/src/report.cc b/src/report.cc index 4dd0bca4..36865a75 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1075,7 +1075,7 @@ option_t * report_t::lookup_option(const char * p) OPT_(limit_); else OPT(lot_dates); else OPT(lot_prices); - else OPT_ALT(lot_tags, lot_notes); + else OPT_ALT(lot_notes, lot_tags); else OPT(lots); else OPT(lots_actual); else OPT_ALT(tail_, last_); -- cgit v1.2.3 From 1956393a44b981ef722dc1eab0264c4f714a81a6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 10 Mar 2012 21:34:37 -0600 Subject: Allow conversion of negative values with int() --- src/value.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/value.cc b/src/value.cc index 87b31cd6..97328c37 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1228,7 +1228,7 @@ void value_t::in_place_cast(type_t cast_type) case STRING: switch (cast_type) { case INTEGER: { - if (all(as_string(), is_digit())) { + if (all(as_string(), is_any_of("-0123456789"))) { set_long(lexical_cast(as_string())); return; } -- cgit v1.2.3 From 860610fdaf94ca5904ea3e0d3d82126de74c7d80 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 10 Mar 2012 21:34:51 -0600 Subject: Added --dc option, for debit/credit style reports --- doc/ledger.1 | 1 + src/report.cc | 19 +++++++-- src/report.h | 76 ++++++++++++++++++++++++++++++++--- test/baseline/opt-dc.test | 16 ++++++++ test/baseline/opt-meta-width.test | 4 +- test/manual/transaction-codes-1.test | 4 +- test/manual/transaction-notes-1.test | 4 +- test/manual/transaction-notes-2.test | 2 +- test/manual/transaction-notes-3.test | 2 +- test/manual/transaction-status-1.test | 6 +-- test/manual/transaction-status-2.test | 2 +- test/manual/transaction-status-3.test | 4 +- test/manual/transaction-status-4.test | 2 +- 13 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 test/baseline/opt-dc.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 20369a78..d69d6c22 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -300,6 +300,7 @@ See .It Fl \-date-format Ar DATEFMT Pq Fl y .It Fl \-datetime-format Ar FMT .It Fl \-date-width Ar INT +.It Fl \-dc .It Fl \-debug Ar STR .It Fl \-decimal-comma .It Fl \-depth Ar INT diff --git a/src/report.cc b/src/report.cc index 36865a75..8cfa7a59 100644 --- a/src/report.cc +++ b/src/report.cc @@ -204,10 +204,22 @@ void report_t::normalize_options(const string& verb) ! HANDLED(amount_width_) && ! HANDLED(total_width_)) { long total = (4 /* the spaces between */ + date_width + payee_width + - account_width + amount_width + total_width); - if (total > cols) { + account_width + amount_width + total_width + + (HANDLED(dc) ? 1 + amount_width : 0)); + while (total > cols && account_width > 5 && payee_width > 5) { DEBUG("auto.columns", "adjusting account down"); - account_width -= total - cols; + if (total > cols) { + --account_width; + --total; + if (total > cols) { + --account_width; + --total; + } + } + if (total > cols) { + --payee_width; + --total; + } DEBUG("auto.columns", "account_width now = " << account_width); } } @@ -1029,6 +1041,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(date_); else OPT(date_format_); else OPT(datetime_format_); + else OPT(dc); else OPT(depth_); else OPT(deviation); else OPT_(display_); diff --git a/src/report.h b/src/report.h index ed3aae2e..04fdcd45 100644 --- a/src/report.h +++ b/src/report.h @@ -215,7 +215,7 @@ public: bool lots = HANDLED(lots) || HANDLED(lots_actual); return keep_details_t(lots || HANDLED(lot_prices), lots || HANDLED(lot_dates), - lots || HANDLED(lot_tags), + lots || HANDLED(lot_notes), HANDLED(lots_actual)); } @@ -250,6 +250,7 @@ public: HANDLER(date_).report(out); HANDLER(date_format_).report(out); HANDLER(datetime_format_).report(out); + HANDLER(dc).report(out); HANDLER(depth_).report(out); HANDLER(deviation).report(out); HANDLER(display_).report(out); @@ -277,7 +278,7 @@ public: HANDLER(limit_).report(out); HANDLER(lot_dates).report(out); HANDLER(lot_prices).report(out); - HANDLER(lot_tags).report(out); + HANDLER(lot_notes).report(out); HANDLER(lots).report(out); HANDLER(lots_actual).report(out); HANDLER(market).report(out); @@ -518,6 +519,73 @@ public: OPTION(report_t, date_format_); OPTION(report_t, datetime_format_); + OPTION_(report_t, dc, DO() { + OTHER(amount_).expr.set_base_expr + ("(amount > 0 ? amount : 0, amount < 0 ? amount : 0)"); + + OTHER(register_format_) + .on(none, + "%(ansify_if(" + " ansify_if(justify(format_date(date), int(date_width))," + " green if color and date > today)," + " bold if should_bold))" + " %(ansify_if(" + " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), " + " bold if color and !cleared and actual)," + " bold if should_bold))" + " %(ansify_if(" + " ansify_if(justify(truncated(display_account, int(account_width), " + " abbrev_len), int(account_width))," + " blue if color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(abs(get_at(display_amount, 0))), int(amount_width), " + " 3 + int(meta_width) + int(date_width) + int(payee_width)" + " + int(account_width) + int(amount_width) + int(prepend_width)," + " true, color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(abs(get_at(display_amount, 1))), int(amount_width), " + " 4 + int(meta_width) + int(date_width) + int(payee_width)" + " + int(account_width) + int(amount_width) + int(amount_width) + int(prepend_width)," + " true, color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(get_at(display_total, 0) + get_at(display_total, 1)), int(total_width), " + " 5 + int(meta_width) + int(date_width) + int(payee_width)" + " + int(account_width) + int(amount_width) + int(amount_width) + int(total_width)" + " + int(prepend_width), true, color)," + " bold if should_bold))\n%/" + "%(justify(\" \", int(date_width)))" + " %(ansify_if(" + " justify(truncated(has_tag(\"Payee\") ? payee : \" \", " + " int(payee_width)), int(payee_width))," + " bold if should_bold))" + " %$3 %$4 %$5 %$6\n"); + + OTHER(balance_format_) + .on(none, + "%(ansify_if(" + " justify(scrub(abs(get_at(display_total, 0))), 14," + " 14 + int(prepend_width), true, color)," + " bold if should_bold)) " + "%(ansify_if(" + " justify(scrub(abs(get_at(display_total, 1))), 14," + " 14 + 1 + int(prepend_width) + int(total_width), true, color)," + " bold if should_bold)) " + "%(ansify_if(" + " justify(scrub(get_at(display_total, 0) + get_at(display_total, 1)), 14," + " 14 + 2 + int(prepend_width) + int(total_width) + int(total_width), true, color)," + " bold if should_bold))" + " %(!options.flat ? depth_spacer : \"\")" + "%-(ansify_if(" + " ansify_if(partial_account(options.flat), blue if color)," + " bold if should_bold))\n%/" + "%$1 %$2 %$3\n%/" + "%(prepend_width ? \" \" * int(prepend_width) : \"\")" + "--------------------------------------------\n"); + }); + OPTION_(report_t, depth_, DO_(str) { OTHER(display_).on(whence, string("depth<=") + str); }); @@ -590,9 +658,7 @@ public: OPTION_(report_t, gain, DO() { // -G OTHER(revalued).on(whence); - OTHER(amount_).expr.set_base_expr("(amount, cost)"); - OTHER(total_).expr.set_base_expr("total"); // Since we are displaying the amounts of revalued postings, they // will end up being composite totals, and hence a pair of pairs. @@ -653,7 +719,7 @@ public: OPTION(report_t, lot_dates); OPTION(report_t, lot_prices); - OPTION(report_t, lot_tags); + OPTION(report_t, lot_notes); OPTION(report_t, lots); OPTION(report_t, lots_actual); diff --git a/test/baseline/opt-dc.test b/test/baseline/opt-dc.test new file mode 100644 index 00000000..24a564dd --- /dev/null +++ b/test/baseline/opt-dc.test @@ -0,0 +1,16 @@ +2012-03-10 Employer + Assets:Cash $100 + Income:Employer + +2012-03-10 KFC + Expenses:Food $20 + Assets:Cash + +2012-03-10 KFC - Rebate + Assets:Cash + Expenses:Food $-5 + +2012-03-10 KFC - Food & Rebate + Expenses:Food $20 + Expenses:Food $-5 + Assets:Cash diff --git a/test/baseline/opt-meta-width.test b/test/baseline/opt-meta-width.test index ce751e24..893e175b 100644 --- a/test/baseline/opt-meta-width.test +++ b/test/baseline/opt-meta-width.test @@ -9,6 +9,6 @@ ; :AnotherTag: test reg --meta Sample --meta-width=15 -Another Value 04-May-27 Credit card com.. Liab:MasterCard $20.00 $20.00 -Value As:Ban:Checking $-20.00 0 +Another Value 04-May-27 Credit card co.. Liabi:MasterCard $20.00 $20.00 +Value As:Bank:Checking $-20.00 0 end test diff --git a/test/manual/transaction-codes-1.test b/test/manual/transaction-codes-1.test index 7a05b349..ff68e0ec 100644 --- a/test/manual/transaction-codes-1.test +++ b/test/manual/transaction-codes-1.test @@ -15,6 +15,6 @@ Liabilities:Credit Card test reg --columns=60 food and code xfer -09-Oct-29 Panera Bread Expenses:Food $4.50 $4.50 -09-Oct-30 Panera Bread Expenses:Food $4.50 $9.00 +09-Oct-29 Panera Bread Expenses:Food $4.50 $4.50 +09-Oct-30 Panera Bread Expenses:Food $4.50 $9.00 end test diff --git a/test/manual/transaction-notes-1.test b/test/manual/transaction-notes-1.test index 7c3d7200..05ab3412 100644 --- a/test/manual/transaction-notes-1.test +++ b/test/manual/transaction-notes-1.test @@ -17,6 +17,6 @@ Assets:Checking test reg --columns=60 food and note eat -09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 -09-Nov-01 Panera Bread Expenses:Food $4.50 $9.00 +09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 +09-Nov-01 Panera Bread Expenses:Food $4.50 $9.00 end test diff --git a/test/manual/transaction-notes-2.test b/test/manual/transaction-notes-2.test index 603fcbe1..a29eda6e 100644 --- a/test/manual/transaction-notes-2.test +++ b/test/manual/transaction-notes-2.test @@ -17,5 +17,5 @@ Assets:Checking test reg --columns=60 food and tag eating -09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 +09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 end test diff --git a/test/manual/transaction-notes-3.test b/test/manual/transaction-notes-3.test index 9b05334c..b83322b0 100644 --- a/test/manual/transaction-notes-3.test +++ b/test/manual/transaction-notes-3.test @@ -17,5 +17,5 @@ Assets:Checking test reg --columns=60 food and tag type=dining -09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 +09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 end test diff --git a/test/manual/transaction-status-1.test b/test/manual/transaction-status-1.test index 1f7ad095..8bfdd6d9 100644 --- a/test/manual/transaction-status-1.test +++ b/test/manual/transaction-status-1.test @@ -11,7 +11,7 @@ Assets test reg --columns=60 food -09-Oct-31 Panera Bread Expenses:Food $4.50 $4.50 -09-Nov-01 Panera Bread Expenses:Food $4.50 $9.00 -09-Nov-02 Panera Bread Expenses:Food $4.50 $13.50 +09-Oct-31 Panera Bread Expenses:Food $4.50 $4.50 +09-Nov-01 Panera Bread Expenses:Food $4.50 $9.00 +09-Nov-02 Panera Bread Expenses:Food $4.50 $13.50 end test diff --git a/test/manual/transaction-status-2.test b/test/manual/transaction-status-2.test index 6c6d4b8c..94c42a65 100644 --- a/test/manual/transaction-status-2.test +++ b/test/manual/transaction-status-2.test @@ -11,5 +11,5 @@ Assets test reg --columns=60 food --cleared -09-Oct-31 Panera Bread Expenses:Food $4.50 $4.50 +09-Oct-31 Panera Bread Expenses:Food $4.50 $4.50 end test diff --git a/test/manual/transaction-status-3.test b/test/manual/transaction-status-3.test index f50ea23c..f11cd0f7 100644 --- a/test/manual/transaction-status-3.test +++ b/test/manual/transaction-status-3.test @@ -11,6 +11,6 @@ Assets test reg --columns=60 food --uncleared -09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 -09-Nov-02 Panera Bread Expenses:Food $4.50 $9.00 +09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 +09-Nov-02 Panera Bread Expenses:Food $4.50 $9.00 end test diff --git a/test/manual/transaction-status-4.test b/test/manual/transaction-status-4.test index 2ae03c3e..c6f0419b 100644 --- a/test/manual/transaction-status-4.test +++ b/test/manual/transaction-status-4.test @@ -11,5 +11,5 @@ Assets test reg --columns=60 food --pending -09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 +09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 end test -- cgit v1.2.3 From 2a4d7e1af0ac18693b0c1ffa39daa4ad51e1492f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 10 Mar 2012 21:58:28 -0600 Subject: Added --immediate option --- doc/ledger.1 | 1 + src/report.cc | 6 ++++++ src/report.h | 2 ++ test/baseline/opt-immediate.test | 0 4 files changed, 9 insertions(+) create mode 100644 test/baseline/opt-immediate.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index d69d6c22..8076c7c4 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -339,6 +339,7 @@ See .It Fl \-help-calc .It Fl \-help-comm .It Fl \-help-disp +.It Fl \-immediate .It Fl \-import Ar STR .It Fl \-init-file Ar FILE .It Fl \-inject Ar STR diff --git a/src/report.cc b/src/report.cc index 8cfa7a59..bd2df046 100644 --- a/src/report.cc +++ b/src/report.cc @@ -162,6 +162,11 @@ void report_t::normalize_options(const string& verb) terminus); } + if (HANDLED(immediate) && HANDLED(market)) { + HANDLER(amount_) + .on("?normalize", "market(amount_expr, value_date, exchange)"); + } + long cols = 0; if (HANDLED(columns_)) cols = lexical_cast(HANDLER(columns_).value); @@ -1080,6 +1085,7 @@ option_t * report_t::lookup_option(const char * p) case 'i': OPT(invert); else OPT(inject_); + else OPT(immediate); break; case 'j': OPT_CH(amount_data); diff --git a/src/report.h b/src/report.h index 04fdcd45..a3825335 100644 --- a/src/report.h +++ b/src/report.h @@ -273,6 +273,7 @@ public: HANDLER(group_by_).report(out); HANDLER(group_title_format_).report(out); HANDLER(head_).report(out); + HANDLER(immediate).report(out); HANDLER(inject_).report(out); HANDLER(invert).report(out); HANDLER(limit_).report(out); @@ -704,6 +705,7 @@ public: "market(amount_expr, value_date, exchange))"); }); + OPTION(report_t, immediate); OPTION(report_t, inject_); OPTION_(report_t, invert, DO() { diff --git a/test/baseline/opt-immediate.test b/test/baseline/opt-immediate.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 6ddd935e4a140d77b9f94beeed1c76286c9cc1ca Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 10 Mar 2012 21:58:43 -0600 Subject: --percent now plays well with -X and -V --- src/report.cc | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index bd2df046..2b07efcb 100644 --- a/src/report.cc +++ b/src/report.cc @@ -162,6 +162,15 @@ void report_t::normalize_options(const string& verb) terminus); } + if (HANDLED(percent) && HANDLED(market)) { + HANDLER(total_) + .on("?normalize", + "(__tmp = market(parent.total, value_date, exchange);" + " ((is_account & parent & __tmp) ?" + " percent(scrub(market(total, value_date, exchange)), " + " scrub(__tmp)) : 0))"); + } + if (HANDLED(immediate) && HANDLED(market)) { HANDLER(amount_) .on("?normalize", "market(amount_expr, value_date, exchange)"); -- cgit v1.2.3 From 2a41649584c731024c3d3331b8a4a5f4011b9693 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 03:05:33 -0500 Subject: Improve error reporting of nested function calls --- src/op.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index 0773c093..23f47f73 100644 --- a/src/op.cc +++ b/src/op.cc @@ -537,13 +537,13 @@ value_t expr_t::op_t::calc_call(scope_t& scope, ptr_op_t * locus, ptr_op_t func = left(); string name = func->is_ident() ? func->as_ident() : ""; - try { - func = find_definition(func, scope, locus, depth); + func = find_definition(func, scope, locus, depth); - call_scope_t call_args(scope, locus, depth + 1); - if (has_right()) - call_args.set_args(split_cons_expr(right())); + call_scope_t call_args(scope, locus, depth + 1); + if (has_right()) + call_args.set_args(split_cons_expr(right())); + try { if (func->is_function()) { return func->as_function()(call_args); } else { @@ -552,7 +552,8 @@ value_t expr_t::op_t::calc_call(scope_t& scope, ptr_op_t * locus, } } catch (const std::exception&) { - add_error_context(_("While calling function '%1':" << name)); + add_error_context(_("While calling function '%1 %2':" << name + << call_args.args)); throw; } } -- cgit v1.2.3 From 0d9d8453387e0f49f0dd4ea4b5f090da91c283bd Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 03:06:03 -0500 Subject: Corrected a rounding error in nail_down Fixes #678 --- src/report.cc | 3 ++- test/regress/889BB167.test | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/regress/889BB167.test (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 2b07efcb..fb585ce1 100644 --- a/src/report.cc +++ b/src/report.cc @@ -783,7 +783,8 @@ value_t report_t::fn_nail_down(call_scope_t& args) amount_t tmp(arg0.as_amount()); if (tmp.has_commodity() && ! arg1.is_null()) { expr_t value_expr(is_expr(arg1) ? - as_expr(arg1) : expr_t::op_t::wrap_value(arg1 / arg0)); + as_expr(arg1) : + expr_t::op_t::wrap_value(arg1.unrounded() / arg0)); std::ostringstream buf; value_expr.print(buf); value_expr.set_text(buf.str()); diff --git a/test/regress/889BB167.test b/test/regress/889BB167.test new file mode 100644 index 00000000..02e25ab6 --- /dev/null +++ b/test/regress/889BB167.test @@ -0,0 +1,17 @@ +D 1000.00 GBP + +P 2011-01-01 EUR 0.8604 GBP +P 2011-02-01 EUR 0.8576 GBP + +2011-01-31 * AdSense earnings + Assets:Receivable:AdSense 11.00 EUR + Income:AdSense + +2011-02-28 * AdSense earnings + Assets:Receivable:AdSense 10.00 EUR + Income:AdSense + +test reg income:adse -X GBP -H +11-Jan-31 AdSense earnings Income:AdSense -9.46 GBP -9.46 GBP +11-Feb-28 AdSense earnings Income:AdSense -8.58 GBP -18.04 GBP +end test -- cgit v1.2.3 From 2303aa993cf41ad5bde8f8f5722bd265c11e3aa7 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 03:06:50 -0500 Subject: Allow balances to be passed to nail_down Fixes #679 --- src/report.cc | 23 +++++++++++++++++++++++ src/value.cc | 2 +- test/regress/14DB77E7.test | 18 ++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 test/regress/14DB77E7.test (limited to 'src') diff --git a/src/report.cc b/src/report.cc index fb585ce1..8205c1dd 100644 --- a/src/report.cc +++ b/src/report.cc @@ -794,6 +794,29 @@ value_t report_t::fn_nail_down(call_scope_t& args) return tmp; } + case value_t::BALANCE: { + balance_t tmp; + foreach (const balance_t::amounts_map::value_type& pair, + arg0.as_balance_lval().amounts) { + call_scope_t inner_args(*args.parent); + inner_args.push_back(pair.second); + inner_args.push_back(arg1); + tmp += fn_nail_down(inner_args).as_amount(); + } + return tmp; + } + + case value_t::SEQUENCE: { + value_t tmp; + foreach (value_t& value, arg0.as_sequence_lval()) { + call_scope_t inner_args(*args.parent); + inner_args.push_back(value); + inner_args.push_back(arg1); + tmp.push_back(fn_nail_down(inner_args)); + } + return tmp; + } + default: throw_(std::runtime_error, _("Attempting to nail down %1") << args[0].label()); diff --git a/src/value.cc b/src/value.cc index 97328c37..c4e7170d 100644 --- a/src/value.cc +++ b/src/value.cc @@ -724,7 +724,7 @@ value_t& value_t::operator/=(const value_t& val) return *this; case AMOUNT: if (as_balance().single_amount()) { - in_place_simplify(); + in_place_cast(AMOUNT); as_amount_lval() /= val.as_amount(); return *this; } diff --git a/test/regress/14DB77E7.test b/test/regress/14DB77E7.test new file mode 100644 index 00000000..ee155afb --- /dev/null +++ b/test/regress/14DB77E7.test @@ -0,0 +1,18 @@ +D 1000.00 GBP + +;P 2011-01-01 EUR 0.8604 GBP +P 2011-02-01 EUR 0.8576 GBP + +2011-01-31 * AdSense earnings + Assets:Receivable:AdSense 11.00 EUR + Income:AdSense + +2011-02-28 * AdSense earnings + Assets:Receivable:AdSense 10.00 EUR + Income:AdSense + +test reg income:adse -X GBP -H +11-Jan-31 AdSense earnings Income:AdSense -11.00 EUR -11.00 EUR +11-Feb-01 Commodities revalued -9.43 GBP -9.43 GBP +11-Feb-28 AdSense earnings Income:AdSense -8.58 GBP -18.01 GBP +end test -- cgit v1.2.3 From 363670d35bf451ff8ce636c071da73a0d93c514a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 03:55:25 -0500 Subject: Tighten up argument passing related to fn_market() --- src/amount.cc | 19 +++++------ src/amount.h | 8 +++-- src/annotate.cc | 16 +++++----- src/annotate.h | 6 ++-- src/balance.cc | 4 +-- src/balance.h | 4 +-- src/commodity.cc | 78 ++++++++++++++++++++++------------------------ src/commodity.h | 22 ++++++------- src/history.cc | 55 ++++++++++++++++---------------- src/history.h | 23 +++++++------- src/pool.h | 3 +- src/py_amount.cc | 6 ++-- src/py_balance.cc | 6 ++-- src/py_value.cc | 6 ++-- src/quotes.cc | 2 +- src/quotes.h | 2 +- src/report.cc | 9 +++--- src/value.cc | 16 +++++----- src/value.h | 10 +++--- test/regress/25A099C9.test | 12 +++---- test/unit/t_commodity.cc | 6 ++-- 21 files changed, 153 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 46eb5531..5fa58528 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -728,16 +728,16 @@ void amount_t::in_place_unreduce() } optional -amount_t::value(const optional& moment, - const optional& in_terms_of) const +amount_t::value(const datetime_t& moment, + const commodity_t * in_terms_of) const { if (quantity) { #if defined(DEBUG_ON) DEBUG("commodity.price.find", "amount_t::value of " << commodity().symbol()); - if (moment) + if (! moment.is_not_a_date_time()) DEBUG("commodity.price.find", - "amount_t::value: moment = " << *moment); + "amount_t::value: moment = " << moment); if (in_terms_of) DEBUG("commodity.price.find", "amount_t::value: in_terms_of = " << in_terms_of->symbol()); @@ -745,7 +745,7 @@ amount_t::value(const optional& moment, if (has_commodity() && (in_terms_of || ! commodity().has_flags(COMMODITY_PRIMARY))) { optional point; - optional comm(in_terms_of); + const commodity_t * comm(in_terms_of); if (has_annotation() && annotation().price) { if (annotation().has_flags(ANNOTATION_PRICE_FIXATED)) { @@ -755,7 +755,7 @@ amount_t::value(const optional& moment, "amount_t::value: fixated price = " << point->price); } else if (! comm) { - comm = annotation().price->commodity(); + comm = annotation().price->commodity_ptr(); } } @@ -869,15 +869,10 @@ bool amount_t::fits_in_long() const commodity_t * amount_t::commodity_ptr() const { - return (has_commodity() ? + return (commodity_ ? commodity_ : commodity_pool_t::current_pool->null_commodity); } -commodity_t& amount_t::commodity() const -{ - return *commodity_ptr(); -} - bool amount_t::has_commodity() const { return commodity_ && commodity_ != commodity_->pool().null_commodity; diff --git a/src/amount.h b/src/amount.h index 7bf4fe51..903a01cd 100644 --- a/src/amount.h +++ b/src/amount.h @@ -404,8 +404,8 @@ public: $100.00. */ optional - value(const optional& moment = none, - const optional& in_terms_of = none) const; + value(const datetime_t& moment = datetime_t(), + const commodity_t * in_terms_of = NULL) const; optional price() const; @@ -534,7 +534,9 @@ public: useful for accessing just the numeric portion of an amount. */ commodity_t * commodity_ptr() const; - commodity_t& commodity() const; + commodity_t& commodity() const { + return *commodity_ptr(); + } bool has_commodity() const; void set_commodity(commodity_t& comm) { diff --git a/src/annotate.cc b/src/annotate.cc index d2c7f983..2b118e76 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -241,16 +241,16 @@ bool annotated_commodity_t::operator==(const commodity_t& comm) const } optional -annotated_commodity_t::find_price(const optional& commodity, - const optional& moment, - const optional& oldest) const +annotated_commodity_t::find_price(const commodity_t * commodity, + const datetime_t& moment, + const datetime_t& oldest) const { DEBUG("commodity.price.find", "annotated_commodity_t::find_price(" << symbol() << ")"); datetime_t when; - if (moment) - when = *moment; + if (! moment.is_not_a_date_time()) + when = moment; else if (epoch) when = *epoch; else @@ -258,7 +258,7 @@ annotated_commodity_t::find_price(const optional& commodity, DEBUG("commodity.price.find", "reference time: " << when); - optional target; + const commodity_t * target = NULL; if (commodity) target = commodity; @@ -272,7 +272,7 @@ annotated_commodity_t::find_price(const optional& commodity, } else if (! target) { DEBUG("commodity.price.find", "setting target commodity from price"); - target = details.price->commodity(); + target = details.price->commodity_ptr(); } } @@ -285,7 +285,7 @@ annotated_commodity_t::find_price(const optional& commodity, return find_price_from_expr(const_cast(*details.value_expr), commodity, when); - return commodity_t::find_price(commodity, moment, oldest); + return commodity_t::find_price(target, moment, oldest); } commodity_t& diff --git a/src/annotate.h b/src/annotate.h index 606c6a60..044ebc4d 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -256,9 +256,9 @@ public: } optional - virtual find_price(const optional& commodity = none, - const optional& moment = none, - const optional& oldest = none) const; + virtual find_price(const commodity_t * commodity = NULL, + const datetime_t& moment = datetime_t(), + const datetime_t& oldest = datetime_t()) const; virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); virtual void write_annotations(std::ostream& out, diff --git a/src/balance.cc b/src/balance.cc index 08368dd8..f87e8bbd 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -185,8 +185,8 @@ balance_t& balance_t::operator/=(const amount_t& amt) } optional -balance_t::value(const optional& moment, - const optional& in_terms_of) const +balance_t::value(const datetime_t& moment, + const commodity_t * in_terms_of) const { balance_t temp; bool resolved = false; diff --git a/src/balance.h b/src/balance.h index 921f87ef..5f0d52ed 100644 --- a/src/balance.h +++ b/src/balance.h @@ -384,8 +384,8 @@ public: } optional - value(const optional& moment = none, - const optional& in_terms_of = none) const; + value(const datetime_t& moment = datetime_t(), + const commodity_t * in_terms_of = NULL) const; /** * Truth tests. An balance may be truth test in two ways: diff --git a/src/commodity.cc b/src/commodity.cc index 963fb646..8f0dc100 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -71,12 +71,12 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) } void commodity_t::map_prices(function fn, - const optional& moment, - const optional& _oldest) + const datetime_t& moment, + const datetime_t& _oldest) { datetime_t when; - if (moment) - when = *moment; + if (! moment.is_not_a_date_time()) + when = moment; else if (epoch) when = *epoch; else @@ -86,8 +86,7 @@ void commodity_t::map_prices(function fn, } optional -commodity_t::find_price_from_expr(expr_t& expr, - const optional& commodity, +commodity_t::find_price_from_expr(expr_t& expr, const commodity_t * commodity, const datetime_t& moment) const { #if defined(DEBUG_ON) @@ -114,31 +113,30 @@ commodity_t::find_price_from_expr(expr_t& expr, } optional -commodity_t::find_price(const optional& commodity, - const optional& moment, - const optional& oldest) const +commodity_t::find_price(const commodity_t * commodity, + const datetime_t& moment, + const datetime_t& oldest) const { DEBUG("commodity.price.find", "commodity_t::find_price(" << symbol() << ")"); - optional target; + const commodity_t * target = NULL; if (commodity) target = commodity; else if (pool().default_commodity) - target = *pool().default_commodity; + target = &*pool().default_commodity; - if (target && *this == *target) + if (target && this == target) return none; - optional - entry(base_t::memoized_price_entry(moment, oldest, - commodity ? &(*commodity) : NULL)); + base_t::memoized_price_entry entry(moment, oldest, + commodity ? commodity : NULL); DEBUG("commodity.price.find", "looking for memoized args: " - << (moment ? format_datetime(*moment) : "NONE") << ", " - << (oldest ? format_datetime(*oldest) : "NONE") << ", " + << (! moment.is_not_a_date_time() ? format_datetime(moment) : "NONE") << ", " + << (! oldest.is_not_a_date_time() ? format_datetime(oldest) : "NONE") << ", " << (commodity ? commodity->symbol() : "NONE")); { - base_t::memoized_price_map::iterator i = base->price_map.find(*entry); + base_t::memoized_price_map::iterator i = base->price_map.find(entry); if (i != base->price_map.end()) { DEBUG("commodity.price.find", "found! returning: " << ((*i).second ? (*i).second->price : amount_t(0L))); @@ -147,8 +145,8 @@ commodity_t::find_price(const optional& commodity, } datetime_t when; - if (moment) - when = *moment; + if (! moment.is_not_a_date_time()) + when = moment; else if (epoch) when = *epoch; else @@ -157,40 +155,40 @@ commodity_t::find_price(const optional& commodity, if (base->value_expr) return find_price_from_expr(*base->value_expr, commodity, when); - optional point = - target ? - pool().commodity_price_history.find_price(*this, *target, when, oldest) : - pool().commodity_price_history.find_price(*this, when, oldest); - - if (entry) { - if (base->price_map.size() > base_t::max_price_map_size) { - DEBUG("history.find", - "price map has grown too large, clearing it by half"); - for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) - base->price_map.erase(base->price_map.begin()); - } + optional + point(target ? + pool().commodity_price_history.find_price(*this, *target, + when, oldest) : + pool().commodity_price_history.find_price(*this, when, oldest)); + // Record this price point in the memoization map + if (base->price_map.size() > base_t::max_price_map_size) { DEBUG("history.find", - "remembered: " << (point ? point->price : amount_t(0L))); - base->price_map.insert(base_t::memoized_price_map::value_type(*entry, point)); + "price map has grown too large, clearing it by half"); + for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) + base->price_map.erase(base->price_map.begin()); } + DEBUG("history.find", + "remembered: " << (point ? point->price : amount_t(0L))); + base->price_map.insert(base_t::memoized_price_map::value_type(entry, point)); + return point; } optional commodity_t::check_for_updated_price(const optional& point, - const optional& moment, - const optional& in_terms_of) + const datetime_t& moment, + const commodity_t* in_terms_of) { if (pool().get_quotes && ! has_flags(COMMODITY_NOMARKET)) { bool exceeds_leeway = true; if (point) { time_duration_t::sec_type seconds_diff; - if (moment) { - seconds_diff = (*moment - point->when).total_seconds(); - DEBUG("commodity.download", "moment = " << *moment); + if (! moment.is_not_a_date_time()) { + seconds_diff = (moment - point->when).total_seconds(); + DEBUG("commodity.download", "moment = " << moment); DEBUG("commodity.download", "slip.moment = " << seconds_diff); } else { seconds_diff = (TRUE_CURRENT_TIME() - point->when).total_seconds(); @@ -209,7 +207,7 @@ commodity_t::check_for_updated_price(const optional& point, pool().get_commodity_quote(*this, in_terms_of)) { if (! in_terms_of || (quote->price.has_commodity() && - quote->price.commodity() == *in_terms_of)) + quote->price.commodity_ptr() == in_terms_of)) return quote; } } diff --git a/src/commodity.h b/src/commodity.h index 1358966e..bd1aedb9 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -117,12 +117,12 @@ protected: optional larger; optional value_expr; - typedef tuple, - optional, commodity_t *> memoized_price_entry; + typedef tuple memoized_price_entry; typedef std::map > memoized_price_map; - static const std::size_t max_price_map_size = 16; + static const std::size_t max_price_map_size = 8; mutable memoized_price_map price_map; public: @@ -272,22 +272,22 @@ public: void remove_price(const datetime_t& date, commodity_t& commodity); void map_prices(function fn, - const optional& moment = none, - const optional& _oldest = none); + const datetime_t& moment = datetime_t(), + const datetime_t& _oldest = datetime_t()); optional - find_price_from_expr(expr_t& expr, const optional& commodity, + find_price_from_expr(expr_t& expr, const commodity_t * commodity, const datetime_t& moment) const; optional - virtual find_price(const optional& commodity = none, - const optional& moment = none, - const optional& oldest = none) const; + virtual find_price(const commodity_t * commodity = NULL, + const datetime_t& moment = datetime_t(), + const datetime_t& oldest = datetime_t()) const; optional check_for_updated_price(const optional& point, - const optional& moment, - const optional& in_terms_of); + const datetime_t& moment, + const commodity_t * in_terms_of); commodity_t& nail_down(const expr_t& expr); diff --git a/src/history.cc b/src/history.cc index 83326728..22ac4494 100644 --- a/src/history.cc +++ b/src/history.cc @@ -52,15 +52,15 @@ public: PricePointMap price_point; PriceRatioMap ratios; - datetime_t reftime; - optional oldest; + datetime_t reftime; + datetime_t oldest; recent_edge_weight() { } - recent_edge_weight(EdgeWeightMap _weight, - PricePointMap _price_point, - PriceRatioMap _ratios, - datetime_t _reftime, - const optional& _oldest = none) + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + const datetime_t& _reftime, + const datetime_t& _oldest = datetime_t()) : weight(_weight), price_point(_price_point), ratios(_ratios), reftime(_reftime), oldest(_oldest) { } @@ -69,8 +69,8 @@ public: { #if defined(DEBUG_ON) DEBUG("history.find", " reftime = " << reftime); - if (oldest) { - DEBUG("history.find", " oldest = " << *oldest); + if (! oldest.is_not_a_date_time()) { + DEBUG("history.find", " oldest = " << oldest); } #endif @@ -88,7 +88,7 @@ public: --low; assert(((*low).first <= reftime)); - if (oldest && (*low).first < *oldest) { + if (! oldest.is_not_a_date_time() && (*low).first < oldest) { DEBUG("history.find", " edge is out of range"); return false; } @@ -170,9 +170,9 @@ void commodity_history_t::remove_price(const commodity_t& source, void commodity_history_t::map_prices(function fn, - const commodity_t& source, - const datetime_t& moment, - const optional& oldest) + const commodity_t& source, + const datetime_t& moment, + const datetime_t& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); @@ -193,7 +193,7 @@ void commodity_history_t::map_prices(function= *oldest) && when <= moment) { + if ((oldest.is_not_a_date_time() || when >= oldest) && when <= moment) { if (pair.second.commodity() == source) { amount_t price(pair.second); price.in_place_invert(); @@ -209,9 +209,9 @@ void commodity_history_t::map_prices(function -commodity_history_t::find_price(const commodity_t& source, - const datetime_t& moment, - const optional& oldest) +commodity_history_t::find_price(const commodity_t& source, + const datetime_t& moment, + const datetime_t& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); @@ -270,10 +270,10 @@ commodity_history_t::find_price(const commodity_t& source, } optional -commodity_history_t::find_price(const commodity_t& source, - const commodity_t& target, - const datetime_t& moment, - const optional& oldest) +commodity_history_t::find_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& moment, + const datetime_t& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); @@ -402,17 +402,16 @@ private: Name name; }; -void commodity_history_t::print_map(std::ostream& out, - const optional& moment) +void commodity_history_t::print_map(std::ostream& out, const datetime_t& moment) { - if (moment) { + if (moment.is_not_a_date_time()) { + write_graphviz(out, price_graph, + label_writer(get(vertex_name, price_graph))); + } else { FGraph fg(price_graph, recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, *moment)); + (get(edge_weight, price_graph), pricemap, ratiomap, moment)); write_graphviz(out, fg, label_writer(get(vertex_name, fg))); - } else { - write_graphviz(out, price_graph, - label_writer(get(vertex_name, price_graph))); } } diff --git a/src/history.h b/src/history.h index 920feec6..71cbad0c 100644 --- a/src/history.h +++ b/src/history.h @@ -111,23 +111,22 @@ public: const datetime_t& date); void map_prices(function fn, - const commodity_t& source, - const datetime_t& moment, - const optional& _oldest = none); + const commodity_t& source, + const datetime_t& moment, + const datetime_t& _oldest = datetime_t()); optional - find_price(const commodity_t& source, - const datetime_t& moment, - const optional& oldest = none); + find_price(const commodity_t& source, + const datetime_t& moment, + const datetime_t& oldest = datetime_t()); optional - find_price(const commodity_t& source, - const commodity_t& target, - const datetime_t& moment, - const optional& oldest = none); + find_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& moment, + const datetime_t& oldest = datetime_t()); - void print_map(std::ostream& out, - const optional& moment = none); + void print_map(std::ostream& out, const datetime_t& moment = datetime_t()); }; } // namespace ledger diff --git a/src/pool.h b/src/pool.h index b7921f59..eb630781 100644 --- a/src/pool.h +++ b/src/pool.h @@ -83,13 +83,12 @@ public: bool get_quotes; // --download function - (commodity_t& commodity, const optional& in_terms_of)> + (commodity_t& commodity, const commodity_t * in_terms_of)> get_commodity_quote; static shared_ptr current_pool; explicit commodity_pool_t(); - virtual ~commodity_pool_t() { TRACE_DTOR(commodity_pool_t); } diff --git a/src/py_amount.cc b/src/py_amount.cc index 25ec8e26..ea69dec5 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -48,12 +48,12 @@ namespace { return amount.value(CURRENT_TIME()); } boost::optional py_value_1(const amount_t& amount, - commodity_t& in_terms_of) { + const commodity_t * in_terms_of) { return amount.value(CURRENT_TIME(), in_terms_of); } boost::optional py_value_2(const amount_t& amount, - commodity_t& in_terms_of, - datetime_t& moment) { + const commodity_t * in_terms_of, + const datetime_t& moment) { return amount.value(moment, in_terms_of); } diff --git a/src/py_balance.cc b/src/py_balance.cc index 38941832..2ae546f1 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -48,12 +48,12 @@ namespace { return balance.value(CURRENT_TIME()); } boost::optional py_value_1(const balance_t& balance, - commodity_t& in_terms_of) { + const commodity_t * in_terms_of) { return balance.value(CURRENT_TIME(), in_terms_of); } boost::optional py_value_2(const balance_t& balance, - commodity_t& in_terms_of, - datetime_t& moment) { + const commodity_t * in_terms_of, + const datetime_t& moment) { return balance.value(moment, in_terms_of); } diff --git a/src/py_value.cc b/src/py_value.cc index 78301acd..efeb4340 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -51,12 +51,12 @@ namespace { return value.value(CURRENT_TIME()); } boost::optional py_value_1(const value_t& value, - commodity_t& in_terms_of) { + const commodity_t * in_terms_of) { return value.value(CURRENT_TIME(), in_terms_of); } boost::optional py_value_2(const value_t& value, - commodity_t& in_terms_of, - datetime_t& moment) { + const commodity_t * in_terms_of, + const datetime_t& moment) { return value.value(moment, in_terms_of); } diff --git a/src/quotes.cc b/src/quotes.cc index b29eb8bd..c33e0826 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -40,7 +40,7 @@ namespace ledger { optional commodity_quote_from_script(commodity_t& commodity, - const optional& exchange_commodity) + const commodity_t * exchange_commodity) { DEBUG("commodity.download", "downloading quote for symbol " << commodity.symbol()); #if defined(DEBUG_ON) diff --git a/src/quotes.h b/src/quotes.h index 52092fbc..56740e47 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -46,7 +46,7 @@ namespace ledger { optional commodity_quote_from_script(commodity_t& commodity, - const optional& exchange_commodity); + const commodity_t * exchange_commodity); } // namespace ledger diff --git a/src/report.cc b/src/report.cc index 8205c1dd..a3abcb98 100644 --- a/src/report.cc +++ b/src/report.cc @@ -532,12 +532,13 @@ value_t report_t::fn_should_bold(call_scope_t& scope) value_t report_t::fn_market(call_scope_t& args) { - optional moment = (args.has(1) ? - args.get(1) : - optional()); value_t result; value_t arg0 = args[0]; + datetime_t moment; + if (args.has(1)) + moment = args.get(1); + if (arg0.is_string()) { amount_t tmp(1L); commodity_t * commodity = @@ -962,7 +963,7 @@ value_t report_t::pricemap_command(call_scope_t& args) std::ostream& out(output_stream); commodity_pool_t::current_pool->commodity_price_history.print_map (out, args.has(0) ? - optional(datetime_t(parse_date(args.get(0)))) : none); + datetime_t(parse_date(args.get(0))) : datetime_t()); return true; } diff --git a/src/value.cc b/src/value.cc index c4e7170d..cae2a356 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1399,8 +1399,8 @@ bool value_t::is_zero() const return false; } -value_t value_t::value(const optional& moment, - const optional& in_terms_of) const +value_t value_t::value(const datetime_t& moment, + const commodity_t * in_terms_of) const { switch (type()) { case INTEGER: @@ -1432,9 +1432,9 @@ value_t value_t::value(const optional& moment, return NULL_VALUE; } -value_t value_t::exchange_commodities(const std::string& commodities, - const bool add_prices, - const optional& moment) +value_t value_t::exchange_commodities(const std::string& commodities, + const bool add_prices, + const datetime_t& moment) { if (type() == SEQUENCE) { value_t temp; @@ -1447,7 +1447,7 @@ value_t value_t::exchange_commodities(const std::string& commodities, // expression, skip the expensive logic below. if (commodities.find(',') == string::npos && commodities.find('=') == string::npos) - return value(moment, *commodity_pool_t::current_pool->find_or_create(commodities)); + return value(moment, commodity_pool_t::current_pool->find_or_create(commodities)); std::vector comms; std::vector force; @@ -1479,7 +1479,7 @@ value_t value_t::exchange_commodities(const std::string& commodities, break; DEBUG("commodity.exchange", "Referent doesn't match, pricing..."); - if (optional val = as_amount_lval().value(moment, *comm)) { + if (optional val = as_amount_lval().value(moment, comm)) { DEBUG("commodity.exchange", "Re-priced amount is: " << *val); return *val; } @@ -1502,7 +1502,7 @@ value_t value_t::exchange_commodities(const std::string& commodities, temp += pair.second; } else { DEBUG("commodity.exchange", "Referent doesn't match, pricing..."); - if (optional val = pair.second.value(moment, *comm)) { + if (optional val = pair.second.value(moment, comm)) { DEBUG("commodity.exchange", "Re-priced member amount is: " << *val); temp += *val; repriced = true; diff --git a/src/value.h b/src/value.h index df075843..a95968c2 100644 --- a/src/value.h +++ b/src/value.h @@ -477,12 +477,12 @@ public: void in_place_unreduce(); // exists for efficiency's sake // Return the "market value" of a given value at a specific time. - value_t value(const optional& moment = none, - const optional& in_terms_of = none) const; + value_t value(const datetime_t& moment = datetime_t(), + const commodity_t * in_terms_of = NULL) const; - value_t exchange_commodities(const std::string& commodities, - const bool add_prices = false, - const optional& moment = none); + value_t exchange_commodities(const std::string& commodities, + const bool add_prices = false, + const datetime_t& moment = datetime_t()); /** * Truth tests. diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index c43deb21..418b77c8 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -2,16 +2,16 @@ test -f $sourcepath/src/amount.h reg -> 7 __ERROR__ While parsing file "$sourcepath/src/amount.h", line 66: Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 732: +While parsing file "$sourcepath/src/amount.h", line 734: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 738: +While parsing file "$sourcepath/src/amount.h", line 740: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 744: +While parsing file "$sourcepath/src/amount.h", line 746: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 750: +While parsing file "$sourcepath/src/amount.h", line 752: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 756: +While parsing file "$sourcepath/src/amount.h", line 758: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 763: +While parsing file "$sourcepath/src/amount.h", line 765: Error: Invalid date/time: line std::istream& end test diff --git a/test/unit/t_commodity.cc b/test/unit/t_commodity.cc index 6a6f27aa..8caeb694 100644 --- a/test/unit/t_commodity.cc +++ b/test/unit/t_commodity.cc @@ -92,18 +92,18 @@ BOOST_AUTO_TEST_CASE(testPriceHistory) BOOST_CHECK_EQUAL(string("$2124.122"), amt->to_fullstring()); #endif - amt = x1.value(CURRENT_TIME(), euro); + amt = x1.value(CURRENT_TIME(), &euro); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("EUR 1787.50"), amt->rounded().to_string()); // Add a newer Euro pricing aapl.add_price(jan17_07, amount_t("EUR 23.00")); - amt = x1.value(CURRENT_TIME(), euro); + amt = x1.value(CURRENT_TIME(), &euro); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("EUR 2302.30"), amt->to_string()); - amt = x1.value(CURRENT_TIME(), cad); + amt = x1.value(CURRENT_TIME(), &cad); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("CAD 3223.22"), amt->to_string()); #endif // NOT_FOR_PYTHON -- cgit v1.2.3 From d93c5a7d437a7a85413658ba3672b715077f6eb6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 04:02:25 -0500 Subject: Corrected a typo --- src/global.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/global.cc b/src/global.cc index cc14c99e..d7742161 100644 --- a/src/global.cc +++ b/src/global.cc @@ -193,7 +193,7 @@ void global_scope_t::execute_command(strings_list args, bool at_repl) is_precommand = true; // If it is not a pre-command, then parse the user's ledger data at this - // time if not done alreday (i.e., if not at a REPL). Then patch up the + // time if not done already (i.e., if not at a REPL). Then patch up the // report options based on the command verb. if (! is_precommand) { -- cgit v1.2.3 From fad24d40f7826b4c4ea840f0c068ac43ed50947d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 04:02:52 -0500 Subject: When --percent is used, disable --decimal-comma after parsing Fixes #674 --- src/report.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index a3abcb98..3151b211 100644 --- a/src/report.cc +++ b/src/report.cc @@ -162,13 +162,16 @@ void report_t::normalize_options(const string& verb) terminus); } - if (HANDLED(percent) && HANDLED(market)) { - HANDLER(total_) - .on("?normalize", - "(__tmp = market(parent.total, value_date, exchange);" - " ((is_account & parent & __tmp) ?" - " percent(scrub(market(total, value_date, exchange)), " - " scrub(__tmp)) : 0))"); + if (HANDLED(percent)) { + commodity_t::decimal_comma_by_default = false; + if (HANDLED(market)) { + HANDLER(total_) + .on("?normalize", + "(__tmp = market(parent.total, value_date, exchange);" + " ((is_account & parent & __tmp) ?" + " percent(scrub(market(total, value_date, exchange)), " + " scrub(__tmp)) : 0))"); + } } if (HANDLED(immediate) && HANDLED(market)) { -- cgit v1.2.3 From aebf77d5b4a0fa5c0cc6d194a635cc26e07bb1c3 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 04:27:11 -0500 Subject: Don't clean up at end if not needed --- src/main.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/main.cc b/src/main.cc index dd03206f..5f435d0b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -209,6 +209,7 @@ int main(int argc, char * argv[], char * envp[]) shutdown_memory_tracing(); #endif } else { + global_scope.release(); // let it leak! INFO("Ledger ended"); } -- cgit v1.2.3 From 234348f7fa9018506e740996e8efb5afee283841 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 04:30:04 -0500 Subject: Fixed a bug that was breaking optimized builds --- src/scope.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index 31ef61cb..ccfc750b 100644 --- a/src/scope.h +++ b/src/scope.h @@ -380,13 +380,8 @@ protected: class call_scope_t : public context_scope_t { -#if defined(DEBUG_ON) public: -#endif value_t args; -#if defined(DEBUG_ON) -private: -#endif mutable void * ptr; value_t& resolve(const std::size_t index, -- cgit v1.2.3 From 020de80f6012bb323f11da77e0ca3c8841999491 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 04:55:51 -0500 Subject: Make sure not to free global_scope unless --verify --- src/main.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/main.cc b/src/main.cc index 5f435d0b..e341b526 100644 --- a/src/main.cc +++ b/src/main.cc @@ -80,12 +80,12 @@ int main(int argc, char * argv[], char * envp[]) ::textdomain("ledger"); #endif - unique_ptr global_scope; + global_scope_t * global_scope = NULL; try { // Create the session object, which maintains nearly all state relating to // this invocation of Ledger; and register all known journal parsers. - global_scope.reset(new global_scope_t(envp)); + global_scope = new global_scope_t(envp); global_scope->session().set_flush_on_next_data_file(true); // Construct an STL-style argument list from the process command arguments @@ -94,7 +94,7 @@ int main(int argc, char * argv[], char * envp[]) args.push_back(argv[i]); // Look for options and a command verb in the command-line arguments - bind_scope_t bound_scope(*global_scope.get(), global_scope->report()); + bind_scope_t bound_scope(*global_scope, global_scope->report()); args = global_scope->read_command_arguments(bound_scope, args); if (global_scope->HANDLED(script_)) { @@ -185,7 +185,7 @@ int main(int argc, char * argv[], char * envp[]) } } catch (const std::exception& err) { - if (global_scope.get()) + if (global_scope) global_scope->report_error(err); else std::cerr << "Exception during initialization: " << err.what() @@ -202,15 +202,14 @@ int main(int argc, char * argv[], char * envp[]) // then shutting down the memory tracing subsystem. Otherwise, let it all // leak because we're about to exit anyway. IF_VERIFY() { - global_scope.reset(); + checked_delete(global_scope); INFO("Ledger ended (Boost/libstdc++ may still hold memory)"); #if defined(VERIFY_ON) shutdown_memory_tracing(); #endif } else { - global_scope.release(); // let it leak! - INFO("Ledger ended"); + INFO("Ledger ended"); // let global_scope leak! } // Return the final status to the operating system, either 1 for error or 0 -- cgit v1.2.3 From 62c04deb8719444d1f958d40fe6442b714bb7e50 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 05:08:55 -0500 Subject: Quieted several compiler warnings from Clang --- acprep | 1 + src/option.h | 5 ++--- src/report.cc | 4 +++- src/value.cc | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/acprep b/acprep index 06087572..d3639f54 100755 --- a/acprep +++ b/acprep @@ -1211,6 +1211,7 @@ class PrepareBuild(CommandLineApp): self.CXXFLAGS.append('-Wno-missing-prototypes') self.CXXFLAGS.append('-Wno-missing-noreturn') self.CXXFLAGS.append('-Wno-disabled-macro-expansion') + self.CXXFLAGS.append('-Wno-unused-parameter') self.CXXFLAGS.append('-fno-limit-debug-info') #self.CXXFLAGS.append('-Wold-style-cast') diff --git a/src/option.h b/src/option.h index f892b00e..36dba3a4 100644 --- a/src/option.h +++ b/src/option.h @@ -162,9 +162,8 @@ public: source = none; } - virtual void handler_thunk(const optional& whence) {} - virtual void handler_thunk(const optional& whence, - const string& str) {} + virtual void handler_thunk(const optional&) {} + virtual void handler_thunk(const optional&, const string&) {} value_t handler(call_scope_t& args) { if (wants_arg) { diff --git a/src/report.cc b/src/report.cc index 3151b211..1adbe9d0 100644 --- a/src/report.cc +++ b/src/report.cc @@ -195,7 +195,8 @@ void report_t::normalize_options(const string& verb) long date_width = (HANDLED(date_width_) ? lexical_cast(HANDLER(date_width_).str()) : - format_date(CURRENT_DATE(),FMT_PRINTED).length()); + static_cast + (format_date(CURRENT_DATE(),FMT_PRINTED).length())); long payee_width = (HANDLED(payee_width_) ? lexical_cast(HANDLER(payee_width_).str()) : long(double(cols) * 0.263157)); @@ -825,6 +826,7 @@ value_t report_t::fn_nail_down(call_scope_t& args) throw_(std::runtime_error, _("Attempting to nail down %1") << args[0].label()); } + return arg0; } value_t report_t::fn_lot_date(call_scope_t& args) diff --git a/src/value.cc b/src/value.cc index cae2a356..c14a7104 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1468,7 +1468,7 @@ value_t value_t::exchange_commodities(const std::string& commodities, } } - int index = 0; + std::size_t index = 0; foreach (commodity_t * comm, comms) { switch (type()) { case AMOUNT: -- cgit v1.2.3 From fcc68a0041d0c1334127a880e74b3a4646292cf6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 05:09:06 -0500 Subject: Critical fix to temporary destruction order --- src/chain.h | 3 ++- src/filters.h | 8 ++++++++ src/iterators.h | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/chain.h b/src/chain.h index 080c4231..15ae12ba 100644 --- a/src/chain.h +++ b/src/chain.h @@ -50,8 +50,9 @@ class post_t; class account_t; template -struct item_handler : public noncopyable +class item_handler : public noncopyable { +protected: shared_ptr handler; public: diff --git a/src/filters.h b/src/filters.h index 7be3acb9..af68cd7c 100644 --- a/src/filters.h +++ b/src/filters.h @@ -372,6 +372,7 @@ public: } virtual ~anonymize_posts() { TRACE_DTOR(anonymize_posts); + handler.reset(); } void render_commodity(amount_t& amt); @@ -451,6 +452,7 @@ public: } virtual ~collapse_posts() { TRACE_DTOR(collapse_posts); + handler.reset(); } void create_accounts() { @@ -539,6 +541,7 @@ public: virtual ~display_filter_posts() { TRACE_DTOR(display_filter_posts); + handler.reset(); } void create_accounts() { @@ -595,6 +598,7 @@ public: virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); + handler.reset(); } void create_accounts() { @@ -671,6 +675,7 @@ public: } virtual ~subtotal_posts() { TRACE_DTOR(subtotal_posts); + handler.reset(); } void report_subtotal(const char * spec_fmt = NULL, @@ -849,6 +854,7 @@ public: } virtual ~transfer_details() { TRACE_DTOR(transfer_details); + handler.reset(); } virtual void operator()(post_t& post); @@ -908,6 +914,7 @@ public: virtual ~generate_posts() { TRACE_DTOR(generate_posts); + handler.reset(); } void add_period_xacts(period_xacts_list& period_xacts); @@ -995,6 +1002,7 @@ class inject_posts : public item_handler virtual ~inject_posts() throw() { TRACE_DTOR(inject_posts); + handler.reset(); } virtual void operator()(post_t& post); diff --git a/src/iterators.h b/src/iterators.h index 6d490259..5bb9de6f 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -169,8 +169,8 @@ protected: journal_posts_iterator journal_posts; xacts_iterator xacts; xact_posts_iterator posts; - temporaries_t temps; xacts_list xact_temps; + temporaries_t temps; public: posts_commodities_iterator() {} -- cgit v1.2.3 From a2b6a81caedeebc4df892ed23bf60b1fbe089916 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 05:15:56 -0500 Subject: DO_VERIFY() was always true in optimized builds --- src/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/utils.h b/src/utils.h index 1fe2b378..efa203c7 100644 --- a/src/utils.h +++ b/src/utils.h @@ -171,7 +171,7 @@ void report_memory(std::ostream& out, bool report_all = false); #else // ! VERIFY_ON #define VERIFY(x) -#define DO_VERIFY() true +#define DO_VERIFY() false #define TRACE_CTOR(cls, args) #define TRACE_DTOR(cls) -- cgit v1.2.3 From 8fe68c5fc3a71fa17cf309f9b87b5089ecce8996 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 05:27:13 -0500 Subject: Guarded against unreachable code warnings --- src/main.cc | 5 ++++- src/utils.cc | 3 --- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/main.cc b/src/main.cc index e341b526..dc0798e3 100644 --- a/src/main.cc +++ b/src/main.cc @@ -201,6 +201,7 @@ int main(int argc, char * argv[], char * envp[]) // up everything by closing the session and deleting the session object, and // then shutting down the memory tracing subsystem. Otherwise, let it all // leak because we're about to exit anyway. +#if defined(VERIFY_ON) IF_VERIFY() { checked_delete(global_scope); @@ -208,7 +209,9 @@ int main(int argc, char * argv[], char * envp[]) #if defined(VERIFY_ON) shutdown_memory_tracing(); #endif - } else { + } else +#endif + { INFO("Ledger ended"); // let global_scope leak! } diff --git a/src/utils.cc b/src/utils.cc index eb1d8009..628fb158 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -553,9 +553,6 @@ void logger_func(log_level_t level) #if defined(VERIFY_ON) IF_VERIFY() *_log_stream << " TIME OBJSZ MEMSZ" << std::endl; -#else - IF_VERIFY() - *_log_stream << " TIME" << std::endl; #endif } -- cgit v1.2.3 From fb2e80406fb61eb79a474fc683839c3536e55fb2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 12 Mar 2012 05:17:53 -0500 Subject: Ignore all trailing whitespace in Ledger journals --- src/textual.cc | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index a8cb844b..362bed4d 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -284,20 +284,23 @@ std::streamsize instance_t::read_line(char *& line) std::streamsize len = in.gcount(); if (len > 0) { - if (context.linenum == 0 && utf8::is_bom(context.linebuf)) - line = &context.linebuf[3]; - else - line = context.linebuf; - - if (line[len - 1] == '\r') // strip Windows CRLF down to LF - line[--len] = '\0'; - context.linenum++; context.curr_pos = context.line_beg_pos; context.curr_pos += len; - return len - 1; // LF is being silently dropped + if (context.linenum == 0 && utf8::is_bom(context.linebuf)) { + line = &context.linebuf[3]; + len -= 3; + } else { + line = context.linebuf; + } + + --len; + while (len > 0 && std::isspace(line[len - 1])) // strip trailing whitespace + line[--len] = '\0'; + + return len; } return 0; } @@ -542,7 +545,6 @@ void instance_t::automated_xact_directive(char * line) while (peek_whitespace_line()) { std::streamsize len = read_line(line); - char * p = skip_ws(line); if (! *p) break; @@ -1660,8 +1662,6 @@ bool instance_t::parse_posts(account_t * account, while (peek_whitespace_line()) { char * line; std::streamsize len = read_line(line); - assert(len > 0); - char * p = skip_ws(line); if (*p != ';') { if (post_t * post = parse_post(line, len, account, NULL, defer_expr)) { @@ -1754,7 +1754,6 @@ xact_t * instance_t::parse_xact(char * line, while (peek_whitespace_line()) { len = read_line(line); - char * p = skip_ws(line); if (! *p) break; -- cgit v1.2.3 From ed42a7ebfef0374fb19ae3ecf4fe1f317bb7fd37 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 12 Mar 2012 05:18:14 -0500 Subject: Allow payee names to contain multiple spaces --- src/textual.cc | 25 ++++++++++++++++++++++++- test/regress/6D9066DD.test | 17 +++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/regress/6D9066DD.test (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 362bed4d..ab87a945 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1732,7 +1732,30 @@ xact_t * instance_t::parse_xact(char * line, // Parse the description text if (next && *next) { - char * p = next_element(next, true); + char * p = next; + std::size_t spaces = 0; + std::size_t tabs = 0; + while (*p) { + if (*p == ' ') { + ++spaces; + } + else if (*p == '\t') { + ++tabs; + } + else if (*p == ';' && (tabs > 0 || spaces > 1)) { + char *q = p - 1; + while (q > next && std::isspace(*q)) + --q; + if (q > next) + *(q + 1) = '\0'; + break; + } + else { + spaces = 0; + tabs = 0; + } + ++p; + } xact->payee = context.journal->register_payee(next, xact.get()); next = p; } else { diff --git a/test/regress/6D9066DD.test b/test/regress/6D9066DD.test new file mode 100644 index 00000000..aa885f7d --- /dev/null +++ b/test/regress/6D9066DD.test @@ -0,0 +1,17 @@ +2009/09/23 * (EFT) Elec Ext Deposit AMAZON.COM FZXXOLTQ - Retail dis payments.amazon.com + Assets:Checking $39.05 + Assets:Receivable:Amazon + +2007/10/15 * FOO ; :USA: + Assets:NRL:Checking $1,726.18 + Assets:Receivable:CEG ; [2007/10/05] + +test print +2009/09/23 * (EFT) Elec Ext Deposit AMAZON.COM FZXXOLTQ - Retail dis payments.amazon.com + Assets:Checking $39.05 + Assets:Receivable:Amazon + +2007/10/15 * FOO ; :USA: + Assets:NRL:Checking $1,726.18 + Assets:Receivable:CEG ; [2007/10/05] +end test -- cgit v1.2.3 From cf67fcbd0628e03ac2f788da6f2c082121faf21f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 12 Mar 2012 05:47:32 -0500 Subject: Resolved a huge performance issue with 'pricedb' --- src/report.cc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 1adbe9d0..c4f916d7 100644 --- a/src/report.cc +++ b/src/report.cc @@ -469,8 +469,20 @@ void report_t::commodities_report(post_handler_ptr handler) { handler = chain_handlers(handler, *this); - posts_commodities_iterator walker(*session.journal.get()); - pass_down_posts(handler, walker); + posts_commodities_iterator * walker(new posts_commodities_iterator(*session.journal.get())); + try { + pass_down_posts(handler, *walker); + } + catch (...) { +#if defined(VERIFY_ON) + IF_VERIFY() { + // If --verify was used, clean up the posts_commodities_iterator. + // Otherwise, just leak like a sieve. + checked_delete(walker); + } +#endif + throw; + } session.journal->clear_xdata(); } -- cgit v1.2.3 From be289fba1f3bdb4dc07d5595ad8b46a9309f42f3 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 12 Mar 2012 22:38:36 -0500 Subject: Fixed a few bugs related to apply directives --- src/textual.cc | 89 ++++++++++++++++++++++++++++---------------- test/baseline/dir-apply.dat | 3 ++ test/baseline/dir-apply.test | 34 +++++++++++++++++ 3 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 test/baseline/dir-apply.dat create mode 100644 test/baseline/dir-apply.test (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index ab87a945..7d96310a 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -92,11 +92,30 @@ namespace { return _("textual parser"); } + template + void get_applications(std::vector& result) { + foreach (application_t& state, apply_stack) { + if (state.value.type() == typeid(T)) + result.push_back(boost::get(state.value)); + } + if (parent) + parent->get_applications(result); + } + + template + optional get_application() { + foreach (application_t& state, apply_stack) { + if (state.value.type() == typeid(T)) + return boost::get(state.value); + } + return parent ? parent->get_application() : none; + } + account_t * top_account() { - foreach (application_t& state, apply_stack) - if (state.value.type() == typeid(account_t *)) - return boost::get(state.value); - return NULL; + if (optional acct = get_application()) + return *acct; + else + return NULL; } void parse(); @@ -744,12 +763,15 @@ void instance_t::include_directive(char * line) #endif // BOOST_VERSION >= 103700 if (glob.match(base)) { journal_t * journal = context.journal; - account_t * master = context.master; + account_t * master = top_account(); scope_t * scope = context.scope; std::size_t& errors = context.errors; std::size_t& count = context.count; std::size_t& sequence = context.sequence; + DEBUG("textual.include", "Including: " << *iter); + DEBUG("textual.include", "Master account: " << master->fullname()); + context_stack.push(*iter); context_stack.get_current().journal = journal; @@ -848,15 +870,21 @@ void instance_t::apply_year_directive(char * line) void instance_t::end_apply_directive(char * kind) { - char * b = next_element(kind); - string name(b ? b : " "); + char * b = kind ? next_element(kind) : NULL; + string name(b ? b : ""); - if (apply_stack.size() <= 1) - throw_(std::runtime_error, - _("'end apply %1' found, but no enclosing 'apply %2' directive") - << name << name); + if (apply_stack.size() <= 1) { + if (name.empty()) { + throw_(std::runtime_error, + _("'end' or 'end apply' found, but no enclosing 'apply' directive")); + } else { + throw_(std::runtime_error, + _("'end apply %1' found, but no enclosing 'apply' directive") + << name); + } + } - if (name != " " && name != apply_stack.front().label) + if (! name.empty() && name != apply_stack.front().label) throw_(std::runtime_error, _("'end apply %1' directive does not match 'apply %2' directive") << name << apply_stack.front().label); @@ -1425,15 +1453,14 @@ post_t * instance_t::parse_post(char * line, context.journal->register_commodity(post->amount.commodity(), post.get()); if (! post->amount.has_annotation()) { - foreach (application_t& state, apply_stack) { - if (state.value.type() == typeid(fixed_rate_t)) { - fixed_rate_t& rate(boost::get(state.value)); - if (*rate.first == post->amount.commodity()) { - annotation_t details(rate.second); - details.add_flags(ANNOTATION_PRICE_FIXATED); - post->amount.annotate(details); - break; - } + std::vector rates; + get_applications(rates); + foreach (fixed_rate_t& rate, rates) { + if (*rate.first == post->amount.commodity()) { + annotation_t details(rate.second); + details.add_flags(ANNOTATION_PRICE_FIXATED); + post->amount.annotate(details); + break; } } } @@ -1631,12 +1658,10 @@ post_t * instance_t::parse_post(char * line, post->pos->end_pos = context.curr_pos; post->pos->end_line = context.linenum; - if (! apply_stack.empty()) { - foreach (const application_t& state, apply_stack) - if (state.value.type() == typeid(string)) - post->parse_tags(boost::get(state.value).c_str(), - *context.scope, true); - } + std::vector tags; + get_applications(tags); + foreach (string& tag, tags) + post->parse_tags(tag.c_str(), *context.scope, true); TRACE_STOP(post_details, 1); @@ -1849,12 +1874,10 @@ xact_t * instance_t::parse_xact(char * line, xact->pos->end_pos = context.curr_pos; xact->pos->end_line = context.linenum; - if (! apply_stack.empty()) { - foreach (const application_t& state, apply_stack) - if (state.value.type() == typeid(string)) - xact->parse_tags(boost::get(state.value).c_str(), - *context.scope, false); - } + std::vector tags; + get_applications(tags); + foreach (string& tag, tags) + xact->parse_tags(tag.c_str(), *context.scope, false); TRACE_STOP(xact_details, 1); diff --git a/test/baseline/dir-apply.dat b/test/baseline/dir-apply.dat new file mode 100644 index 00000000..bcdcacf1 --- /dev/null +++ b/test/baseline/dir-apply.dat @@ -0,0 +1,3 @@ +2012-03-12 KFC + Expenses:Food $40 + Assets:Cash diff --git a/test/baseline/dir-apply.test b/test/baseline/dir-apply.test new file mode 100644 index 00000000..7d9e91d9 --- /dev/null +++ b/test/baseline/dir-apply.test @@ -0,0 +1,34 @@ +apply account Master Account + +2012-03-12 KFC + Expenses:Food $20 + Assets:Cash + +end apply account + +apply account Master Account + +2012-03-12 KFC + Expenses:Food $20 + Assets:Cash + +end apply + +apply account Master Account + +2012-03-12 KFC + Expenses:Food $20 + Assets:Cash + +end + +apply account Master Account +include dir-apply.dat +end + +test reg food +12-Mar-12 KFC Master A:Expenses:Food $20 $20 +12-Mar-12 KFC Master A:Expenses:Food $20 $40 +12-Mar-12 KFC Master A:Expenses:Food $20 $60 +12-Mar-12 KFC Master A:Expenses:Food $40 $100 +end test -- cgit v1.2.3 From 098f3d45d723a1fe1fa5f011f65ffd49915cc281 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 12 Mar 2012 22:38:52 -0500 Subject: Fixed speed issue with commodities reports --- src/filters.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/filters.h b/src/filters.h index af68cd7c..1ef92bbe 100644 --- a/src/filters.h +++ b/src/filters.h @@ -156,7 +156,7 @@ public: : item_handler(handler) { TRACE_CTOR(pass_down_posts, "post_handler_ptr, posts_iterator"); - while (post_t * post = *iter++) { + while (post_t * post = *iter) { try { item_handler::operator()(*post); } @@ -164,6 +164,7 @@ public: add_error_context(item_context(*post, _("While handling posting"))); throw; } + iter.increment(); } item_handler::flush(); -- cgit v1.2.3 From 72fc1824d01bb4fe50405ed183afb57b0e129d69 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 12 Mar 2012 23:04:16 -0500 Subject: dijkstra_shortest_paths should never return a reverse path --- src/history.cc | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/history.cc b/src/history.cc index 22ac4494..fcb80d67 100644 --- a/src/history.cc +++ b/src/history.cc @@ -306,23 +306,34 @@ commodity_history_t::find_price(const commodity_t& source, const commodity_t * last_target = ⌖ +#if defined(REVERSE_PREDECESSOR_MAP) typedef tuple results_tuple; std::vector results; bool results_reversed = false; +#endif vertex_descriptor v = tv; for (vertex_descriptor u = predecessorMap[v]; u != v; v = u, u = predecessorMap[v]) { - std::pair edgePair = edge(u, v, fg); - Graph::edge_descriptor edge = edgePair.first; + std::pair edgePair_uv = edge(u, v, fg); + std::pair edgePair_vu = edge(v, u, fg); - const commodity_t * u_comm = get(namemap, u); - const commodity_t * v_comm = get(namemap, v); - const price_point_t& point(get(pricemap, edge)); + Graph::edge_descriptor edge_uv = edgePair_uv.first; + Graph::edge_descriptor edge_vu = edgePair_vu.first; + + const price_point_t& point_uv(get(pricemap, edge_uv)); + const price_point_t& point_vu(get(pricemap, edge_vu)); + const price_point_t& point(point_vu.when > point_uv.when ? + point_vu : point_uv); + + const commodity_t * u_comm = get(namemap, u); + const commodity_t * v_comm = get(namemap, v); + +#if defined(REVERSE_PREDECESSOR_MAP) if (v == tv && u_comm != last_target && v_comm != last_target) results_reversed = true; @@ -336,6 +347,9 @@ commodity_history_t::find_price(const commodity_t& source, const commodity_t * u_comm = edge.get<0>(); const commodity_t * v_comm = edge.get<1>(); const price_point_t& point(*edge.get<2>()); +#else + assert(u_comm == last_target || v_comm == last_target); +#endif bool first_run = false; if (price.is_null()) { -- cgit v1.2.3 From 59cbcf703c407833e4bdb07aa53a8ad6c20c292c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 00:03:56 -0500 Subject: post_t's copy constructor wasn't copying xdata_ --- src/item.h | 2 +- src/post.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/item.h b/src/item.h index 3a9c55bb..e7415918 100644 --- a/src/item.h +++ b/src/item.h @@ -132,7 +132,7 @@ public: TRACE_DTOR(item_t); } - void copy_details(const item_t& item) + virtual void copy_details(const item_t& item) { set_flags(item.flags()); set_state(item.state()); diff --git a/src/post.h b/src/post.h index 7d93b1cc..aecd65cf 100644 --- a/src/post.h +++ b/src/post.h @@ -95,6 +95,7 @@ public: xdata_(post.xdata_) { TRACE_CTOR(post_t, "copy"); + copy_details(post); } virtual ~post_t() { TRACE_DTOR(post_t); @@ -141,6 +142,12 @@ public: std::size_t xact_id() const; std::size_t account_id() const; + virtual void copy_details(const item_t& item) { + const post_t& post(dynamic_cast(item)); + xdata_ = post.xdata_; + item_t::copy_details(item); + } + bool valid() const; struct xdata_t : public supports_flags -- cgit v1.2.3 From dcd83203e6bcc9e861e784c0c1e1dff14b1cf930 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 00:04:31 -0500 Subject: posts_commodities_iterator wasn't setting the journal --- src/iterators.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/iterators.cc b/src/iterators.cc index b994d59a..8ca5922b 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -76,16 +76,19 @@ void journal_posts_iterator::increment() } namespace { - struct create_price_xact { + struct create_price_xact + { + journal_t& journal; account_t * account; temporaries_t& temps; xacts_list& xact_temps; std::map xacts_by_commodity; - create_price_xact(account_t * _account, temporaries_t& _temps, - xacts_list& _xact_temps) - : account(_account), temps(_temps), xact_temps(_xact_temps) {} + create_price_xact(journal_t& _journal, account_t * _account, + temporaries_t& _temps, xacts_list& _xact_temps) + : journal(_journal), account(_account), temps(_temps), + xact_temps(_xact_temps) {} void operator()(datetime_t& date, const amount_t& price) { xact_t * xact; @@ -102,6 +105,7 @@ namespace { xact->_date = date.date(); xacts_by_commodity.insert (std::pair(symbol, xact)); + xact->journal = &journal; } bool post_already_exists = false; @@ -139,7 +143,7 @@ void posts_commodities_iterator::reset(journal_t& journal) foreach (commodity_t * comm, commodities) comm->map_prices - (create_price_xact(journal.master->find_account(comm->symbol()), + (create_price_xact(journal, journal.master->find_account(comm->symbol()), temps, xact_temps)); xacts.reset(xact_temps.begin(), xact_temps.end()); -- cgit v1.2.3 From f35f68823a2054e99ef720ca78cbaf33f4188391 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 00:04:44 -0500 Subject: The anonymizer wasn't properly setting the journal --- src/filters.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 3a975920..9501856e 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -263,6 +263,8 @@ void anonymize_posts::operator()(post_t& post) xact.payee = to_hex(message_digest); xact.note = none; + } else { + xact.journal = post.xact->journal; } std::list account_names; -- cgit v1.2.3 From cb317f9d395f44b2c2fc48f02869c3ed0f5ebcd0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 03:40:16 -0500 Subject: Added format_datetime valexpr function --- src/report.cc | 11 +++++++++++ src/report.h | 1 + 2 files changed, 12 insertions(+) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index c4f916d7..cf227fe6 100644 --- a/src/report.cc +++ b/src/report.cc @@ -756,6 +756,15 @@ value_t report_t::fn_format_date(call_scope_t& args) return string_value(format_date(args.get(0), FMT_PRINTED)); } +value_t report_t::fn_format_datetime(call_scope_t& args) +{ + if (args.has(1)) + return string_value(format_datetime(args.get(0), FMT_CUSTOM, + args.get(1).c_str())); + else + return string_value(format_datetime(args.get(0), FMT_PRINTED)); +} + value_t report_t::fn_ansify_if(call_scope_t& args) { if (args.has(1)) { @@ -1331,6 +1340,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'f': if (is_eq(p, "format_date")) return MAKE_FUNCTOR(report_t::fn_format_date); + else if (is_eq(p, "format_datetime")) + return MAKE_FUNCTOR(report_t::fn_format_datetime); else if (is_eq(p, "format")) return MAKE_FUNCTOR(report_t::fn_format); else if (is_eq(p, "floor")) diff --git a/src/report.h b/src/report.h index a3825335..6322aeb8 100644 --- a/src/report.h +++ b/src/report.h @@ -167,6 +167,7 @@ public: value_t fn_quoted(call_scope_t& scope); value_t fn_join(call_scope_t& scope); value_t fn_format_date(call_scope_t& scope); + value_t fn_format_datetime(call_scope_t& scope); value_t fn_ansify_if(call_scope_t& scope); value_t fn_percent(call_scope_t& scope); value_t fn_commodity(call_scope_t& scope); -- cgit v1.2.3 From cfe8142b2b371dd40867e010b6b6c58fe864ba21 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 03:41:00 -0500 Subject: Access to checkin/checkout of timelog entries --- src/post.cc | 13 ++++++++++++- src/post.h | 16 +++++++++------- src/timelog.cc | 7 +++++++ 3 files changed, 28 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/post.cc b/src/post.cc index babb1292..d5e0d9bc 100644 --- a/src/post.cc +++ b/src/post.cc @@ -351,7 +351,14 @@ namespace { return post.date(); } value_t get_datetime(post_t& post) { - return post.xdata().datetime; + return (! post.xdata().datetime.is_not_a_date_time() ? + post.xdata().datetime : datetime_t(post.date())); + } + value_t get_checkin(post_t& post) { + return post.checkin ? *post.checkin : NULL_VALUE; + } + value_t get_checkout(post_t& post) { + return post.checkout ? *post.checkout : NULL_VALUE; } template @@ -444,6 +451,10 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_is_calculated>); else if (name == "commodity") return WRAP_FUNCTOR(&get_commodity); + else if (name == "checkin") + return WRAP_FUNCTOR(get_wrapper<&get_checkin>); + else if (name == "checkout") + return WRAP_FUNCTOR(get_wrapper<&get_checkout>); break; case 'd': diff --git a/src/post.h b/src/post.h index aecd65cf..d6004c9f 100644 --- a/src/post.h +++ b/src/post.h @@ -61,13 +61,15 @@ public: #define POST_COST_VIRTUAL 0x0400 // cost is virtualized: (@) #define POST_ANONYMIZED 0x0800 // a temporary, anonymous posting - xact_t * xact; // only set for posts of regular xacts - account_t * account; - - amount_t amount; // can be null until finalization - optional amount_expr; - optional cost; - optional assigned_amount; + xact_t * xact; // only set for posts of regular xacts + account_t * account; + + amount_t amount; // can be null until finalization + optional amount_expr; + optional cost; + optional assigned_amount; + optional checkin; + optional checkout; post_t(account_t * _account = NULL, flags_t _flags = ITEM_NORMAL) diff --git a/src/timelog.cc b/src/timelog.cc index b46d3922..00cefe10 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -76,6 +76,11 @@ namespace { (_("Timelog check-out event does not match any current check-ins")); } + if (event.checkin.is_not_a_date_time()) + throw parse_error(_("Timelog check-in has no corresponding check-out")); + if (out_event.checkin.is_not_a_date_time()) + throw parse_error(_("Timelog check-out has no corresponding check-in")); + if (out_event.checkin < event.checkin) throw parse_error (_("Timelog check-out date less than corresponding check-in")); @@ -107,6 +112,8 @@ namespace { post_t * post = new post_t(event.account, amt, POST_VIRTUAL); post->set_state(item_t::CLEARED); post->pos = event.position; + post->checkin = event.checkin; + post->checkout = out_event.checkin; curr->add_post(post); event.account->add_post(post); -- cgit v1.2.3 From 2932e05c1841d7154c83ead89f15a664e09ecbf0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 03:42:11 -0500 Subject: Account valexprs: earliest_checkin, latest_checkout Lets you access the earliest/latest checkin/checkout times for timelog entries in an account. Will be NULL if the account doesn't contain any. --- src/account.cc | 45 ++++++++++++++++++++++++++++++++++++++++++--- src/account.h | 3 +++ 2 files changed, 45 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index d772368c..206e2350 100644 --- a/src/account.cc +++ b/src/account.cc @@ -290,6 +290,26 @@ namespace { return account.self_details().latest_cleared_post; } + value_t get_earliest(account_t& account) + { + return account.self_details().earliest_post; + } + value_t get_earliest_checkin(account_t& account) + { + return (! account.self_details().earliest_checkin.is_not_a_date_time() ? + value_t(account.self_details().earliest_checkin) : NULL_VALUE); + } + + value_t get_latest(account_t& account) + { + return account.self_details().latest_post; + } + value_t get_latest_checkout(account_t& account) + { + return (! account.self_details().latest_checkout.is_not_a_date_time() ? + value_t(account.self_details().latest_checkout) : NULL_VALUE); + } + template value_t get_wrapper(call_scope_t& args) { return (*Func)(args.context()); @@ -362,6 +382,13 @@ expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_depth_spacer>); break; + case 'e': + if (fn_name == "earliest") + return WRAP_FUNCTOR(get_wrapper<&get_earliest>); + else if (fn_name == "earliest_checkin") + return WRAP_FUNCTOR(get_wrapper<&get_earliest_checkin>); + break; + case 'i': if (fn_name == "is_account") return WRAP_FUNCTOR(get_wrapper<&get_true>); @@ -370,10 +397,14 @@ expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, break; case 'l': - if (fn_name == "latest_cleared") - return WRAP_FUNCTOR(get_wrapper<&get_latest_cleared>); - else if (fn_name[1] == '\0') + if (fn_name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_depth>); + else if (fn_name == "latest_cleared") + return WRAP_FUNCTOR(get_wrapper<&get_latest_cleared>); + else if (fn_name == "latest") + return WRAP_FUNCTOR(get_wrapper<&get_latest>); + else if (fn_name == "latest_checkout") + return WRAP_FUNCTOR(get_wrapper<&get_latest_checkout>); break; case 'n': @@ -626,6 +657,14 @@ void account_t::xdata_t::details_t::update(post_t& post, if (! is_valid(latest_post) || post.date() > latest_post) latest_post = post.date(); + if (post.checkin && (earliest_checkin.is_not_a_date_time() || + *post.checkin < earliest_checkin)) + earliest_checkin = *post.checkin; + + if (post.checkout && (latest_checkout.is_not_a_date_time() || + *post.checkout > latest_checkout)) + latest_checkout = *post.checkout; + if (post.state() == item_t::CLEARED) { posts_cleared_count++; diff --git a/src/account.h b/src/account.h index 95e04079..4ddd85e7 100644 --- a/src/account.h +++ b/src/account.h @@ -170,6 +170,9 @@ public: date_t latest_post; date_t latest_cleared_post; + datetime_t earliest_checkin; + datetime_t latest_checkout; + std::set filenames; std::set accounts_referenced; std::set payees_referenced; -- cgit v1.2.3 From c8dd3d28e3ed39ca93f9aac71a127f15c8d65e4b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 03:43:51 -0500 Subject: Added --time-report option This is a rather basic option at the moment which only affects the balance report. I use it as follows, for entering contractor hours into a project planning application, where $1 is the contractor's timelog file, and $2 is the date after which new entries appear in the file: ledger -f $1 balance \ --account=payee \ --time-report \ -d "latest > [$2]" \ --datetime-format='%m/%d/%y %I:%M %p' --- doc/ledger.1 | 3 ++- src/report.cc | 1 + src/report.h | 20 ++++++++++++++++++++ test/baseline/opt-time-report.test | 0 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/baseline/opt-time-report.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 8076c7c4..63017452 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd March 10, 2012 +.Dd March 13, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -415,6 +415,7 @@ appeared in the original journal file. .It Fl \-strict .It Fl \-subtotal Pq Fl s .It Fl \-tail Ar INT +.It Fl \-time-report .It Fl \-total Ar EXPR .It Fl \-total-data Pq Fl J .It Fl \-total-width Ar INT diff --git a/src/report.cc b/src/report.cc index cf227fe6..e93bd6fd 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1224,6 +1224,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(total_data); else OPT(truncate_); else OPT(total_width_); + else OPT(time_report); break; case 'u': OPT(unbudgeted); diff --git a/src/report.h b/src/report.h index 6322aeb8..c0398d4c 100644 --- a/src/report.h +++ b/src/report.h @@ -324,6 +324,7 @@ public: HANDLER(start_of_week_).report(out); HANDLER(subtotal).report(out); HANDLER(tail_).report(out); + HANDLER(time_report).report(out); HANDLER(total_).report(out); HANDLER(total_data).report(out); HANDLER(truncate_).report(out); @@ -947,6 +948,25 @@ public: OPTION(report_t, subtotal); // -s OPTION(report_t, tail_); + OPTION_(report_t, time_report, DO() { + OTHER(balance_format_) + .on(none, + "%(justify(earliest_checkin ? " + " format_datetime(earliest_checkin) : \"\", 19, -1, true)) " + "%(justify(latest_checkout ? " + " format_datetime(latest_checkout) : \"\", 19, -1, true)) " + "%(ansify_if(" + " justify(scrub(display_total), 8," + " 8 + 4 + 19 * 2, true, color), bold if should_bold))" + " %(!options.flat ? depth_spacer : \"\")" + "%-(ansify_if(" + " ansify_if(partial_account(options.flat), blue if color)," + " bold if should_bold))\n%/" + "%$1 %$2 %$3\n%/" + "%(prepend_width ? \" \" * int(prepend_width) : \"\")" + "--------------------------------------------------\n"); + }); + OPTION__ (report_t, total_, // -T DECL1(report_t, total_, merged_expr_t, expr, ("total_expr", "total")) {} diff --git a/test/baseline/opt-time-report.test b/test/baseline/opt-time-report.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From e65fc729bc4d8adaa66b3fd702f93ef790155543 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 10:33:51 -0500 Subject: Made split_cons_expr a global function --- src/op.cc | 42 +++++++++++++++++++++--------------------- src/op.h | 2 ++ 2 files changed, 23 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index 23f47f73..7f2418b1 100644 --- a/src/op.cc +++ b/src/op.cc @@ -48,31 +48,31 @@ void intrusive_ptr_release(const expr_t::op_t * op) op->release(); } -namespace { - value_t split_cons_expr(expr_t::ptr_op_t op) - { - if (op->kind == expr_t::op_t::O_CONS) { - value_t seq; - seq.push_back(expr_value(op->left())); - - expr_t::ptr_op_t next = op->right(); - while (next) { - expr_t::ptr_op_t value_op; - if (next->kind == expr_t::op_t::O_CONS) { - value_op = next->left(); - next = next->has_right() ? next->right() : NULL; - } else { - value_op = next; - next = NULL; - } - seq.push_back(expr_value(value_op)); +value_t split_cons_expr(expr_t::ptr_op_t op) +{ + if (op->kind == expr_t::op_t::O_CONS) { + value_t seq; + seq.push_back(expr_value(op->left())); + + expr_t::ptr_op_t next = op->right(); + while (next) { + expr_t::ptr_op_t value_op; + if (next->kind == expr_t::op_t::O_CONS) { + value_op = next->left(); + next = next->has_right() ? next->right() : NULL; + } else { + value_op = next; + next = NULL; } - return seq; - } else { - return expr_value(op); + seq.push_back(expr_value(value_op)); } + return seq; + } else { + return expr_value(op); } +} +namespace { inline void check_type_context(scope_t& scope, value_t& result) { if (scope.type_required() && diff --git a/src/op.h b/src/op.h index 03fcf816..973ba67f 100644 --- a/src/op.h +++ b/src/op.h @@ -371,6 +371,8 @@ expr_t::op_t::wrap_functor(expr_t::func_t fobj) { string op_context(const expr_t::ptr_op_t op, const expr_t::ptr_op_t locus = NULL); +value_t split_cons_expr(expr_t::ptr_op_t op); + } // namespace ledger #endif // _OP_H -- cgit v1.2.3 From 927d404ad5d145bbcc58e0a4f9ee5d5df0637160 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 10:33:59 -0500 Subject: Added a syntax error check --- src/op.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/op.cc b/src/op.cc index 7f2418b1..8be0796a 100644 --- a/src/op.cc +++ b/src/op.cc @@ -198,6 +198,9 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, } if (! result) { + if (! left()) + throw_(calc_error, _("Syntax error")); + ptr_op_t lhs(left()->compile(*scope_ptr, depth + 1, param_scope)); ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ? (kind == O_LOOKUP ? right() : -- cgit v1.2.3 From a9c387377b796ecfca6d3f222d03e8e2f838f274 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 10:34:16 -0500 Subject: Made join_args() a global function --- src/precmd.cc | 16 ---------------- src/scope.h | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/precmd.cc b/src/precmd.cc index 6b106a8b..fe0836bc 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -43,22 +43,6 @@ namespace ledger { namespace { - string join_args(call_scope_t& args) - { - std::ostringstream buf; - bool first = true; - - for (std::size_t i = 0; i < args.size(); i++) { - if (first) - first = false; - else - buf << ' '; - buf << args[i]; - } - - return buf.str(); - } - post_t * get_sample_xact(report_t& report) { { diff --git a/src/scope.h b/src/scope.h index ccfc750b..134babb2 100644 --- a/src/scope.h +++ b/src/scope.h @@ -658,6 +658,21 @@ call_scope_t::get(std::size_t index, bool) { return args[index].as_any(); } +inline string join_args(call_scope_t& args) { + std::ostringstream buf; + bool first = true; + + for (std::size_t i = 0; i < args.size(); i++) { + if (first) + first = false; + else + buf << ' '; + buf << args[i]; + } + + return buf.str(); +} + class value_scope_t : public child_scope_t { value_t value; -- cgit v1.2.3 From 70941cbd4a64f15df76a327d88e133429f76d4ea Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 10:34:26 -0500 Subject: Added to_string(std::size_t) --- src/utils.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/utils.h b/src/utils.h index efa203c7..2feb73b7 100644 --- a/src/utils.h +++ b/src/utils.h @@ -284,6 +284,12 @@ inline string to_string(long num) { return buf.str(); } +inline string to_string(std::size_t num) { + std::ostringstream buf; + buf << num; + return buf.str(); +} + inline string operator+(const char * left, const string& right) { return string(left) + right; } -- cgit v1.2.3 From c4e942fcb163583e426dc1f65b0afb0bb75c21e3 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 13 Mar 2012 10:34:51 -0500 Subject: Change abbrev_len to int(abbrev_len) --- src/report.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/report.h b/src/report.h index c0398d4c..280b2f48 100644 --- a/src/report.h +++ b/src/report.h @@ -538,7 +538,7 @@ public: " bold if should_bold))" " %(ansify_if(" " ansify_if(justify(truncated(display_account, int(account_width), " - " abbrev_len), int(account_width))," + " int(abbrev_len)), int(account_width))," " blue if color)," " bold if should_bold))" " %(ansify_if(" @@ -886,7 +886,7 @@ public: " bold if should_bold))" " %(ansify_if(" " ansify_if(justify(truncated(display_account, int(account_width), " - " abbrev_len), int(account_width))," + " int(abbrev_len)), int(account_width))," " blue if color)," " bold if should_bold))" " %(ansify_if(" @@ -1027,7 +1027,6 @@ public: OPTION(report_t, total_width_); }; - template Date: Tue, 13 Mar 2012 10:35:08 -0500 Subject: Add experimental support for select queries --- src/report.cc | 3 + src/select.cc | 441 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/select.h | 55 +++++++ tools/Makefile.am | 2 + 4 files changed, 501 insertions(+) create mode 100644 src/select.cc create mode 100644 src/select.h (limited to 'src') diff --git a/src/report.cc b/src/report.cc index e93bd6fd..91de2eb5 100644 --- a/src/report.cc +++ b/src/report.cc @@ -41,6 +41,7 @@ #include "iterators.h" #include "filters.h" #include "precmd.h" +#include "select.h" #include "stats.h" #include "generate.h" #include "draft.h" @@ -1633,6 +1634,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(report_statistics); else if (is_eq(p, "source")) return WRAP_FUNCTOR(source_command); + else if (is_eq(p, "select")) + return WRAP_FUNCTOR(select_command); break; case 'x': diff --git a/src/select.cc b/src/select.cc new file mode 100644 index 00000000..56bd3f2d --- /dev/null +++ b/src/select.cc @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2003-2012, 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. + */ + +#include + +#include "select.h" +#include "journal.h" +#include "account.h" +#include "report.h" +#include "output.h" +#include "print.h" +#include "chain.h" +#include "filters.h" +#include "scope.h" +#include "op.h" + +namespace ledger { + +namespace { + bool get_principal_identifiers(expr_t::ptr_op_t expr, string& ident, + bool do_transforms = false) + { + bool result = true; + + if (expr->is_ident()) { + string name(expr->as_ident()); + if (name == "date" || name == "aux_date" || name == "payee") { + if (! ident.empty() && + ! (name == "date" || name == "aux_date" || name == "payee")) + result = false; + ident = name; + } + else if (name == "account") { + if (! ident.empty() && ! (name == "account")) + result = false; + ident = name; + if (do_transforms) + expr->set_ident("display_account"); + } + else if (name == "amount") { + if (! ident.empty() && ! (name == "amount")) + result = false; + ident = name; + if (do_transforms) + expr->set_ident("display_amount"); + } + else if (name == "total") { + if (! ident.empty() && ! (name == "total")) + result = false; + ident = name; + if (do_transforms) + expr->set_ident("display_total"); + } + } + + if (expr->kind > expr_t::op_t::TERMINALS || expr->is_scope()) { + if (expr->left()) { + if (! get_principal_identifiers(expr->left(), ident, do_transforms)) + result = false; + if (expr->kind > expr_t::op_t::UNARY_OPERATORS && expr->has_right()) + if (! get_principal_identifiers(expr->right(), ident, do_transforms)) + result = false; + } + } + + return result; + } +} + +value_t select_command(call_scope_t& args) +{ + string text = "select " + join_args(args); + if (text.empty()) + throw std::logic_error(_("Usage: select TEXT")); + + report_t& report(find_scope(args)); + + // Our first step is to divide the select statement into its principal + // parts: + // + // SELECT + // FROM + // WHERE + // DISPLAY + // COLLECT + // GROUP BY + // STYLE + + boost::regex select_re + ("(select|from|where|display|collect|group\\s+by|style)\\s+" + "(.+?)" + "(?=(\\s+(from|where|display|collect|group\\s+by|style)\\s+|$))", + boost::regex::perl | boost::regex::icase); + + boost::regex from_accounts_re("from\\s+accounts\\>"); + bool accounts_report = boost::regex_search(text, from_accounts_re); + + boost::sregex_iterator m1(text.begin(), text.end(), select_re); + boost::sregex_iterator m2; + + expr_t::ptr_op_t report_functor; + std::ostringstream formatter; + + while (m1 != m2) { + const boost::match_results& match(*m1); + + string keyword(match[1]); + string arg(match[2]); + + DEBUG("select.parse", "keyword: " << keyword); + DEBUG("select.parse", "arg: " << arg); + + if (keyword == "select") { + expr_t args_expr(arg); + value_t columns(split_cons_expr(args_expr.get_op())); + bool first = true; + string thus_far = ""; + + std::size_t cols = 0; + if (report.HANDLED(columns_)) + cols = lexical_cast(report.HANDLER(columns_).value); + else if (const char * columns_env = std::getenv("COLUMNS")) + cols = lexical_cast(columns_env); + else + cols = 80; + + std::size_t date_width = + (report.HANDLED(date_width_) ? + lexical_cast(report.HANDLER(date_width_).str()) : + static_cast + (format_date(CURRENT_DATE(),FMT_PRINTED).length())); + std::size_t payee_width = + (report.HANDLED(payee_width_) ? + lexical_cast(report.HANDLER(payee_width_).str()) : + std::size_t(double(cols) * 0.263157)); + std::size_t account_width = + (report.HANDLED(account_width_) ? + lexical_cast(report.HANDLER(account_width_).str()) : + std::size_t(double(cols) * 0.302631)); + std::size_t amount_width = + (report.HANDLED(amount_width_) ? + lexical_cast(report.HANDLER(amount_width_).str()) : + std::size_t(double(cols) * 0.157894)); + std::size_t total_width = + (report.HANDLED(total_width_) ? + lexical_cast(report.HANDLER(total_width_).str()) : + amount_width); + std::size_t meta_width = + (report.HANDLED(meta_width_) ? + lexical_cast(report.HANDLER(meta_width_).str()) : + 10); + + bool saw_date = false; + bool saw_payee = false; + bool saw_account = false; + bool saw_amount = false; + bool saw_total = false; + bool saw_meta = false; + + std::size_t cols_needed = 0; + foreach (const value_t& column, columns.to_sequence()) { + string ident; + if (get_principal_identifiers(as_expr(column), ident)) { + if (ident == "date" || ident == "aux_date") { + cols_needed += date_width + 1; + saw_date = true; + } + else if (ident == "payee") { + cols_needed += payee_width + 1; + saw_payee = true; + } + else if (ident == "account") { + cols_needed += account_width + 1; + saw_account = true; + } + else if (ident == "amount") { + cols_needed += amount_width + 1; + saw_amount = true; + } + else if (ident == "total") { + cols_needed += total_width + 1; + saw_total = true; + } + else { + cols_needed += meta_width + 1; + saw_meta = true; + } + } + } + + while ((saw_account || saw_payee) && cols_needed < cols) { + if (saw_account && cols_needed < cols) { + ++account_width; + ++cols_needed; + if (cols_needed < cols) { + ++account_width; + ++cols_needed; + } + } + if (saw_payee && cols_needed < cols) { + ++payee_width; + ++cols_needed; + } + } + + while ((saw_account || saw_payee) && cols_needed > cols && + account_width > 5 && payee_width > 5) { + DEBUG("auto.columns", "adjusting account down"); + if (saw_account && cols_needed > cols) { + --account_width; + --cols_needed; + if (cols_needed > cols) { + --account_width; + --cols_needed; + } + } + if (saw_payee && cols_needed > cols) { + --payee_width; + --cols_needed; + } + DEBUG("auto.columns", "account_width now = " << account_width); + } + + if (! report.HANDLED(date_width_)) + report.HANDLER(date_width_).value = to_string(date_width); + if (! report.HANDLED(payee_width_)) + report.HANDLER(payee_width_).value = to_string(payee_width); + if (! report.HANDLED(account_width_)) + report.HANDLER(account_width_).value = to_string(account_width); + if (! report.HANDLED(amount_width_)) + report.HANDLER(amount_width_).value = to_string(amount_width); + if (! report.HANDLED(total_width_)) + report.HANDLER(total_width_).value = to_string(total_width); + + foreach (const value_t& column, columns.to_sequence()) { + if (first) + first = false; + else + formatter << ' '; + + formatter << "%("; + + string ident; + if (get_principal_identifiers(as_expr(column), ident, true)) { + if (ident == "date" || ident == "aux_date") { + formatter << "ansify_if(" + << "ansify_if(justify(format_date("; + + as_expr(column)->print(formatter); + + formatter << "), int(date_width)),"; + formatter << "green if color and date > today)," + << "bold if should_bold)"; + + if (! thus_far.empty()) + thus_far += " + "; + thus_far += "int(date_width) + 1"; + } + else if (ident == "payee") { + formatter << "ansify_if(" + << "ansify_if(justify(truncated("; + + as_expr(column)->print(formatter); + + formatter << ", int(payee_width)), int(payee_width)),"; + formatter << "bold if color and !cleared and actual)," + << "bold if should_bold)"; + + if (! thus_far.empty()) + thus_far += " + "; + thus_far += "int(payee_width) + 1"; + } + else if (ident == "account") { + formatter << "ansify_if(" + << "ansify_if("; + + if (accounts_report) { + formatter << "partial_account(options.flat), blue if color),"; + } else { + formatter << "justify(truncated("; + as_expr(column)->print(formatter); + formatter << ", int(account_width), int(abbrev_len))," + << "int(account_width)),"; + formatter << "true, color),"; + + if (! thus_far.empty()) + thus_far += " + "; + thus_far += "int(account_width) + 1"; + } + + formatter << " bold if should_bold)"; + } + else if (ident == "amount" || ident == "total") { + formatter << "ansify_if(" + << "justify(scrub("; + + as_expr(column)->print(formatter); + + formatter << "), "; + + if (ident == "amount") + formatter << "int(amount_width),"; + else + formatter << "int(total_width),"; + + if (! thus_far.empty()) + thus_far += " + "; + + if (ident == "amount") + thus_far += "int(amount_width)"; + else + thus_far += "int(total_width)"; + + if (thus_far.empty()) + formatter << "-1"; + else + formatter << thus_far; + + formatter << ", true, color)," + << " bold if should_bold)"; + + thus_far += " + 1"; + } + else { + formatter << "ansify_if(" + << "justify(truncated("; + + as_expr(column)->print(formatter); + + formatter << ", int(meta_width or 10)), int(meta_width) or 10),"; + formatter << "bold if should_bold)"; + + if (! thus_far.empty()) + thus_far += " + "; + thus_far += "(int(meta_width) or 10) + 1"; + } + } + formatter << ")"; + } + formatter << "\\n"; + } + else if (keyword == "from") { + DEBUG("select.parse", "formatter: " << formatter.str()); + + if (arg == "xacts" || arg == "txns" || arg == "transactions") { + report_functor = expr_t::op_t::wrap_functor + (reporter<>(post_handler_ptr(new print_xacts(report, + report.HANDLED(raw))), + report, string("#select"))); + } + else if (arg == "posts" || arg == "postings") { + report_functor = expr_t::op_t::wrap_functor + (reporter<>(post_handler_ptr(new format_posts(report, formatter.str())), + report, string("#select"))); + } + else if (arg == "accounts") { + report_functor = expr_t::op_t::wrap_functor + (reporter + (acct_handler_ptr(new format_accounts(report, formatter.str())), + report, string("#select"))); + } + else if (arg == "commodities") { + report_functor = expr_t::op_t::wrap_functor + (reporter + (post_handler_ptr(new format_posts(report, formatter.str())), + report, string("#select"))); + } + } + else if (keyword == "where") { +#if 0 + query_t query; + keep_details_t keeper(true, true, true); + expr_t::ptr_op_t expr = + query.parse_args(string_value(arg).to_sequence(), keeper, false, true); + report.HANDLER(limit_).on("#select", query.get_query(query_t::QUERY_LIMIT)); +#else + report.HANDLER(limit_).on("#select", arg); +#endif + } + else if (keyword == "display") { + report.HANDLER(display_).on("#select", arg); + } + else if (keyword == "collect") { + report.HANDLER(amount_).on("#select", arg); + } + else if (keyword == "group by") { + report.HANDLER(group_by_).on("#select", arg); + } + else if (keyword == "style") { + if (arg == "csv") { + } + else if (arg == "xml") { + } + else if (arg == "emacs") { + } + else if (arg == "org") { + } + } + + ++m1; + } + + if (! report_functor) { + report_functor = expr_t::op_t::wrap_functor + (reporter<>(post_handler_ptr(new format_posts(report, formatter.str())), + report, string("#select"))); + } + + call_scope_t call_args(report); + return report_functor->as_function()(call_args); +} + +} // namespace ledger diff --git a/src/select.h b/src/select.h new file mode 100644 index 00000000..54883d22 --- /dev/null +++ b/src/select.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2003-2012, 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 select + */ + +/** + * @file select.h + * @author John Wiegley + * + * @ingroup select + */ +#ifndef _SELECT_H +#define _SELECT_H + +#include "utils.h" +#include "value.h" + +namespace ledger { + +class call_scope_t; +value_t select_command(call_scope_t& args); + +} // namespace ledger + +#endif // _SELECT_H diff --git a/tools/Makefile.am b/tools/Makefile.am index c6295aad..5d299b05 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -79,6 +79,7 @@ libledger_report_la_SOURCES = \ src/chain.cc \ src/filters.cc \ src/report.cc \ + src/select.cc \ src/session.cc libledger_report_la_CPPFLAGS = $(lib_cppflags) @@ -128,6 +129,7 @@ pkginclude_HEADERS = \ src/lookup.h \ \ src/session.h \ + src/select.h \ src/report.h \ src/filters.h \ src/chain.h \ -- cgit v1.2.3 From 610a3e170994dc3cd3ae0dc989a49e4e7c7fdadf Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 15 Mar 2012 04:47:32 -0500 Subject: Don't map_prices if price commodity matches source Fixes #680 --- src/commodity.cc | 6 ++++-- src/commodity.h | 3 ++- src/filters.cc | 2 +- src/history.cc | 32 ++++++++++++++++++++++++-------- src/history.h | 3 ++- src/pool.cc | 3 +++ test/regress/786A3DD0.test | 17 +++++++++++++++++ 7 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 test/regress/786A3DD0.test (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index 8f0dc100..0dad9a1a 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -72,7 +72,8 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) void commodity_t::map_prices(function fn, const datetime_t& moment, - const datetime_t& _oldest) + const datetime_t& _oldest, + bool bidirectionally) { datetime_t when; if (! moment.is_not_a_date_time()) @@ -82,7 +83,8 @@ void commodity_t::map_prices(function fn, else when = CURRENT_TIME(); - pool().commodity_price_history.map_prices(fn, *this, when, _oldest); + pool().commodity_price_history.map_prices(fn, *this, when, _oldest, + bidirectionally); } optional diff --git a/src/commodity.h b/src/commodity.h index bd1aedb9..148a3636 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -273,7 +273,8 @@ public: void map_prices(function fn, const datetime_t& moment = datetime_t(), - const datetime_t& _oldest = datetime_t()); + const datetime_t& _oldest = datetime_t(), + bool bidirectionally = false); optional find_price_from_expr(expr_t& expr, const commodity_t * commodity, diff --git a/src/filters.cc b/src/filters.cc index 9501856e..d5cb8ebb 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -776,7 +776,7 @@ void changed_value_posts::output_intermediate_prices(post_t& post, display_total.as_balance().amounts) amt_comm.first->map_prices(insert_prices_in_map(all_prices), datetime_t(current), - datetime_t(post.value_date())); + datetime_t(post.value_date()), true); // Choose the last price from each day as the price to use typedef std::map date_map; diff --git a/src/history.cc b/src/history.cc index fcb80d67..3767c1df 100644 --- a/src/history.cc +++ b/src/history.cc @@ -132,6 +132,8 @@ void commodity_history_t::add_price(const commodity_t& source, const datetime_t& when, const amount_t& price) { + assert(source != price.commodity()); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*price.commodity().graph_index(), price_graph); @@ -153,6 +155,8 @@ void commodity_history_t::remove_price(const commodity_t& source, const commodity_t& target, const datetime_t& date) { + assert(source != target); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); @@ -172,8 +176,11 @@ void commodity_history_t::map_prices(function fn, const commodity_t& source, const datetime_t& moment, - const datetime_t& oldest) + const datetime_t& oldest, + bool bidirectionally) { + DEBUG("history.map", "Mapping prices for source commodity: " << source); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); FGraph fg(price_graph, @@ -193,16 +200,23 @@ void commodity_history_t::map_prices(function= oldest) && when <= moment) { if (pair.second.commodity() == source) { - amount_t price(pair.second); - price.in_place_invert(); - if (source == *get(namemap, sv)) - price.set_commodity(const_cast(*get(namemap, *f_vi))); - else - price.set_commodity(const_cast(*get(namemap, sv))); + if (bidirectionally) { + amount_t price(pair.second); + price.in_place_invert(); + if (source == *get(namemap, sv)) + price.set_commodity(const_cast(*get(namemap, *f_vi))); + else + price.set_commodity(const_cast(*get(namemap, sv))); + DEBUG("history.map", "Inverted price is " << price); + fn(when, price); + } + } else { + fn(when, pair.second); } - fn(when, pair.second); } } } @@ -275,6 +289,8 @@ commodity_history_t::find_price(const commodity_t& source, const datetime_t& moment, const datetime_t& oldest) { + assert(source != target); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); diff --git a/src/history.h b/src/history.h index 71cbad0c..af0d90f9 100644 --- a/src/history.h +++ b/src/history.h @@ -113,7 +113,8 @@ public: void map_prices(function fn, const commodity_t& source, const datetime_t& moment, - const datetime_t& _oldest = datetime_t()); + const datetime_t& _oldest = datetime_t(), + bool bidirectionally = false); optional find_price(const commodity_t& source, diff --git a/src/pool.cc b/src/pool.cc index 0118a97d..d5494352 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -266,6 +266,9 @@ commodity_pool_t::exchange(const amount_t& amount, amount_t per_unit_cost = (is_per_unit || amount.is_realzero()) ? cost.abs() : (cost / amount).abs(); + if (! cost.has_commodity()) + per_unit_cost.clear_commodity(); + DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost); // Do not record commodity exchanges where amount's commodity has a diff --git a/test/regress/786A3DD0.test b/test/regress/786A3DD0.test new file mode 100644 index 00000000..051f6382 --- /dev/null +++ b/test/regress/786A3DD0.test @@ -0,0 +1,17 @@ +D 1000.00 EUR + +2011-02-27 * Australia + A -100.00 AUD @ 0.746 EUR + B + +2012-03-12 * Withdrawal + Assets:Cash USD 200.00 + Expenses:Banking:Fees USD 2.50 + Assets:Chequing CAD -203.42 + Epenses:Banking:Fees CAD 2.00 + Assets:Chqeuing CAD -2.00 + +test pricedb +P 2011/02/27 00:00:00 AUD 0.746 EUR +P 2012/03/12 00:00:00 USD CAD 1.00454320987654321 +end test -- cgit v1.2.3 From a49e33a8ce91e07ebcbbb99bd8f01108d578dcc0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 00:59:32 -0500 Subject: Don't add price exchanges for a commodity itself Fixes #703 --- 95350193.test | 6 ++++++ src/pool.cc | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 95350193.test (limited to 'src') diff --git a/95350193.test b/95350193.test new file mode 100644 index 00000000..dadb39cf --- /dev/null +++ b/95350193.test @@ -0,0 +1,6 @@ +2011-11-08 * Test + Assets:Voucher:Amazon 137.87 GBP (48H5) + Assets:Cash -137.87 GBP + +test pricedb +end test diff --git a/src/pool.cc b/src/pool.cc index d5494352..5813c0f6 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -277,8 +277,10 @@ commodity_pool_t::exchange(const amount_t& amount, if (! per_unit_cost.is_realzero() && (current_annotation == NULL || ! (current_annotation->price && - current_annotation->has_flags(ANNOTATION_PRICE_FIXATED)))) + current_annotation->has_flags(ANNOTATION_PRICE_FIXATED))) && + commodity.referent() != per_unit_cost.commodity().referent()) { exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME()); + } cost_breakdown_t breakdown; breakdown.final_cost = ! is_per_unit ? cost : cost * amount.abs(); -- cgit v1.2.3 From 11067d9cc6593fd19cb44ecd3cd8c31b9b8b0f65 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 02:39:42 -0500 Subject: Unexpected initial whitespace is an error (again) Fixes #565 --- 95350193.test | 6 - lisp/ldg-test.el | 11 +- src/textual.cc | 5 +- test/regress/25A099C9.test | 1196 +++++++++++++++++++++++++++++++++++++++++++- test/regress/8EAF77C0.test | 21 + test/regress/95350193.test | 6 + 6 files changed, 1232 insertions(+), 13 deletions(-) delete mode 100644 95350193.test create mode 100644 test/regress/8EAF77C0.test create mode 100644 test/regress/95350193.test (limited to 'src') diff --git a/95350193.test b/95350193.test deleted file mode 100644 index dadb39cf..00000000 --- a/95350193.test +++ /dev/null @@ -1,6 +0,0 @@ -2011-11-08 * Test - Assets:Voucher:Amazon 137.87 GBP (48H5) - Assets:Cash -137.87 GBP - -test pricedb -end test diff --git a/lisp/ldg-test.el b/lisp/ldg-test.el index 2ab7a0c6..ad612f4d 100644 --- a/lisp/ldg-test.el +++ b/lisp/ldg-test.el @@ -39,8 +39,11 @@ (setq output (concat (or output "") (buffer-substring beg (match-beginning 0))))))) - (find-file-other-window (expand-file-name (concat prefix ".test") - ledger-source-directory)) + (find-file-other-window + (expand-file-name (concat prefix ".test") + (expand-file-name "test/regress" + ledger-source-directory))) + (ledger-mode) (when input (insert input)) (when output @@ -52,9 +55,11 @@ (interactive) (save-excursion (goto-char (point-min)) - (when (re-search-forward "^test \\(.+\\)" nil t) + (when (re-search-forward "^test \\(.+?\\)\\( ->.*\\)?$" nil t) (let ((command (expand-file-name ledger-test-binary)) (args (format "-f \"%s\" %s" buffer-file-name (match-string 1)))) + (setq args (replace-regexp-in-string "\\$sourcepath" + ledger-source-directory args)) (kill-new args) (message "Testing: ledger %s" args) (async-shell-command (format "\"%s\" %s" command args)))))) diff --git a/src/textual.cc b/src/textual.cc index 7d96310a..e7c523d9 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -337,9 +337,8 @@ void instance_t::read_next_directive() break; case ' ': - case '\t': { - break; - } + case '\t': + throw parse_error(_("Unexpected whitespace at beginning of line")); case ';': // comments case '#': diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index 418b77c8..6c0546ee 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -1,17 +1,1211 @@ -test -f $sourcepath/src/amount.h reg -> 7 +test -f $sourcepath/src/amount.h reg -> 92 __ERROR__ +While parsing file "$sourcepath/src/amount.h", line 2: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 3: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 4: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 5: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 6: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 7: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 8: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 9: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 10: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 11: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 12: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 13: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 14: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 15: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 16: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 17: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 18: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 19: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 20: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 21: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 22: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 23: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 24: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 25: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 26: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 27: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 28: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 29: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 30: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 33: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 34: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 37: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 38: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 39: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 40: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 41: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 42: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 43: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 44: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 45: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 46: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 47: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 48: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 49: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 50: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 51: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 52: +Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 66: Error: No quantity specified for amount +While parsing file "$sourcepath/src/amount.h", line 69: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 70: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 71: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 72: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 73: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 74: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 75: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 76: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 77: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 83: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 84: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 85: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 86: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 87: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 88: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 89: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 90: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 91: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 93: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 94: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 95: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 96: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 99: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 100: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 101: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 102: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 103: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 104: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 106: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 108: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 109: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 111: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 112: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 113: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 115: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 116: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 117: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 118: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 121: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 122: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 123: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 124: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 126: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 128: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 129: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 132: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 133: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 135: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 136: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 137: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 138: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 139: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 140: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 142: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 143: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 144: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 146: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 147: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 149: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 150: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 151: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 153: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 154: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 155: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 156: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 157: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 158: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 159: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 160: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 161: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 162: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 163: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 164: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 165: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 166: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 167: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 168: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 169: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 171: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 173: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 174: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 175: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 176: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 177: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 178: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 179: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 180: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 182: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 183: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 184: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 185: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 186: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 187: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 188: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 190: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 191: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 193: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 194: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 195: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 196: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 197: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 198: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 199: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 200: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 201: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 202: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 203: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 204: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 205: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 206: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 207: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 208: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 209: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 210: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 211: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 212: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 213: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 214: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 215: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 216: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 217: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 219: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 220: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 221: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 222: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 223: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 224: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 225: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 226: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 227: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 229: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 230: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 231: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 232: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 233: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 234: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 235: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 236: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 237: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 238: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 240: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 242: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 243: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 245: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 246: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 247: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 248: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 249: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 251: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 252: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 253: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 254: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 256: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 257: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 258: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 259: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 260: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 261: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 262: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 263: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 264: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 265: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 266: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 267: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 269: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 271: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 272: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 273: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 275: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 276: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 277: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 278: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 279: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 280: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 282: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 283: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 284: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 285: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 286: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 287: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 289: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 291: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 292: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 294: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 295: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 296: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 297: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 298: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 299: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 300: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 301: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 302: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 303: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 305: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 306: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 307: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 308: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 309: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 310: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 311: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 312: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 313: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 315: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 316: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 317: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 319: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 320: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 321: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 322: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 323: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 324: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 325: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 326: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 327: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 328: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 330: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 331: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 332: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 333: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 334: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 335: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 337: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 338: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 339: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 340: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 341: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 342: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 343: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 344: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 345: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 346: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 347: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 349: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 350: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 351: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 352: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 353: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 354: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 355: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 356: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 358: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 359: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 360: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 361: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 362: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 363: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 364: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 365: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 367: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 368: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 369: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 370: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 371: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 372: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 373: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 374: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 376: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 377: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 378: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 379: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 380: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 381: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 382: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 383: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 384: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 385: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 387: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 388: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 389: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 390: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 391: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 392: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 393: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 394: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 395: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 396: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 398: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 399: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 400: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 401: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 402: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 403: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 404: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 405: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 406: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 407: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 408: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 410: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 412: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 414: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 415: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 416: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 418: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 420: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 421: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 422: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 423: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 424: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 425: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 427: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 428: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 430: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 431: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 432: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 434: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 435: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 437: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 438: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 439: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 440: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 441: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 443: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 444: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 445: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 446: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 447: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 448: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 450: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 451: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 452: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 453: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 455: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 456: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 457: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 458: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 459: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 460: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 461: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 463: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 465: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 466: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 467: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 469: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 470: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 471: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 472: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 473: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 475: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 476: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 477: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 479: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 480: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 481: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 483: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 484: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 486: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 487: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 488: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 490: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 491: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 493: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 494: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 495: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 496: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 497: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 498: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 499: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 500: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 501: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 503: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 504: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 505: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 506: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 507: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 508: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 510: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 512: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 513: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 514: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 516: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 517: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 519: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 520: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 522: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 524: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 525: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 526: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 527: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 528: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 530: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 531: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 533: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 534: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 535: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 536: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 537: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 538: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 539: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 541: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 542: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 543: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 544: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 545: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 546: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 547: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 548: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 549: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 551: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 552: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 553: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 555: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 556: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 557: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 558: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 560: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 562: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 563: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 564: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 566: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 567: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 568: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 570: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 571: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 572: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 573: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 575: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 576: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 578: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 579: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 580: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 582: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 583: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 584: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 585: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 586: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 588: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 589: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 590: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 591: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 593: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 594: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 596: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 597: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 598: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 599: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 601: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 602: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 603: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 604: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 605: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 606: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 607: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 608: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 610: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 612: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 613: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 614: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 616: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 617: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 619: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 620: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 621: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 622: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 623: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 625: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 626: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 628: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 629: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 630: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 631: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 632: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 633: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 635: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 636: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 637: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 638: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 640: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 641: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 642: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 643: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 645: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 646: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 647: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 649: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 650: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 652: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 654: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 655: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 656: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 657: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 658: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 659: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 660: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 661: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 662: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 663: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 665: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 666: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 668: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 670: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 671: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 672: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 674: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 675: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 676: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 677: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 679: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 680: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 681: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 682: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 683: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 684: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 685: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 686: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 693: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 694: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 696: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 698: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 699: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 700: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 702: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 704: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 705: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 706: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 707: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 709: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 710: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 711: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 712: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 713: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 714: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 715: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 716: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 717: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 719: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 723: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 725: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 727: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 728: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 731: +Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 734: Error: Invalid date/time: line amount_t amoun +While parsing file "$sourcepath/src/amount.h", line 735: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 736: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 737: +Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 740: Error: Invalid date/time: line string amount_ +While parsing file "$sourcepath/src/amount.h", line 741: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 742: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 743: +Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 746: Error: Invalid date/time: line string amount_ +While parsing file "$sourcepath/src/amount.h", line 747: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 748: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 749: +Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 752: Error: Invalid date/time: line string amount_ +While parsing file "$sourcepath/src/amount.h", line 753: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 754: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 755: +Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 758: Error: Invalid date/time: line std::ostream& +While parsing file "$sourcepath/src/amount.h", line 759: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 760: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 761: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 762: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 763: +Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 765: Error: Invalid date/time: line std::istream& +While parsing file "$sourcepath/src/amount.h", line 766: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 767: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/src/amount.h", line 771: +Error: Unexpected whitespace at beginning of line end test diff --git a/test/regress/8EAF77C0.test b/test/regress/8EAF77C0.test new file mode 100644 index 00000000..fd16d642 --- /dev/null +++ b/test/regress/8EAF77C0.test @@ -0,0 +1,21 @@ +2011/08/05 Rehab Donation + Asset:Bank:Boi:Current:Dk 10 + Expense:Misc:Charity + + 2011/08/07 Net Salary + Asset:Bank:Boi:Savings:Dk -3016.24 + Income:NetSalary:Dk + +2011/08/30 Net Salary + Asset:Bank:Boi:Savings:Dk -3016.24 + Income:NetSalary:Dk + +test reg -> 3 +__ERROR__ +While parsing file "$sourcepath/test/regress/8EAF77C0.test", line 5: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/test/regress/8EAF77C0.test", line 6: +Error: Unexpected whitespace at beginning of line +While parsing file "$sourcepath/test/regress/8EAF77C0.test", line 7: +Error: Unexpected whitespace at beginning of line +end test diff --git a/test/regress/95350193.test b/test/regress/95350193.test new file mode 100644 index 00000000..dadb39cf --- /dev/null +++ b/test/regress/95350193.test @@ -0,0 +1,6 @@ +2011-11-08 * Test + Assets:Voucher:Amazon 137.87 GBP (48H5) + Assets:Cash -137.87 GBP + +test pricedb +end test -- cgit v1.2.3 From 49ec40a939ac5cd46f7ea85e03896e89377890d2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 03:09:13 -0500 Subject: pricedb was outputting redundant prices Fixes #704 --- src/history.cc | 2 ++ src/iterators.cc | 2 +- test/regress/8CE88DB4.test | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/regress/8CE88DB4.test (limited to 'src') diff --git a/src/history.cc b/src/history.cc index 3767c1df..f1e88401 100644 --- a/src/history.cc +++ b/src/history.cc @@ -212,9 +212,11 @@ void commodity_history_t::map_prices(function(*get(namemap, sv))); DEBUG("history.map", "Inverted price is " << price); + DEBUG("history.map", "fn(" << when << ", " << price << ")"); fn(when, price); } } else { + DEBUG("history.map", "fn(" << when << ", " << pair.second << ")"); fn(when, pair.second); } } diff --git a/src/iterators.cc b/src/iterators.cc index 8ca5922b..acbb581f 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -138,7 +138,7 @@ void posts_commodities_iterator::reset(journal_t& journal) commodity_t& comm(post->amount.commodity()); if (comm.flags() & COMMODITY_NOMARKET) continue; - commodities.insert(&comm); + commodities.insert(&comm.referent()); } foreach (commodity_t * comm, commodities) diff --git a/test/regress/8CE88DB4.test b/test/regress/8CE88DB4.test new file mode 100644 index 00000000..52fe0a9b --- /dev/null +++ b/test/regress/8CE88DB4.test @@ -0,0 +1,11 @@ +2010-01-01 * Test + Expenses:Food 100.00 EUR + Assets:Cash -100.00 EUR + +2011-07-30 * Exchange EUR to BAM + Assets:Cash -22.00 EUR + Assets:Cash 44.00 BAM + +test pricedb +P 2011/07/30 00:00:00 EUR 2.00 BAM +end test -- cgit v1.2.3 From 20c076dff92806c6c2aae7c0c87b000ffc703382 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 03:50:40 -0500 Subject: Improved error reports about leading whitespace --- src/textual.cc | 17 +- test/regress/25A099C9.test | 1190 +------------------------------------------- test/regress/8EAF77C0.test | 6 +- test/regress/AEDE9734.test | 12 + test/regress/D51BFF74.test | 24 + 5 files changed, 61 insertions(+), 1188 deletions(-) create mode 100644 test/regress/AEDE9734.test create mode 100644 test/regress/D51BFF74.test (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index e7c523d9..3555ea4d 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -133,7 +133,7 @@ namespace { } #endif - void read_next_directive(); + void read_next_directive(bool& error_flag); #if defined(TIMELOG_SUPPORT) void clock_in_directive(char * line, bool capitalized); @@ -251,11 +251,15 @@ void instance_t::parse() context.linenum = 0; context.curr_pos = in.tellg(); + bool error_flag = false; + while (in.good() && ! in.eof()) { try { - read_next_directive(); + read_next_directive(error_flag); } catch (const std::exception& err) { + error_flag = true; + string current_context = error_context(); if (parent) { @@ -324,13 +328,16 @@ std::streamsize instance_t::read_line(char *& line) return 0; } -void instance_t::read_next_directive() +void instance_t::read_next_directive(bool& error_flag) { char * line; std::streamsize len = read_line(line); if (len == 0 || line == NULL) return; + if (! std::isspace(line[0])) + error_flag = false; + switch (line[0]) { case '\0': assert(false); // shouldn't ever reach here @@ -338,7 +345,9 @@ void instance_t::read_next_directive() case ' ': case '\t': - throw parse_error(_("Unexpected whitespace at beginning of line")); + if (! error_flag) + throw parse_error(_("Unexpected whitespace at beginning of line")); + break; case ';': // comments case '#': diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index 6c0546ee..df4fef1c 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -1,1211 +1,43 @@ -test -f $sourcepath/src/amount.h reg -> 92 +test -f $sourcepath/src/amount.h reg -> 20 __ERROR__ While parsing file "$sourcepath/src/amount.h", line 2: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 3: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 4: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 5: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 6: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 7: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 8: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 9: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 10: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 11: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 12: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 13: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 14: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 15: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 16: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 17: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 18: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 19: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 20: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 21: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 22: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 23: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 24: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 25: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 26: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 27: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 28: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 29: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 30: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 33: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 34: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 37: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 38: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 39: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 40: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 41: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 42: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 43: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 44: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 45: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 46: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 47: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 48: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 49: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 50: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 51: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 52: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 66: -Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 69: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 70: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 71: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 72: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 73: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 74: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 75: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 76: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 77: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 83: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 84: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 85: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 86: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 87: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 88: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 89: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 90: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 91: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 93: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 94: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 95: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 96: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 99: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 100: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 101: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 102: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 103: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 104: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 106: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 108: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 109: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 111: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 112: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 113: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 115: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 116: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 117: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 118: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 121: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 122: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 123: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 124: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 126: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 128: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 129: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 132: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 133: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 135: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 136: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 137: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 138: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 139: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 140: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 142: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 143: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 144: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 146: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 147: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 149: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 150: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 151: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 153: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 154: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 155: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 156: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 157: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 158: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 159: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 160: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 161: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 162: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 163: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 164: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 165: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 166: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 167: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 168: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 169: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 171: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 173: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 174: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 175: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 176: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 177: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 178: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 179: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 180: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 182: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 183: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 184: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 185: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 186: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 187: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 188: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 190: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 191: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 193: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 194: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 195: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 196: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 197: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 198: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 199: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 200: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 201: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 202: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 203: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 204: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 205: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 206: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 207: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 208: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 209: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 210: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 211: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 212: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 213: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 214: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 215: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 216: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 217: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 219: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 220: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 221: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 222: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 223: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 224: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 225: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 226: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 227: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 229: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 230: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 231: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 232: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 233: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 234: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 235: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 236: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 237: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 238: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 240: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 242: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 243: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 245: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 246: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 247: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 248: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 249: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 251: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 252: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 253: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 254: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 256: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 257: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 258: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 259: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 260: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 261: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 262: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 263: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 264: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 265: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 266: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 267: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 269: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 271: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 272: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 273: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 275: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 276: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 277: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 278: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 279: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 280: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 282: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 283: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 284: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 285: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 286: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 287: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 289: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 291: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 292: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 294: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 295: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 296: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 297: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 298: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 299: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 300: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 301: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 302: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 303: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 305: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 306: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 307: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 308: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 309: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 310: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 311: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 312: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 313: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 315: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 316: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 317: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 319: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 320: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 321: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 322: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 323: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 324: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 325: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 326: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 327: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 328: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 330: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 331: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 332: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 333: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 334: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 335: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 337: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 338: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 339: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 340: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 341: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 342: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 343: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 344: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 345: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 346: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 347: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 349: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 350: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 351: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 352: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 353: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 354: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 355: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 356: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 358: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 359: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 360: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 361: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 362: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 363: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 364: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 365: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 367: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 368: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 369: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 370: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 371: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 372: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 373: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 374: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 376: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 377: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 378: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 379: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 380: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 381: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 382: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 383: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 384: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 385: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 387: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 388: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 389: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 390: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 391: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 392: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 393: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 394: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 395: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 396: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 398: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 399: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 400: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 401: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 402: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 403: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 404: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 405: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 406: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 407: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 408: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 410: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 412: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 414: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 415: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 416: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 418: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 420: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 421: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 422: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 423: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 424: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 425: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 427: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 428: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 430: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 431: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 432: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 434: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 435: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 437: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 438: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 439: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 440: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 441: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 443: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 444: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 445: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 446: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 447: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 448: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 450: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 451: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 452: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 453: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 455: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 456: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 457: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 458: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 459: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 460: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 461: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 463: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 465: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 466: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 467: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 469: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 470: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 471: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 472: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 473: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 475: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 476: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 477: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 479: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 480: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 481: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 483: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 484: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 486: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 487: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 488: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 490: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 491: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 493: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 494: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 495: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 496: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 497: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 498: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 499: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 500: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 501: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 503: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 504: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 505: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 506: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 507: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 508: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 510: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 512: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 513: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 514: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 516: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 517: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 519: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 520: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 522: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 524: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 525: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 526: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 527: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 528: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 530: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 531: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 533: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 534: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 535: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 536: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 537: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 538: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 539: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 541: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 542: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 543: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 544: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 545: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 546: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 547: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 548: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 549: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 551: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 552: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 553: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 555: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 556: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 557: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 558: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 560: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 562: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 563: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 564: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 566: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 567: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 568: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 570: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 571: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 572: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 573: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 575: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 576: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 578: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 579: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 580: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 582: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 583: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 584: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 585: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 586: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 588: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 589: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 590: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 591: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 593: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 594: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 596: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 597: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 598: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 599: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 601: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 602: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 603: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 604: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 605: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 606: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 607: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 608: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 610: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 612: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 613: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 614: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 616: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 617: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 619: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 620: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 621: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 622: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 623: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 625: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 626: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 628: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 629: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 630: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 631: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 632: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 633: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 635: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 636: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 637: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 638: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 640: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 641: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 642: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 643: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 645: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 646: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 647: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 649: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 650: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 652: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 654: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 655: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 656: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 657: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 658: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 659: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 660: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 661: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 662: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 663: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 665: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 666: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 668: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 670: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 671: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 672: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 674: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 675: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 676: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 677: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 679: +While parsing file "$sourcepath/src/amount.h", line 33: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 680: +While parsing file "$sourcepath/src/amount.h", line 37: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 681: +While parsing file "$sourcepath/src/amount.h", line 66: +Error: No quantity specified for amount +While parsing file "$sourcepath/src/amount.h", line 69: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 682: +While parsing file "$sourcepath/src/amount.h", line 83: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 683: +While parsing file "$sourcepath/src/amount.h", line 93: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 684: +While parsing file "$sourcepath/src/amount.h", line 99: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 685: +While parsing file "$sourcepath/src/amount.h", line 121: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 686: +While parsing file "$sourcepath/src/amount.h", line 132: Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 693: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 694: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 696: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 698: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 699: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 700: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 702: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 704: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 705: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 706: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 707: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 709: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 710: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 711: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 712: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 713: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 714: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 715: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 716: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 717: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 719: -Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 723: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 725: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 727: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 728: -Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 731: Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 734: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 735: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 736: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 737: -Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 740: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 741: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 742: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 743: -Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 746: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 747: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 748: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 749: -Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 752: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 753: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 754: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 755: -Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 758: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 759: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 760: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 761: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 762: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 763: -Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 765: Error: Invalid date/time: line std::istream& -While parsing file "$sourcepath/src/amount.h", line 766: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 767: -Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 771: Error: Unexpected whitespace at beginning of line end test diff --git a/test/regress/8EAF77C0.test b/test/regress/8EAF77C0.test index fd16d642..fd73197e 100644 --- a/test/regress/8EAF77C0.test +++ b/test/regress/8EAF77C0.test @@ -10,12 +10,8 @@ Asset:Bank:Boi:Savings:Dk -3016.24 Income:NetSalary:Dk -test reg -> 3 +test reg -> 1 __ERROR__ While parsing file "$sourcepath/test/regress/8EAF77C0.test", line 5: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/test/regress/8EAF77C0.test", line 6: -Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/test/regress/8EAF77C0.test", line 7: -Error: Unexpected whitespace at beginning of line end test diff --git a/test/regress/AEDE9734.test b/test/regress/AEDE9734.test new file mode 100644 index 00000000..cd2245b8 --- /dev/null +++ b/test/regress/AEDE9734.test @@ -0,0 +1,12 @@ +2011-02-23 Rocket Fuel + Expense:Travel $100000000.00 ; trip: Moon + Asset:NASA + +2011-02-23 Liquid Oxygen + Expense:Travel $232233223.00 ; trip: Moon + Asset:NASA + +test bal --group-by "tag('trip')" +Moon + $332233223.00 Expense:Travel +end test diff --git a/test/regress/D51BFF74.test b/test/regress/D51BFF74.test new file mode 100644 index 00000000..dc472215 --- /dev/null +++ b/test/regress/D51BFF74.test @@ -0,0 +1,24 @@ +2012-03-16 KFC + Expenses:Food $-20 + Assets:Cash + +2012-03-16 KFC + Expenses:Food $- 20 + Assets:Cash + +2012-03-16 KFC + Expenses:Food -$20 + Assets:Cash + +2012-03-16 KFC + Expenses:Food - $20 + Assets:Cash + +test reg -> 1 +__ERROR__ +While parsing file "$sourcepath/test/regress/D51BFF74.test", line 6: +While parsing posting: + Expenses:Food $- 20 + ^^^^^ +Error: No quantity specified for amount +end test -- cgit v1.2.3 From 7462d09b214f2497d9d41a24f4fa8a4dd1577aba Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 04:09:21 -0500 Subject: Correct parsing of automated xact expressions Fixes #458 --- lisp/ldg-test.el | 3 ++- src/query.cc | 2 +- test/regress/605A410D.test | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 test/regress/605A410D.test (limited to 'src') diff --git a/lisp/ldg-test.el b/lisp/ldg-test.el index ad612f4d..77e03026 100644 --- a/lisp/ldg-test.el +++ b/lisp/ldg-test.el @@ -57,7 +57,8 @@ (goto-char (point-min)) (when (re-search-forward "^test \\(.+?\\)\\( ->.*\\)?$" nil t) (let ((command (expand-file-name ledger-test-binary)) - (args (format "-f \"%s\" %s" buffer-file-name (match-string 1)))) + (args (format "--args-only --columns=80 --no-color -f \"%s\" %s" + buffer-file-name (match-string 1)))) (setq args (replace-regexp-in-string "\\$sourcepath" ledger-source-directory args)) (kill-new args) diff --git a/src/query.cc b/src/query.cc index 8bdabb38..3fec708a 100644 --- a/src/query.cc +++ b/src/query.cc @@ -146,7 +146,7 @@ query_t::lexer_t::next_token(query_t::lexer_t::token_t::kind_t tok_context) case '\t': case '\n': case '\r': - if (! multiple_args && ! consume_whitespace) + if (! multiple_args && ! consume_whitespace && ! consume_next_arg) goto test_ident; else ident.push_back(*arg_i); diff --git a/test/regress/605A410D.test b/test/regress/605A410D.test new file mode 100644 index 00000000..6943939a --- /dev/null +++ b/test/regress/605A410D.test @@ -0,0 +1,32 @@ += expr amount > 500 and account =~ /Employer:One/ + (Virtual) 1 + +2012-01-16 KFC + Employer:One $1,000.00 + Assets:Cash + += expr amount>500 and account =~ /Employer:Two/ + (Virtual) 10 + +2012-02-16 KFC + Employer:Two $1,000.00 + Assets:Cash + += Employer:Three and expr amount>500 + (Virtual) 100 + +2012-03-16 KFC + Employer:Three $1,000.00 + Assets:Cash + +test reg +12-Jan-16 KFC Employer:One $1,000.00 $1,000.00 + Assets:Cash $-1,000.00 0 + (Virtual) $1,000.00 $1,000.00 +12-Feb-16 KFC Employer:Two $1,000.00 $2,000.00 + Assets:Cash $-1,000.00 $1,000.00 + (Virtual) $10,000.00 $11,000.00 +12-Mar-16 KFC Employer:Three $1,000.00 $12,000.00 + Assets:Cash $-1,000.00 $11,000.00 + (Virtual) $100,000.00 $111,000.00 +end test -- cgit v1.2.3 From 2605d8a711348dd677a99749b155ed2d6085cd59 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 04:29:35 -0500 Subject: Improved error reporting for bad account names Fixes #374 --- src/xact.cc | 23 +++++++++++++++++++++-- test/regress/5D92A5EB.test | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 test/regress/5D92A5EB.test (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index 4e8e56fa..f514776f 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -111,6 +111,14 @@ value_t xact_base_t::magnitude() const return halfbal; } +namespace { + inline bool account_ends_with_special_char(const string& name) { + string::size_type len(name.length()); + return (std::isdigit(name[len - 1]) || name[len - 1] == ')' || + name[len - 1] == '}' || name[len - 1] == ']'); + } +} + bool xact_base_t::finalize() { // Scan through and compute the total balance for the xact. This is used @@ -136,8 +144,19 @@ bool xact_base_t::finalize() p.rounded().reduced() : p.reduced()); } else if (null_post) { - throw_(std::logic_error, - _("Only one posting with null amount allowed per transaction")); + bool post_account_bad = + account_ends_with_special_char(post->account->fullname()); + bool null_post_account_bad = + account_ends_with_special_char(null_post->account->fullname()); + + if (post_account_bad || null_post_account_bad) + throw_(std::logic_error, + _("Posting with null amount's account may be mispelled:\n \"%1\"") + << (post_account_bad ? post->account->fullname() : + null_post->account->fullname())); + else + throw_(std::logic_error, + _("Only one posting with null amount allowed per transaction")); } else { null_post = post; diff --git a/test/regress/5D92A5EB.test b/test/regress/5D92A5EB.test new file mode 100644 index 00000000..1d3a06bb --- /dev/null +++ b/test/regress/5D92A5EB.test @@ -0,0 +1,34 @@ +~ Monthly from 2010/7/1 + Expenses:Auto:Gas $100.00 + Expenses:Auto:Insurance $100.00 + Expenses:Childcare $100.00 + Expenses:Entertainment:Blizzard $100.00 + Expenses:Entertainment:Netflix $100.00 + Expenses:Groceries $100.00 + Expenses:Utilities:Electric $100.00 + Expenses:Utilities:Water $100.00 + Expenses:Utilities:Sewage $100.00 + Liabilities:Education:ULL $100.00 + Liabilities:Mortgage $100.00 + Assets:Bank:Checking + +test -J reg checking -> 1 +__ERROR__ +While parsing file "$sourcepath/test/regress/5D92A5EB.test", line 13: +While parsing periodic transaction: +> ~ Monthly from 2010/7/1 +> Expenses:Auto:Gas $100.00 +> Expenses:Auto:Insurance $100.00 +> Expenses:Childcare $100.00 +> Expenses:Entertainment:Blizzard $100.00 +> Expenses:Entertainment:Netflix $100.00 +> Expenses:Groceries $100.00 +> Expenses:Utilities:Electric $100.00 +> Expenses:Utilities:Water $100.00 +> Expenses:Utilities:Sewage $100.00 +> Liabilities:Education:ULL $100.00 +> Liabilities:Mortgage $100.00 +> Assets:Bank:Checking +Error: Posting with null amount's account may be mispelled: + "Expenses:Entertainment:Blizzard $100.00" +end test -- cgit v1.2.3 From 630585599786427e25d9758638ff3b7d74680b4f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 05:30:34 -0500 Subject: Don't allow trailing ws in abbrev. account segments Fixes #358 --- lisp/ldg-post.el | 2 +- src/format.cc | 12 +++++++++++- test/baseline/opt-payee-as-account.test | 4 ++-- test/regress/6E041C52.test | 8 ++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 test/regress/6E041C52.test (limited to 'src') diff --git a/lisp/ldg-post.el b/lisp/ldg-post.el index da4a2806..05b9d352 100644 --- a/lisp/ldg-post.el +++ b/lisp/ldg-post.el @@ -4,7 +4,7 @@ "" :group 'ledger) -(defcustom ledger-post-auto-adjust-amounts t +(defcustom ledger-post-auto-adjust-amounts nil "If non-nil, ." :type 'boolean :group 'ledger-post) diff --git a/src/format.cc b/src/format.cc index 79f94869..8c3cbc14 100644 --- a/src/format.cc +++ b/src/format.cc @@ -599,6 +599,7 @@ string format_t::truncate(const unistring& ustr, index = 0; #endif std::size_t counter = lens.size(); + std::list::iterator x = parts.begin(); for (std::list::iterator i = lens.begin(); i != lens.end(); i++) { @@ -628,12 +629,21 @@ string format_t::truncate(const unistring& ustr, if (adjust > 0) { DEBUG("format.abbrev", "Reducing segment " << ++index << " by " << adjust << " chars"); + while (std::isspace((*x)[*i - adjust - 1]) && adjust < *i) { + DEBUG("format.abbrev", + "Segment ends in whitespace, adjusting down"); + ++adjust; + } (*i) -= adjust; DEBUG("format.abbrev", "Segment " << index << " is now " << *i << " chars wide"); - overflow -= adjust; + if (adjust > overflow) + overflow = 0; + else + overflow -= adjust; DEBUG("format.abbrev", "Overflow is now " << overflow << " chars"); } + ++x; } DEBUG("format.abbrev", "Overflow ending this time at " << overflow << " chars"); diff --git a/test/baseline/opt-payee-as-account.test b/test/baseline/opt-payee-as-account.test index 113a395b..0d1f87d6 100644 --- a/test/baseline/opt-payee-as-account.test +++ b/test/baseline/opt-payee-as-account.test @@ -21,11 +21,11 @@ test reg --account=payee 08-Jan-01 January January:Expenses:Books $10.00 $10.00 08-Jan-01 January January:Assets:Cash $-10.00 0 -08-Jan-31 End of January End of :Expenses:Books $10.00 $10.00 +08-Jan-31 End of January End of:Expenses:Books $10.00 $10.00 08-Jan-31 End of January End of Jan:Assets:Cash $-10.00 0 08-Feb-01 February Februar:Expenses:Books $20.00 $20.00 08-Feb-01 February February:Assets:Cash $-20.00 0 -08-Feb-28 End of February End of :Expenses:Books $20.00 $20.00 +08-Feb-28 End of February End of:Expenses:Books $20.00 $20.00 08-Feb-28 End of February End of Feb:Assets:Cash $-20.00 0 08-Mar-01 March March:Expenses:Books $30.00 $30.00 08-Mar-01 March March:Assets:Cash $-30.00 0 diff --git a/test/regress/6E041C52.test b/test/regress/6E041C52.test new file mode 100644 index 00000000..0a56dd70 --- /dev/null +++ b/test/regress/6E041C52.test @@ -0,0 +1,8 @@ +2012-03-16 KFC + Expenses:E of March: End of April $100.00 + Assets:Cash + +test reg +12-Mar-16 KFC Ex:E of: End of April $100.00 $100.00 + Assets:Cash $-100.00 0 +end test -- cgit v1.2.3 From 00ff141ee75f854d39e78fbf6eda5869a3c4a70f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 15:31:30 -0500 Subject: Always close the last report output stream Should fix #701 --- src/global.h | 5 +++++ src/main.cc | 1 + src/report.h | 4 ++++ 3 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/global.h b/src/global.h index 392b03a9..0c11e025 100644 --- a/src/global.h +++ b/src/global.h @@ -56,6 +56,11 @@ public: global_scope_t(char ** envp); ~global_scope_t(); + void quick_close() { + if (! report_stack.empty()) + report_stack.front().quick_close(); + } + virtual string description() { return _("global scope"); } diff --git a/src/main.cc b/src/main.cc index dc0798e3..9d2ba311 100644 --- a/src/main.cc +++ b/src/main.cc @@ -212,6 +212,7 @@ int main(int argc, char * argv[], char * envp[]) } else #endif { + global_scope->quick_close(); INFO("Ledger ended"); // let global_scope leak! } diff --git a/src/report.h b/src/report.h index 280b2f48..aca4f466 100644 --- a/src/report.h +++ b/src/report.h @@ -125,6 +125,10 @@ public: output_stream.close(); } + void quick_close() { + output_stream.close(); + } + virtual string description() { return _("current report"); } -- cgit v1.2.3 From 22c1b867f03e4a495f9e47c899d1fdfcaec3a659 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 17 Mar 2012 05:27:43 -0500 Subject: Added option --day-break --- doc/ledger.1 | 3 +- src/journal.cc | 1 + src/journal.h | 1 + src/session.cc | 3 ++ src/session.h | 2 + src/textual.cc | 3 +- src/timelog.cc | 101 ++++++++++++++++++++++++++------------- src/timelog.h | 2 +- test/baseline/opt-day-break.test | 12 +++++ 9 files changed, 91 insertions(+), 37 deletions(-) create mode 100644 test/baseline/opt-day-break.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 63017452..cd76d5b0 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd March 13, 2012 +.Dd March 17, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -300,6 +300,7 @@ See .It Fl \-date-format Ar DATEFMT Pq Fl y .It Fl \-datetime-format Ar FMT .It Fl \-date-width Ar INT +.It Fl \-day-break .It Fl \-dc .It Fl \-debug Ar STR .It Fl \-decimal-comma diff --git a/src/journal.cc b/src/journal.cc index 37eacdaf..be6a8e51 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -94,6 +94,7 @@ void journal_t::initialize() was_loaded = false; force_checking = false; check_payees = false; + day_break = false; checking_style = CHECK_PERMISSIVE; } diff --git a/src/journal.h b/src/journal.h index ca73c415..759826a0 100644 --- a/src/journal.h +++ b/src/journal.h @@ -130,6 +130,7 @@ public: bool was_loaded; bool force_checking; bool check_payees; + bool day_break; payee_mappings_t payee_mappings; account_mappings_t account_mappings; accounts_map account_aliases; diff --git a/src/session.cc b/src/session.cc index 76061de7..5c9e4fd4 100644 --- a/src/session.cc +++ b/src/session.cc @@ -105,6 +105,8 @@ std::size_t session_t::read_data(const string& master_account) journal->force_checking = true; if (HANDLED(check_payees)) journal->check_payees = true; + if (HANDLED(day_break)) + journal->day_break = true; if (HANDLED(permissive)) journal->checking_style = journal_t::CHECK_PERMISSIVE; @@ -320,6 +322,7 @@ option_t * session_t::lookup_option(const char * p) case 'd': OPT(download); // -Q else OPT(decimal_comma); + else OPT(day_break); break; case 'e': OPT(explicit); diff --git a/src/session.h b/src/session.h index 962664ef..a0aba91b 100644 --- a/src/session.h +++ b/src/session.h @@ -97,6 +97,7 @@ public: { HANDLER(cache_).report(out); HANDLER(check_payees).report(out); + HANDLER(day_break).report(out); HANDLER(download).report(out); HANDLER(decimal_comma).report(out); HANDLER(file_).report(out); @@ -122,6 +123,7 @@ public: OPTION(session_t, cache_); OPTION(session_t, check_payees); + OPTION(session_t, day_break); OPTION(session_t, download); // -Q OPTION_(session_t, decimal_comma, DO() { diff --git a/src/textual.cc b/src/textual.cc index 3555ea4d..8856af5d 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -486,8 +486,7 @@ void instance_t::clock_out_directive(char * line, bool /*capitalized*/) n ? n : "", end ? end : ""); - timelog.clock_out(event); - context.count++; + context.count += timelog.clock_out(event); } #endif // TIMELOG_SUPPORT diff --git a/src/timelog.cc b/src/timelog.cc index 00cefe10..e84e4188 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -41,9 +41,43 @@ namespace ledger { namespace { - void clock_out_from_timelog(std::list& time_xacts, - time_xact_t out_event, - parse_context_t& context) + void create_timelog_xact(const time_xact_t& in_event, + const time_xact_t& out_event, + parse_context_t& context) + { + unique_ptr curr(new xact_t); + curr->_date = in_event.checkin.date(); + curr->code = out_event.desc; // if it wasn't used above + curr->payee = in_event.desc; + curr->pos = in_event.position; + + if (! in_event.note.empty()) + curr->append_note(in_event.note.c_str(), *context.scope); + + char buf[32]; + std::sprintf(buf, "%lds", long((out_event.checkin - in_event.checkin) + .total_seconds())); + amount_t amt; + amt.parse(buf); + VERIFY(amt.valid()); + + post_t * post = new post_t(in_event.account, amt, POST_VIRTUAL); + post->set_state(item_t::CLEARED); + post->pos = in_event.position; + post->checkin = in_event.checkin; + post->checkout = out_event.checkin; + curr->add_post(post); + in_event.account->add_post(post); + + if (! context.journal->add_xact(curr.get())) + throw parse_error(_("Failed to record 'out' timelog transaction")); + else + curr.release(); + } + + std::size_t clock_out_from_timelog(std::list& time_xacts, + time_xact_t out_event, + parse_context_t& context) { time_xact_t event; @@ -93,34 +127,35 @@ namespace { if (! out_event.note.empty() && event.note.empty()) event.note = out_event.note; - unique_ptr curr(new xact_t); - curr->_date = event.checkin.date(); - curr->code = out_event.desc; // if it wasn't used above - curr->payee = event.desc; - curr->pos = event.position; - - if (! event.note.empty()) - curr->append_note(event.note.c_str(), *context.scope); - - char buf[32]; - std::sprintf(buf, "%lds", long((out_event.checkin - event.checkin) - .total_seconds())); - amount_t amt; - amt.parse(buf); - VERIFY(amt.valid()); - - post_t * post = new post_t(event.account, amt, POST_VIRTUAL); - post->set_state(item_t::CLEARED); - post->pos = event.position; - post->checkin = event.checkin; - post->checkout = out_event.checkin; - curr->add_post(post); - event.account->add_post(post); - - if (! context.journal->add_xact(curr.get())) - throw parse_error(_("Failed to record 'out' timelog transaction")); - else - curr.release(); + if (! context.journal->day_break) { + create_timelog_xact(event, out_event, context); + return 1; + } else { + time_xact_t begin(event); + std::size_t xact_count = 0; + + while (begin.checkin < out_event.checkin) { + DEBUG("timelog", "begin.checkin: " << begin.checkin); + datetime_t days_end(begin.checkin.date(), time_duration_t(23, 59, 59)); + days_end += seconds(1); + DEBUG("timelog", "days_end: " << days_end); + + if (out_event.checkin <= days_end) { + create_timelog_xact(begin, out_event, context); + ++xact_count; + break; + } else { + time_xact_t end(out_event); + end.checkin = days_end; + DEBUG("timelog", "end.checkin: " << end.checkin); + create_timelog_xact(begin, end, context); + ++xact_count; + + begin.checkin = end.checkin; + } + } + return xact_count; + } } } // unnamed namespace @@ -155,12 +190,12 @@ void time_log_t::clock_in(time_xact_t event) time_xacts.push_back(event); } -void time_log_t::clock_out(time_xact_t event) +std::size_t time_log_t::clock_out(time_xact_t event) { if (time_xacts.empty()) throw std::logic_error(_("Timelog check-out event without a check-in")); - clock_out_from_timelog(time_xacts, event, context); + return clock_out_from_timelog(time_xacts, event, context); } } // namespace ledger diff --git a/src/timelog.h b/src/timelog.h index ed5a2d36..857952ff 100644 --- a/src/timelog.h +++ b/src/timelog.h @@ -98,7 +98,7 @@ public: } void clock_in(time_xact_t event); - void clock_out(time_xact_t event); + std::size_t clock_out(time_xact_t event); void close(); }; diff --git a/test/baseline/opt-day-break.test b/test/baseline/opt-day-break.test new file mode 100644 index 00000000..18dde546 --- /dev/null +++ b/test/baseline/opt-day-break.test @@ -0,0 +1,12 @@ +i 05/10/2011 08:58:37 682 +o 05/12/2011 11:25:21 + +test reg --base +11-May-10 (682) 181604s 181604s +end test + +test reg --base --day-break +11-May-10 (682) 54083s 54083s +11-May-11 (682) 86400s 140483s +11-May-12 (682) 41121s 181604s +end test -- cgit v1.2.3 From f96a205c03c177a474db3040e4eb27d482271b8f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 17 Mar 2012 05:55:11 -0500 Subject: Make --days-of-week an alias for --dow --- src/report.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 91de2eb5..bff068b8 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1113,7 +1113,7 @@ option_t * report_t::lookup_option(const char * p) else OPT_(display_); else OPT(display_amount_); else OPT(display_total_); - else OPT_ALT(dow, days-of-week); + else OPT_ALT(dow, days_of_week); else OPT(date_width_); break; case 'e': -- cgit v1.2.3 From bc31e4838bae16748352901634d264af9b045369 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 17 Mar 2012 07:02:03 -0500 Subject: Fixed #711: Parsing of alias directive --- src/textual.cc | 12 ++++-------- test/regress/1384C1D8.test | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 test/regress/1384C1D8.test (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 8856af5d..0cb7fb81 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -982,27 +982,23 @@ void instance_t::account_alias_directive(account_t * account, string alias) // (account), add a reference to the account in the `account_aliases' // map, which is used by the post parser to resolve alias references. trim(alias); -#if defined(DEBUG_ON) std::pair result = -#endif context.journal->account_aliases.insert (accounts_map::value_type(alias, account)); -#if defined(DEBUG_ON) - assert(result.second); -#endif + if (! result.second) + (*result.first).second = account; } void instance_t::alias_directive(char * line) { - char * b = next_element(line); - if (char * e = std::strchr(b, '=')) { + if (char * e = std::strchr(line, '=')) { char * z = e - 1; while (std::isspace(*z)) *z-- = '\0'; *e++ = '\0'; e = skip_ws(e); - account_alias_directive(top_account()->find_account(e), b); + account_alias_directive(top_account()->find_account(e), line); } } diff --git a/test/regress/1384C1D8.test b/test/regress/1384C1D8.test new file mode 100644 index 00000000..77a07b7a --- /dev/null +++ b/test/regress/1384C1D8.test @@ -0,0 +1,27 @@ +@alias OLD1 = NEW1 + +2012-01-01 Something + OLD1 $10.00 + Other + +!alias OLD2 = NEW2 + +2012-01-01 Something + OLD2 $10.00 + Other + +account NEW3 + alias OLD3 + +2012-01-01 Something + OLD3 $10.00 + Other + +test bal + $10.00 NEW1 + $10.00 NEW2 + $10.00 NEW3 + $-30.00 Other +-------------------- + 0 +end test -- cgit v1.2.3 From 09cf0cbb2ae3690b357fc04d6f80476b8d60ce37 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 17 Mar 2012 23:14:17 -0500 Subject: Change some #if guards to test for gcc 4.7 --- src/draft.cc | 4 ++-- src/filters.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/draft.cc b/src/draft.cc index 74a6f4d2..7edf7edc 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -109,12 +109,12 @@ void draft_t::parse_args(const value_t& args) } else if (check_for_date && bool(weekday = string_to_day_of_week(what[0]))) { -#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif short dow = static_cast(*weekday); -#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #pragma GCC diagnostic pop #endif date_t date = CURRENT_DATE() - date_duration(1); diff --git a/src/filters.cc b/src/filters.cc index d5cb8ebb..6fedd7ce 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -835,7 +835,7 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, foreach (post_t * post, component_posts) { date_t date = post->date(); date_t value_date = post->value_date(); -#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif @@ -843,7 +843,7 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, range_start = date; if (! range_finish || value_date > *range_finish) range_finish = value_date; -#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #pragma GCC diagnostic pop #endif } -- cgit v1.2.3 From e7d26d53cfc73cc706a6b4903f5c5d6641bdeae8 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 18 Mar 2012 00:14:49 -0500 Subject: Whitespace fix --- src/expr.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/expr.h b/src/expr.h index 590bdc15..645b5cf9 100644 --- a/src/expr.h +++ b/src/expr.h @@ -162,7 +162,6 @@ public: : expr_t(), term(_term), base_expr(expr), merge_operator(merge_op) { TRACE_CTOR(merged_expr_t, "string, string, string"); } - virtual ~merged_expr_t() { TRACE_DTOR(merged_expr_t); } -- cgit v1.2.3 From f9088f88360019bb4be8743dd8091036502adb9c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 18 Mar 2012 01:01:30 -0500 Subject: Added --verify-memory and missing TRACE_[CD]TOR calls --- doc/ledger.1 | 3 +- src/account.h | 11 +++- src/accum.h | 11 +++- src/commodity.h | 4 +- src/csv.h | 4 ++ src/draft.h | 16 ++++- src/global.cc | 21 +++++-- src/global.h | 1 + src/history.cc | 7 ++- src/iterators.cc | 8 ++- src/iterators.h | 103 ++++++++++++++++++++++++++++---- src/main.cc | 1 + src/pstream.h | 5 ++ src/py_journal.cc | 5 +- src/report.cc | 7 ++- src/report.h | 23 +++++++- src/scope.h | 14 ++++- src/temps.h | 4 ++ src/utils.cc | 110 ++++++++++++++++++++++++++--------- test/baseline/opt-verify-memory.test | 0 20 files changed, 299 insertions(+), 59 deletions(-) create mode 100644 test/baseline/opt-verify-memory.test (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index cd76d5b0..9cfa41c0 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd March 17, 2012 +.Dd March 18, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -431,6 +431,7 @@ appeared in the original journal file. .It Fl \-value-expr Ar EXPR .It Fl \-verbose .It Fl \-verify +.It Fl \-verify-memory .It Fl \-version .It Fl \-weekly Pq Fl W .It Fl \-wide Pq Fl w diff --git a/src/account.h b/src/account.h index 4ddd85e7..fee12595 100644 --- a/src/account.h +++ b/src/account.h @@ -189,7 +189,16 @@ public: posts_cleared_count(0), posts_last_7_count(0), posts_last_30_count(0), - posts_this_month_count(0) {} + posts_this_month_count(0) { + TRACE_CTOR(account_t::xdata_t::details_t, ""); + } + // A copy copies nothing + details_t(const details_t&) { + TRACE_CTOR(account_t::xdata_t::details_t, "copy"); + } + ~details_t() throw() { + TRACE_DTOR(account_t::xdata_t::details_t); + } details_t& operator+=(const details_t& other); diff --git a/src/accum.h b/src/accum.h index dde93c30..628a6b36 100644 --- a/src/accum.h +++ b/src/accum.h @@ -51,7 +51,12 @@ protected: std::string::size_type index; public: - straccbuf() : index(0) {} + straccbuf() : index(0) { + TRACE_CTOR(straccbuf, ""); + } + ~straccbuf() throw() { + TRACE_DTOR(straccbuf); + } protected: virtual std::streamsize xsputn(const char * s, std::streamsize num); @@ -66,8 +71,12 @@ protected: public: straccstream() : std::ostream(0) { + TRACE_CTOR(straccstream, ""); rdbuf(&buf); } + ~straccstream() throw() { + TRACE_DTOR(straccstream); + } void clear() { std::ostream::clear(); diff --git a/src/commodity.h b/src/commodity.h index 148a3636..ba47a572 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -132,10 +132,10 @@ protected: static_cast(COMMODITY_STYLE_DECIMAL_COMMA) : static_cast(COMMODITY_STYLE_DEFAULTS)), symbol(_symbol), precision(0) { - TRACE_CTOR(base_t, "const string&"); + TRACE_CTOR(commodity_t::base_t, "const string&"); } virtual ~base_t() { - TRACE_DTOR(base_t); + TRACE_DTOR(commodity_t::base_t); } #if defined(HAVE_BOOST_SERIALIZATION) diff --git a/src/csv.h b/src/csv.h index 24ea9121..d98c0567 100644 --- a/src/csv.h +++ b/src/csv.h @@ -91,8 +91,12 @@ public: cost_mask("cost"), total_mask("total"), note_mask("note") { + TRACE_CTOR(csv_reader, "parse_context_t&"); read_index(*context.stream.get()); } + ~csv_reader() { + TRACE_DTOR(csv_reader); + } void read_index(std::istream& in); string read_field(std::istream& in); diff --git a/src/draft.h b/src/draft.h index 41485731..e5d29134 100644 --- a/src/draft.h +++ b/src/draft.h @@ -68,12 +68,22 @@ class draft_t : public expr_base_t optional cost_operator; optional cost; - post_template_t() : from(false) {} + post_template_t() : from(false) { + TRACE_CTOR(post_template_t, ""); + } + ~post_template_t() throw() { + TRACE_DTOR(post_template_t); + } }; std::list posts; - xact_template_t() {} + xact_template_t() { + TRACE_CTOR(xact_template_t, ""); + } + ~xact_template_t() throw() { + TRACE_DTOR(xact_template_t); + } void dump(std::ostream& out) const; }; @@ -86,7 +96,7 @@ public: if (! args.empty()) parse_args(args); } - virtual ~draft_t() { + virtual ~draft_t() throw() { TRACE_DTOR(draft_t); } diff --git a/src/global.cc b/src/global.cc index d7742161..b5ceb614 100644 --- a/src/global.cc +++ b/src/global.cc @@ -272,6 +272,7 @@ void global_scope_t::report_options(report_t& report, std::ostream& out) HANDLER(trace_).report(out); HANDLER(verbose).report(out); HANDLER(verify).report(out); + HANDLER(verify_memory).report(out); out << std::endl << "[Session scope options]" << std::endl; report.session.report_options(out); @@ -315,6 +316,7 @@ option_t * global_scope_t::lookup_option(const char * p) case 'v': OPT_(verbose); else OPT(verify); + else OPT(verify_memory); else OPT(version); break; } @@ -452,29 +454,36 @@ void handle_debug_options(int argc, char * argv[]) if (std::strcmp(argv[i], "--args-only") == 0) { args_only = true; } + else if (std::strcmp(argv[i], "--verify-memory") == 0) { +#if defined(VERIFY_ON) + verify_enabled = true; + + _log_level = LOG_DEBUG; + _log_category = "memory\\.counts"; +#endif + } else if (std::strcmp(argv[i], "--verify") == 0) { #if defined(VERIFY_ON) - verify_enabled = true; // global in utils.h + verify_enabled = true; #endif } else if (std::strcmp(argv[i], "--verbose") == 0 || std::strcmp(argv[i], "-v") == 0) { #if defined(LOGGING_ON) - _log_level = LOG_INFO; // global in utils.h + _log_level = LOG_INFO; #endif } else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) { #if defined(DEBUG_ON) - _log_level = LOG_DEBUG; // global in utils.h - _log_category = argv[i + 1]; // global in utils.h + _log_level = LOG_DEBUG; + _log_category = argv[i + 1]; i++; #endif } else if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) { #if defined(TRACING_ON) - _log_level = LOG_TRACE; // global in utils.h + _log_level = LOG_TRACE; try { - // global in utils.h _trace_level = boost::lexical_cast(argv[i + 1]); } catch (const boost::bad_lexical_cast&) { diff --git a/src/global.h b/src/global.h index 0c11e025..5786bb89 100644 --- a/src/global.h +++ b/src/global.h @@ -158,6 +158,7 @@ See LICENSE file included with the distribution for details and disclaimer."); OPTION(global_scope_t, trace_); OPTION(global_scope_t, verbose); OPTION(global_scope_t, verify); + OPTION(global_scope_t, verify_memory); OPTION_(global_scope_t, version, DO() { // -v parent->show_version_info(std::cout); diff --git a/src/history.cc b/src/history.cc index f1e88401..d94ec647 100644 --- a/src/history.cc +++ b/src/history.cc @@ -423,7 +423,12 @@ commodity_history_t::find_price(const commodity_t& source, template class label_writer { public: - label_writer(Name _name) : name(_name) {} + label_writer(Name _name) : name(_name) { + TRACE_CTOR(label_writer, "Name"); + } + ~label_writer() throw() { + TRACE_DTOR(label_writer); + } template void operator()(std::ostream& out, const VertexOrEdge& v) const { diff --git a/src/iterators.cc b/src/iterators.cc index acbb581f..7cc1291a 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -88,7 +88,13 @@ namespace { create_price_xact(journal_t& _journal, account_t * _account, temporaries_t& _temps, xacts_list& _xact_temps) : journal(_journal), account(_account), temps(_temps), - xact_temps(_xact_temps) {} + xact_temps(_xact_temps) { + TRACE_CTOR(create_price_xact, + "journal_t&, account_t *, temporaries_t&, xacts_list&"); + } + ~create_price_xact() throw() { + TRACE_DTOR(create_price_xact); + } void operator()(datetime_t& date, const amount_t& price) { xact_t * xact; diff --git a/src/iterators.h b/src/iterators.h index 5bb9de6f..922ebccd 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -58,7 +58,15 @@ class iterator_facade_base typedef Value node_base; public: - iterator_facade_base() : m_node(NULL) {} + iterator_facade_base() : m_node(NULL) { + TRACE_CTOR(iterator_facade_base, ""); + } + iterator_facade_base(const iterator_facade_base& i) : m_node(i.m_node) { + TRACE_CTOR(iterator_facade_base, "copy"); + } + ~iterator_facade_base() throw() { + TRACE_DTOR(iterator_facade_base); + } explicit iterator_facade_base(node_base p) : m_node(p) {} @@ -87,12 +95,24 @@ class xact_posts_iterator bool posts_uninitialized; public: - xact_posts_iterator() : posts_uninitialized(true) {} + xact_posts_iterator() : posts_uninitialized(true) { + TRACE_CTOR(xact_posts_iterator, ""); + } xact_posts_iterator(xact_t& xact) : posts_uninitialized(true) { + TRACE_CTOR(xact_posts_iterator, "xact_t&"); reset(xact); } - ~xact_posts_iterator() throw() {} + xact_posts_iterator(const xact_posts_iterator& i) + : iterator_facade_base(i), + posts_i(i.posts_i), posts_end(i.posts_end), + posts_uninitialized(i.posts_uninitialized) { + TRACE_CTOR(xact_posts_iterator, "copy"); + } + ~xact_posts_iterator() throw() { + TRACE_DTOR(xact_posts_iterator); + } void reset(xact_t& xact) { posts_i = xact.posts.begin(); @@ -121,15 +141,28 @@ public: bool xacts_uninitialized; - xacts_iterator() : xacts_uninitialized(true) {} + xacts_iterator() : xacts_uninitialized(true) { + TRACE_CTOR(xacts_iterator, ""); + } xacts_iterator(journal_t& journal) : xacts_uninitialized(false) { + TRACE_CTOR(xacts_iterator, "journal_t&"); reset(journal); } xacts_iterator(xacts_list::iterator beg, xacts_list::iterator end) : xacts_uninitialized(false) { + TRACE_CTOR(xacts_iterator, "xacts_list::iterator, xacts_list::iterator"); reset(beg, end); } - ~xacts_iterator() throw() {} + xacts_iterator(const xacts_iterator& i) + : iterator_facade_base(i), + xacts_i(i.xacts_i), xacts_end(i.xacts_end), + xacts_uninitialized(i.xacts_uninitialized) { + TRACE_CTOR(xacts_iterator, "copy"); + } + ~xacts_iterator() throw() { + TRACE_DTOR(xacts_iterator); + } void reset(journal_t& journal); @@ -150,11 +183,22 @@ class journal_posts_iterator xact_posts_iterator posts; public: - journal_posts_iterator() {} + journal_posts_iterator() { + TRACE_CTOR(journal_posts_iterator, ""); + } journal_posts_iterator(journal_t& journal) { + TRACE_CTOR(journal_posts_iterator, "journal_t&"); reset(journal); } - ~journal_posts_iterator() throw() {} + journal_posts_iterator(const journal_posts_iterator& i) + : iterator_facade_base(i), + xacts(i.xacts), posts(i.posts) { + TRACE_CTOR(journal_posts_iterator, "copy"); + } + ~journal_posts_iterator() throw() { + TRACE_DTOR(journal_posts_iterator); + } void reset(journal_t& journal); @@ -173,11 +217,23 @@ protected: temporaries_t temps; public: - posts_commodities_iterator() {} + posts_commodities_iterator() { + TRACE_CTOR(posts_commodities_iterator, ""); + } posts_commodities_iterator(journal_t& journal) { + TRACE_CTOR(posts_commodities_iterator, "journal_t&"); reset(journal); } - ~posts_commodities_iterator() throw() {} + posts_commodities_iterator(const posts_commodities_iterator& i) + : iterator_facade_base(i), + journal_posts(i.journal_posts), xacts(i.xacts), posts(i.posts), + xact_temps(i.xact_temps), temps(i.temps) { + TRACE_CTOR(posts_commodities_iterator, "copy"); + } + ~posts_commodities_iterator() throw() { + TRACE_DTOR(posts_commodities_iterator); + } void reset(journal_t& journal); @@ -192,12 +248,23 @@ class basic_accounts_iterator std::list accounts_end; public: - basic_accounts_iterator() {} + basic_accounts_iterator() { + TRACE_CTOR(basic_accounts_iterator, ""); + } basic_accounts_iterator(account_t& account) { + TRACE_CTOR(basic_accounts_iterator, "account_t&"); push_back(account); increment(); } - ~basic_accounts_iterator() throw() {} + basic_accounts_iterator(const basic_accounts_iterator& i) + : iterator_facade_base(i), + accounts_i(i.accounts_i), accounts_end(i.accounts_end) { + TRACE_CTOR(basic_accounts_iterator, "copy"); + } + ~basic_accounts_iterator() throw() { + TRACE_DTOR(basic_accounts_iterator); + } void increment(); @@ -225,10 +292,22 @@ public: sorted_accounts_iterator(account_t& account, const expr_t& _sort_cmp, bool _flatten_all) : sort_cmp(_sort_cmp), flatten_all(_flatten_all) { + TRACE_CTOR(sorted_accounts_iterator, "account_t&, expr_t, bool"); push_back(account); increment(); } - ~sorted_accounts_iterator() throw() {} + sorted_accounts_iterator(const sorted_accounts_iterator& i) + : iterator_facade_base(i), + sort_cmp(i.sort_cmp), flatten_all(i.flatten_all), + accounts_list(i.accounts_list), + sorted_accounts_i(i.sorted_accounts_i), + sorted_accounts_end(i.sorted_accounts_end) { + TRACE_CTOR(sorted_accounts_iterator, "copy"); + } + ~sorted_accounts_iterator() throw() { + TRACE_DTOR(sorted_accounts_iterator); + } void increment(); diff --git a/src/main.cc b/src/main.cc index 9d2ba311..0130d5c6 100644 --- a/src/main.cc +++ b/src/main.cc @@ -58,6 +58,7 @@ int main(int argc, char * argv[], char * envp[]) // --verbose ; turns on logging // --debug CATEGORY ; turns on debug logging // --trace LEVEL ; turns on trace logging + // --memory ; turns on memory usage tracing handle_debug_options(argc, argv); #if defined(VERIFY_ON) IF_VERIFY() initialize_memory_tracing(); diff --git a/src/pstream.h b/src/pstream.h index a894325d..dfb27056 100644 --- a/src/pstream.h +++ b/src/pstream.h @@ -58,6 +58,8 @@ class ptristream : public std::istream public: ptrinbuf(char * _ptr, std::size_t _len) : ptr(_ptr), len(_len) { + TRACE_CTOR(ptrinbuf, "char *, std::size_t"); + if (*ptr && len == 0) len = std::strlen(ptr); @@ -65,6 +67,9 @@ class ptristream : public std::istream ptr, // read position ptr+len); // end position } + ~ptrinbuf() throw() { + TRACE_DTOR(ptrinbuf); + } protected: virtual int_type underflow() { diff --git a/src/py_journal.cc b/src/py_journal.cc index 550fb14e..50a52be9 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -151,8 +151,11 @@ namespace { collector_wrapper(journal_t& _journal, report_t& base) : journal(_journal), report(base), - posts_collector(new collect_posts) {} + posts_collector(new collect_posts) { + TRACE_CTOR(collector_wrapper, "journal_t&, report_t&"); + } ~collector_wrapper() { + TRACE_DTOR(collector_wrapper); journal.clear_xdata(); } diff --git a/src/report.cc b/src/report.cc index bff068b8..28836d0f 100644 --- a/src/report.cc +++ b/src/report.cc @@ -321,7 +321,12 @@ namespace { report_t& report; posts_flusher(post_handler_ptr _handler, report_t& _report) - : handler(_handler), report(_report) {} + : handler(_handler), report(_report) { + TRACE_CTOR(posts_flusher, "post_handler_ptr, report_t&"); + } + ~posts_flusher() throw() { + TRACE_DTOR(posts_flusher); + } void operator()(const value_t&) { report.session.journal->clear_xdata(); diff --git a/src/report.h b/src/report.h index aca4f466..37123377 100644 --- a/src/report.h +++ b/src/report.h @@ -119,9 +119,19 @@ public: explicit report_t(session_t& _session) : session(_session), terminus(CURRENT_TIME()), - budget_flags(BUDGET_NO_BUDGET) {} + budget_flags(BUDGET_NO_BUDGET) { + TRACE_CTOR(report_t, "session_t&"); + } + report_t(const report_t& report) + : session(report.session), + output_stream(report.output_stream), + terminus(report.terminus), + budget_flags(report.budget_flags) { + TRACE_CTOR(report_t, "copy"); + } virtual ~report_t() { + TRACE_DTOR(report_t); output_stream.close(); } @@ -1045,7 +1055,16 @@ class reporter public: reporter(shared_ptr > _handler, report_t& _report, const string& _whence) - : handler(_handler), report(_report), whence(_whence) {} + : handler(_handler), report(_report), whence(_whence) { + TRACE_CTOR(reporter, "item_handler, report_t&, string"); + } + reporter(const reporter& other) + : handler(other.handler), report(other.report), whence(other.whence) { + TRACE_CTOR(reporter, "copy"); + } + ~reporter() throw() { + TRACE_DTOR(reporter); + } value_t operator()(call_scope_t& args) { diff --git a/src/scope.h b/src/scope.h index 134babb2..9318fc5c 100644 --- a/src/scope.h +++ b/src/scope.h @@ -142,6 +142,13 @@ private: class empty_scope_t : public scope_t { public: + empty_scope_t() { + TRACE_CTOR(empty_scope_t, ""); + } + ~empty_scope_t() throw() { + TRACE_DTOR(empty_scope_t); + } + virtual string description() { return _(""); } @@ -683,7 +690,12 @@ class value_scope_t : public child_scope_t public: value_scope_t(scope_t& _parent, const value_t& _value) - : child_scope_t(_parent), value(_value) {} + : child_scope_t(_parent), value(_value) { + TRACE_CTOR(value_scope_t, "scope_t&, value_t"); + } + ~value_scope_t() throw() { + TRACE_DTOR(value_scope_t); + } virtual string description() { return parent->description(); diff --git a/src/temps.h b/src/temps.h index ad4e5672..f41c487c 100644 --- a/src/temps.h +++ b/src/temps.h @@ -51,7 +51,11 @@ class temporaries_t optional > acct_temps; public: + temporaries_t() { + TRACE_CTOR(temporaries_t, ""); + } ~temporaries_t() { + TRACE_DTOR(temporaries_t); clear(); } diff --git a/src/utils.cc b/src/utils.cc index 628fb158..5a364008 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -270,13 +270,79 @@ void operator delete[](void * ptr, const std::nothrow_t&) throw() { namespace ledger { -inline void report_count_map(std::ostream& out, object_count_map& the_map) -{ - foreach (object_count_map::value_type& pair, the_map) - out << " " << std::right << std::setw(12) << pair.second.first - << " " << std::right << std::setw(7) << pair.second.second - << " " << std::left << pair.first - << std::endl; +namespace { + void stream_commified_number(std::ostream& out, std::size_t num) + { + std::ostringstream buf; + std::ostringstream obuf; + + buf << num; + + int integer_digits = 0; + // Count the number of integer digits + for (const char * p = buf.str().c_str(); *p; p++) { + if (*p == '.') + break; + else if (*p != '-') + integer_digits++; + } + + for (const char * p = buf.str().c_str(); *p; p++) { + if (*p == '.') { + obuf << *p; + assert(integer_digits <= 3); + } + else if (*p == '-') { + obuf << *p; + } + else { + obuf << *p; + + if (integer_digits > 3 && --integer_digits % 3 == 0) + obuf << ','; + } + } + + out << obuf.str(); + } + + void stream_memory_size(std::ostream& out, std::size_t size) + { + std::ostringstream obuf; + + if (size > 10 * 1024 * 1024) + obuf << "\033[1m"; + if (size > 100 * 1024 * 1024) + obuf << "\033[31m"; + + obuf << std::setw(7); + + if (size < 1024) + obuf << size << 'b'; + else if (size < (1024 * 1024)) + obuf << int(double(size) / 1024.0) << 'K'; + else if (size < (1024 * 1024 * 1024)) + obuf << int(double(size) / (1024.0 * 1024.0)) << 'M'; + else + obuf << int(double(size) / (1024.0 * 1024.0 * 1024.0)) << 'G'; + + if (size > 10 * 1024 * 1024) + obuf << "\033[0m"; + + out << obuf.str(); + } + + void report_count_map(std::ostream& out, object_count_map& the_map) + { + foreach (object_count_map::value_type& pair, the_map) { + out << " " << std::right << std::setw(12); + stream_commified_number(out, pair.second.first); + out << " " << std::right << std::setw(7); + stream_memory_size(out, pair.second.second); + out << " " << std::left << pair.first + << std::endl; + } + } } std::size_t current_objects_size() @@ -354,7 +420,7 @@ void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size) void report_memory(std::ostream& out, bool report_all) { - if (! live_memory || ! memory_tracing_active) return; + if (! live_memory) return; if (live_memory_count->size() > 0) { out << "NOTE: There may be memory held by Boost " @@ -366,11 +432,13 @@ void report_memory(std::ostream& out, bool report_all) if (live_memory->size() > 0) { out << "Live memory:" << std::endl; - foreach (const memory_map::value_type& pair, *live_memory) + foreach (const memory_map::value_type& pair, *live_memory) { out << " " << std::right << std::setw(12) << pair.first - << " " << std::right << std::setw(7) << pair.second.second - << " " << std::left << pair.second.first + << " " << std::right << std::setw(7); + stream_memory_size(out, pair.second.second); + out << " " << std::left << pair.second.first << std::endl; + } } if (report_all && total_memory_count->size() > 0) { @@ -386,11 +454,13 @@ void report_memory(std::ostream& out, bool report_all) if (live_objects->size() > 0) { out << "Live objects:" << std::endl; - foreach (const objects_map::value_type& pair, *live_objects) + foreach (const objects_map::value_type& pair, *live_objects) { out << " " << std::right << std::setw(12) << pair.first - << " " << std::right << std::setw(7) << pair.second.second - << " " << std::left << pair.second.first + << " " << std::right << std::setw(7); + stream_memory_size(out, pair.second.second); + out << " " << std::left << pair.second.first << std::endl; + } } if (report_all) { @@ -529,18 +599,6 @@ std::ostringstream _log_buffer; uint8_t _trace_level; #endif -static inline void stream_memory_size(std::ostream& out, std::size_t size) -{ - if (size < 1024) - out << size << 'b'; - else if (size < (1024 * 1024)) - out << (double(size) / 1024.0) << 'K'; - else if (size < (1024 * 1024 * 1024)) - out << (double(size) / (1024.0 * 1024.0)) << 'M'; - else - out << (double(size) / (1024.0 * 1024.0 * 1024.0)) << 'G'; -} - static bool logger_has_run = false; static ptime logger_start; diff --git a/test/baseline/opt-verify-memory.test b/test/baseline/opt-verify-memory.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 47d7f5a43b286b3c736e21480d84caf75cea885f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 18 Mar 2012 02:16:41 -0500 Subject: Fix problem with -H and lack of pricing info Fixes #691 --- src/annotate.cc | 6 +++++- src/annotate.h | 9 +++++---- src/pool.cc | 37 +++++++++++++++---------------------- test/regress/96A8E4A1.test | 10 ++++++++++ 4 files changed, 35 insertions(+), 27 deletions(-) create mode 100644 test/regress/96A8E4A1.test (limited to 'src') diff --git a/src/annotate.cc b/src/annotate.cc index 2b118e76..25f0e582 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -56,6 +56,7 @@ bool annotation_t::operator<(const annotation_t& rhs) const return true; if (price->commodity().symbol() > rhs.price->commodity().symbol()) return false; + if (*price < *rhs.price) return true; if (*price > *rhs.price) return false; } @@ -68,9 +69,12 @@ bool annotation_t::operator<(const annotation_t& rhs) const if (*tag > *rhs.tag) return false; } if (value_expr) { + DEBUG("annotate.less", "Comparing (" << value_expr->text() + << ") < (" << rhs.value_expr->text()); if (value_expr->text() < rhs.value_expr->text()) return true; - if (value_expr->text() > rhs.value_expr->text()) return false; + //if (value_expr->text() > rhs.value_expr->text()) return false; } + return false; } diff --git a/src/annotate.h b/src/annotate.h index 044ebc4d..163ffac5 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -72,7 +72,7 @@ struct annotation_t : public supports_flags<>, : supports_flags<>(), price(_price), date(_date), tag(_tag), value_expr(_value_expr) { TRACE_CTOR(annotation_t, - "const optional& + date_t + string + expr_t"); + "optional + date_t + string + expr_t"); } annotation_t(const annotation_t& other) : supports_flags<>(other.flags()), @@ -91,9 +91,9 @@ struct annotation_t : public supports_flags<>, bool operator<(const annotation_t& rhs) const; bool operator==(const annotation_t& rhs) const { - return (price == rhs.price && - date == rhs.date && - tag == rhs.tag && + return (price == rhs.price && + date == rhs.date && + tag == rhs.tag && (value_expr && rhs.value_expr ? value_expr->text() == rhs.value_expr->text() : value_expr == rhs.value_expr)); @@ -228,6 +228,7 @@ protected: : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) { TRACE_CTOR(annotated_commodity_t, "commodity_t *, annotation_t"); annotated = true; + qualified_symbol = _ptr->qualified_symbol; } public: diff --git a/src/pool.cc b/src/pool.cc index 5813c0f6..be26de3b 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -102,7 +102,7 @@ commodity_t * commodity_pool_t::find_or_create(const string& symbol) commodity_t * commodity_pool_t::alias(const string& name, commodity_t& referent) { - commodities_map::const_iterator i = commodities.find(referent.symbol()); + commodities_map::const_iterator i = commodities.find(referent.base_symbol()); assert(i != commodities.end()); std::pair result @@ -130,20 +130,16 @@ commodity_pool_t::find(const string& symbol, const annotation_t& details) DEBUG("pool.commodities", "commodity_pool_t::find[ann] " << "symbol " << symbol << std::endl << details); - if (details) { - annotated_commodities_map::const_iterator i = - annotated_commodities.find - (annotated_commodities_map::key_type(symbol, details)); - if (i != annotated_commodities.end()) { - DEBUG("pool.commodities", "commodity_pool_t::find[ann] found " - << "symbol " << (*i).second->symbol() << std::endl - << as_annotated_commodity(*(*i).second.get()).details); - return (*i).second.get(); - } else { - return NULL; - } + annotated_commodities_map::const_iterator i = + annotated_commodities.find + (annotated_commodities_map::key_type(symbol, details)); + if (i != annotated_commodities.end()) { + DEBUG("pool.commodities", "commodity_pool_t::find[ann] found " + << "symbol " << (*i).second->base_symbol() << std::endl + << as_annotated_commodity(*(*i).second.get()).details); + return (*i).second.get(); } else { - return find(symbol); + return NULL; } } @@ -170,10 +166,10 @@ commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, const annotation_t& details) { DEBUG("pool.commodities", "commodity_pool_t::find_or_create[ann:comm] " - << "symbol " << comm.symbol() << std::endl << details); + << "symbol " << comm.base_symbol() << std::endl << details); if (details) { - if (commodity_t * ann_comm = find(comm.symbol(), details)) { + if (commodity_t * ann_comm = find(comm.base_symbol(), details)) { assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); return ann_comm; } else { @@ -189,7 +185,7 @@ commodity_pool_t::create(commodity_t& comm, const annotation_t& details) { DEBUG("pool.commodities", "commodity_pool_t::create[ann:comm] " - << "symbol " << comm.symbol() << std::endl << details); + << "symbol " << comm.base_symbol() << std::endl << details); assert(comm); assert(! comm.has_annotation()); @@ -206,11 +202,8 @@ commodity_pool_t::create(commodity_t& comm, comm.add_flags(COMMODITY_SAW_ANN_PRICE_FLOAT); } - commodity->qualified_symbol = comm.symbol(); - assert(! commodity->qualified_symbol->empty()); - DEBUG("pool.commodities", "Creating annotated commodity " - << "symbol " << commodity->symbol() + << "symbol " << commodity->base_symbol() << std::endl << details); #if defined(DEBUG_ON) @@ -218,7 +211,7 @@ commodity_pool_t::create(commodity_t& comm, #endif annotated_commodities.insert(annotated_commodities_map::value_type (annotated_commodities_map::key_type - (comm.symbol(), details), commodity)); + (comm.base_symbol(), details), commodity)); #if defined(DEBUG_ON) assert(result.second); #endif diff --git a/test/regress/96A8E4A1.test b/test/regress/96A8E4A1.test new file mode 100644 index 00000000..93fb55d2 --- /dev/null +++ b/test/regress/96A8E4A1.test @@ -0,0 +1,10 @@ +2011-01-31 * Test + Expenses:Travel 1 "Spr MegaBonus" + Assets:Voucher + +test -X EUR -H bal + -1 "Spr MegaBonus" Assets:Voucher + 1 "Spr MegaBonus" Expenses:Travel +-------------------- + 0 +end test -- cgit v1.2.3 From 20edb3a34069b33ec83846c3026138d8fa432c97 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 18 Mar 2012 02:33:25 -0500 Subject: Fixed bug relating to historical pricing Fixes #681 --- src/filters.cc | 11 +++++++---- src/filters.h | 1 + test/regress/14DB77E7.test | 2 +- test/regress/9E0E606D.test | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 test/regress/9E0E606D.test (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 6fedd7ce..749efc77 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -593,6 +593,7 @@ changed_value_posts::changed_value_posts report.HANDLER(display_total_).expr), display_total_expr(report.HANDLER(display_total_).expr), changed_values_only(report.HANDLED(revalued_only)), + historical_prices_only(report.HANDLED(historical)), for_accounts_report(_for_accounts_report), show_unrealized(_show_unrealized), last_post(NULL), display_filter(_display_filter) @@ -624,9 +625,11 @@ changed_value_posts::changed_value_posts void changed_value_posts::flush() { if (last_post && last_post->date() <= report.terminus.date()) { - if (! for_accounts_report) - output_intermediate_prices(*last_post, report.terminus.date()); - output_revaluation(*last_post, report.terminus.date()); + if (! historical_prices_only) { + if (! for_accounts_report) + output_intermediate_prices(*last_post, report.terminus.date()); + output_revaluation(*last_post, report.terminus.date()); + } last_post = NULL; } item_handler::flush(); @@ -807,7 +810,7 @@ void changed_value_posts::output_intermediate_prices(post_t& post, void changed_value_posts::operator()(post_t& post) { if (last_post) { - if (! for_accounts_report) + if (! for_accounts_report && ! historical_prices_only) output_intermediate_prices(*last_post, post.value_date()); output_revaluation(*last_post, post.value_date()); } diff --git a/src/filters.h b/src/filters.h index 1ef92bbe..d73fff86 100644 --- a/src/filters.h +++ b/src/filters.h @@ -576,6 +576,7 @@ class changed_value_posts : public item_handler expr_t& total_expr; expr_t& display_total_expr; bool changed_values_only; + bool historical_prices_only; bool for_accounts_report; bool show_unrealized; post_t * last_post; diff --git a/test/regress/14DB77E7.test b/test/regress/14DB77E7.test index ee155afb..4d8734f9 100644 --- a/test/regress/14DB77E7.test +++ b/test/regress/14DB77E7.test @@ -13,6 +13,6 @@ P 2011-02-01 EUR 0.8576 GBP test reg income:adse -X GBP -H 11-Jan-31 AdSense earnings Income:AdSense -11.00 EUR -11.00 EUR -11-Feb-01 Commodities revalued -9.43 GBP -9.43 GBP +11-Feb-28 Commodities revalued -9.43 GBP -9.43 GBP 11-Feb-28 AdSense earnings Income:AdSense -8.58 GBP -18.01 GBP end test diff --git a/test/regress/9E0E606D.test b/test/regress/9E0E606D.test new file mode 100644 index 00000000..86b8e36f --- /dev/null +++ b/test/regress/9E0E606D.test @@ -0,0 +1,19 @@ +D 1000.00 GBP + +P 2011-02-01 EUR 0.8576 GBP +P 2011-03-01 EUR 0.8612 GBP +P 2011-04-01 EUR 0.8510 GBP + +2011-01-31 * AdSense earnings + Assets:Receivable:AdSense 11.00 EUR + Income:AdSense + +2011-02-28 * AdSense earnings + Assets:Receivable:AdSense 10.00 EUR + Income:AdSense + +test reg income:ad -X GBP -H +11-Jan-31 AdSense earnings Income:AdSense -11.00 EUR -11.00 EUR +11-Feb-28 Commodities revalued -9.43 GBP -9.43 GBP +11-Feb-28 AdSense earnings Income:AdSense -8.58 GBP -18.01 GBP +end test -- cgit v1.2.3 From d6274fee19143554f232c9d0d78263bbd19efaed Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 00:30:15 -0500 Subject: Extend commodity_t::compare_by_commodity::operator() --- src/commodity.cc | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index 0dad9a1a..5335d8a8 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -481,6 +481,15 @@ bool commodity_t::compare_by_commodity::operator()(const amount_t * left, if (aleftcomm.details.tag && arightcomm.details.tag) return *aleftcomm.details.tag < *arightcomm.details.tag; + if (! aleftcomm.details.value_expr && arightcomm.details.value_expr) + return true; + if (aleftcomm.details.value_expr && ! arightcomm.details.value_expr) + return false; + + if (aleftcomm.details.value_expr && arightcomm.details.value_expr) + return (aleftcomm.details.value_expr->text() < + arightcomm.details.value_expr->text()); + assert(false); return true; } -- cgit v1.2.3 From 60a281a55a2fd009723c291c6e665b0de743f062 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 00:30:29 -0500 Subject: Corrected an error print in value.cc --- src/value.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/value.cc b/src/value.cc index c14a7104..2b1b561f 100644 --- a/src/value.cc +++ b/src/value.cc @@ -742,7 +742,7 @@ value_t& value_t::operator/=(const value_t& val) break; } - add_error_context(_("While dividing %1 by %2:") << val << *this); + add_error_context(_("While dividing %1 by %2:") << *this << val); throw_(value_error, _("Cannot divide %1 by %2") << label() << val.label()); return *this; @@ -948,8 +948,7 @@ bool value_t::is_less_than(const value_t& val) const break; } - add_error_context(_("While comparing if %1 is less than %2:") - << *this << val); + add_error_context(_("While comparing if %1 is less than %2:") << *this << val); throw_(value_error, _("Cannot compare %1 to %2") << label() << val.label()); return *this; @@ -1064,8 +1063,7 @@ bool value_t::is_greater_than(const value_t& val) const break; } - add_error_context(_("While comparing if %1 is greater than %2:") - << *this << val); + add_error_context(_("While comparing if %1 is greater than %2:") << *this << val); throw_(value_error, _("Cannot compare %1 to %2") << label() << val.label()); return *this; -- cgit v1.2.3 From cd50fe054794d89253fce26daa685e2043428873 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 00:41:32 -0500 Subject: More improvements to nail_down; fixes #715 --- src/report.cc | 6 ++++-- test/regress/BFD3FBE1.test | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 test/regress/BFD3FBE1.test (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 28836d0f..21fc9c1b 100644 --- a/src/report.cc +++ b/src/report.cc @@ -813,10 +813,12 @@ value_t report_t::fn_nail_down(call_scope_t& args) switch (arg0.type()) { case value_t::AMOUNT: { amount_t tmp(arg0.as_amount()); - if (tmp.has_commodity() && ! arg1.is_null()) { + if (tmp.has_commodity() && ! tmp.is_null() && ! tmp.is_realzero()) { + arg1 = arg1.strip_annotations(keep_details_t()).to_amount(); expr_t value_expr(is_expr(arg1) ? as_expr(arg1) : - expr_t::op_t::wrap_value(arg1.unrounded() / arg0)); + expr_t::op_t::wrap_value(arg1.unrounded() / + arg0.number())); std::ostringstream buf; value_expr.print(buf); value_expr.set_text(buf.str()); diff --git a/test/regress/BFD3FBE1.test b/test/regress/BFD3FBE1.test new file mode 100644 index 00000000..5b2f8b42 --- /dev/null +++ b/test/regress/BFD3FBE1.test @@ -0,0 +1,17 @@ +2011-01-01 * Opening balance + Assets:Investment 100 "AAA" @ 16.58900489 EUR + Assets:Investments 5 "BBB" @ 24.79900855 EUR + Equity:Opening balance + +2011-02-10 * Reimbursement: Taxi / Subway / Bus / Train + Assets:A 1.59 GBP + Assets:B -1.80 EUR @ 0.884955752212389381 GBP + +test reg -X EUR -H +11-Jan-01 Opening balance Assets:Investment 1658.90 EUR 1658.90 EUR + Assets:Investments 124.00 EUR 1782.90 EUR + Equity:Opening balance -1782.90 EUR 0 +11-Feb-10 Reimbursement: Taxi.. -0.01 EUR -0.01 EUR + Assets:A 1.80 EUR 1.80 EUR + Assets:B -1.80 EUR 0 +end test -- cgit v1.2.3 From e2a22cb8c245b857e39417eb47b543ecf7204573 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 03:51:07 -0500 Subject: Allow --input-date-format to specify separators Fixes #650 --- src/times.cc | 11 ++++++++--- test/regress/751B2357.test | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 test/regress/751B2357.test (limited to 'src') diff --git a/src/times.cc b/src/times.cc index 21ec1859..3c556a47 100644 --- a/src/times.cc +++ b/src/times.cc @@ -197,6 +197,8 @@ namespace { std::deque > readers; + bool convert_separators_to_slashes = true; + date_t parse_date_mask_routine(const char * date_str, date_io_t& io, date_traits_t * traits = NULL) { @@ -205,9 +207,11 @@ namespace { char buf[128]; std::strcpy(buf, date_str); - for (char * p = buf; *p; p++) - if (*p == '.' || *p == '-') - *p = '/'; + if (convert_separators_to_slashes) { + for (char * p = buf; *p; p++) + if (*p == '.' || *p == '-') + *p = '/'; + } date_t when = io.parse(buf); @@ -1775,6 +1779,7 @@ void set_date_format(const char * format) void set_input_date_format(const char * format) { readers.push_front(shared_ptr(new date_io_t(format, true))); + convert_separators_to_slashes = false; } void times_initialize() diff --git a/test/regress/751B2357.test b/test/regress/751B2357.test new file mode 100644 index 00000000..2b0f9a16 --- /dev/null +++ b/test/regress/751B2357.test @@ -0,0 +1,17 @@ +01.10.2011 4b4e2a89 + ef9d9585:efa1fb7b:22845e93:0e3763f0 2,00 A + 2c166ff7:d34e3aa1:8a5075b3:56f3c726 + +01.10.2011 15983995 + eb78b6c0:a2857de3:d6d8ea07:6688fc4e 2,58 A + ba3ffe56:c3ba36a5:aa63399f:e9e1d043 + +test print --date-format=%d.%m.%Y --input-date-format=%d.%m.%Y +01.10.2011 4b4e2a89 + ef9d9585:efa1fb7b:22845e93:0e3763f0 2,00 A + 2c166ff7:d34e3aa1:8a5075b3:56f3c726 + +01.10.2011 15983995 + eb78b6c0:a2857de3:d6d8ea07:6688fc4e 2,58 A + ba3ffe56:c3ba36a5:aa63399f:e9e1d043 +end test -- cgit v1.2.3 From 9097bc8cd3a19d98e271dd683986e3fc455da123 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 04:02:49 -0500 Subject: Made an assert more relaxed in item_context() --- src/item.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 4ddbe913..33c3110c 100644 --- a/src/item.cc +++ b/src/item.cc @@ -543,7 +543,7 @@ string item_context(const item_t& item, const string& desc) if (! (len > 0)) return empty_string; - assert(len < 8192); + assert(len < 1024 * 1024); std::ostringstream out; -- cgit v1.2.3 From aee05dc7b7099d4bff8827e293e6b1e3442b9cd9 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 04:18:33 -0500 Subject: print computed annotations if --generated --- src/print.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/print.cc b/src/print.cc index 9e52ce95..79d83161 100644 --- a/src/print.cc +++ b/src/print.cc @@ -226,7 +226,8 @@ namespace { std::ostringstream amt_str; value_t(post->amount).print(amt_str, static_cast(amount_width), -1, AMOUNT_PRINT_RIGHT_JUSTIFY | - AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS); + (report.HANDLED(generated) ? 0 : + AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS)); amt = amt_str.str(); } -- cgit v1.2.3 From d2422f99e6edd17b86502b34be4fb618fe4c838d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 04:18:56 -0500 Subject: Allow the equity command to strip annotations --- src/chain.cc | 2 +- src/filters.cc | 47 ++++++++++++++++++++++++++--------------------- src/filters.h | 6 ++++-- 3 files changed, 31 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/chain.cc b/src/chain.cc index 44b3db82..52d52f14 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -207,7 +207,7 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, // day_of_week_posts is like period_posts, except that it reports // all the posts that fall on each subsequent day of the week. if (report.HANDLED(equity)) - handler.reset(new posts_as_equity(handler, expr)); + handler.reset(new posts_as_equity(handler, report, expr)); else if (report.HANDLED(subtotal)) handler.reset(new subtotal_posts(handler, expr)); } diff --git a/src/filters.cc b/src/filters.cc index 749efc77..96e8026a 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1033,40 +1033,45 @@ void posts_as_equity::report_subtotal() value_t total = 0L; foreach (values_map::value_type& pair, values) { - if (pair.second.value.is_balance()) { - foreach (const balance_t::amounts_map::value_type& amount_pair, - pair.second.value.as_balance().amounts) - handle_value(/* value= */ amount_pair.second, + value_t value(pair.second.value.strip_annotations(report.what_to_keep())); + if (! value.is_zero()) { + if (value.is_balance()) { + foreach (const balance_t::amounts_map::value_type& amount_pair, + value.as_balance_lval().amounts) + handle_value(/* value= */ amount_pair.second, + /* account= */ pair.second.account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ finish, + /* act_date_p= */ false); + } else { + handle_value(/* value= */ value.to_amount(), /* account= */ pair.second.account, /* xact= */ &xact, /* temps= */ temps, /* handler= */ handler, /* date= */ finish, /* act_date_p= */ false); - } else { - handle_value(/* value= */ pair.second.value, - /* account= */ pair.second.account, - /* xact= */ &xact, - /* temps= */ temps, - /* handler= */ handler, - /* date= */ finish, - /* act_date_p= */ false); + } } - total += pair.second.value; + total += value; } values.clear(); - if (total.is_balance()) { - foreach (const balance_t::amounts_map::value_type& pair, - total.as_balance().amounts) { + if (! total.is_zero()) { + if (total.is_balance()) { + foreach (const balance_t::amounts_map::value_type& pair, + total.as_balance().amounts) { + post_t& balance_post = temps.create_post(xact, balance_account); + balance_post.amount = - pair.second; + (*handler)(balance_post); + } + } else { post_t& balance_post = temps.create_post(xact, balance_account); - balance_post.amount = - pair.second; + balance_post.amount = - total.to_amount(); (*handler)(balance_post); } - } else { - post_t& balance_post = temps.create_post(xact, balance_account); - balance_post.amount = - total.to_amount(); - (*handler)(balance_post); } } diff --git a/src/filters.h b/src/filters.h index d73fff86..3e766863 100644 --- a/src/filters.h +++ b/src/filters.h @@ -763,6 +763,7 @@ public: class posts_as_equity : public subtotal_posts { + report_t& report; post_t * last_post; account_t * equity_account; account_t * balance_account; @@ -770,8 +771,9 @@ class posts_as_equity : public subtotal_posts posts_as_equity(); public: - posts_as_equity(post_handler_ptr _handler, expr_t& amount_expr) - : subtotal_posts(_handler, amount_expr) { + posts_as_equity(post_handler_ptr _handler, report_t& _report, + expr_t& amount_expr) + : subtotal_posts(_handler, amount_expr), report(_report) { TRACE_CTOR(posts_as_equity, "post_handler_ptr, expr_t&"); create_accounts(); } -- cgit v1.2.3 From ceb8dd89a6740c218d17ed01dbe5a7e8a37be2f0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 04:21:15 -0500 Subject: Don't output zero balances with the equity command --- src/filters.cc | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 96e8026a..73ee200d 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1037,14 +1037,16 @@ void posts_as_equity::report_subtotal() if (! value.is_zero()) { if (value.is_balance()) { foreach (const balance_t::amounts_map::value_type& amount_pair, - value.as_balance_lval().amounts) - handle_value(/* value= */ amount_pair.second, - /* account= */ pair.second.account, - /* xact= */ &xact, - /* temps= */ temps, - /* handler= */ handler, - /* date= */ finish, - /* act_date_p= */ false); + value.as_balance_lval().amounts) { + if (! amount_pair.second.is_zero()) + handle_value(/* value= */ amount_pair.second, + /* account= */ pair.second.account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ finish, + /* act_date_p= */ false); + } } else { handle_value(/* value= */ value.to_amount(), /* account= */ pair.second.account, @@ -1063,9 +1065,11 @@ void posts_as_equity::report_subtotal() if (total.is_balance()) { foreach (const balance_t::amounts_map::value_type& pair, total.as_balance().amounts) { - post_t& balance_post = temps.create_post(xact, balance_account); - balance_post.amount = - pair.second; - (*handler)(balance_post); + if (! pair.second.is_zero()) { + post_t& balance_post = temps.create_post(xact, balance_account); + balance_post.amount = - pair.second; + (*handler)(balance_post); + } } } else { post_t& balance_post = temps.create_post(xact, balance_account); -- cgit v1.2.3 From 79ecbf0c176215349efa458dadffa770c3decebc Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 22:28:00 -0500 Subject: Corrected an uninitialized memory bug --- src/account.h | 12 +++++++++++- test/fullcheck.sh | 5 ++++- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/account.h b/src/account.h index fee12595..fd98a9ac 100644 --- a/src/account.h +++ b/src/account.h @@ -193,7 +193,17 @@ public: TRACE_CTOR(account_t::xdata_t::details_t, ""); } // A copy copies nothing - details_t(const details_t&) { + details_t(const details_t&) + : calculated(false), + gathered(false), + + posts_count(0), + posts_virtuals_count(0), + posts_cleared_count(0), + posts_last_7_count(0), + posts_last_30_count(0), + posts_this_month_count(0) + { TRACE_CTOR(account_t::xdata_t::details_t, "copy"); } ~details_t() throw() { diff --git a/test/fullcheck.sh b/test/fullcheck.sh index db63921d..fc89a13c 100755 --- a/test/fullcheck.sh +++ b/test/fullcheck.sh @@ -2,7 +2,10 @@ VALGRIND='' if [ -x /usr/bin/valgrind -o -x /opt/local/bin/valgrind ]; then - VALGRIND="valgrind -q" + VALGRIND="valgrind -q --track-origins=yes" + if [ `uname` = "Darwin" ]; then + VALGRIND="$VALGRIND --dsymutil=yes" + fi fi #export MallocGuardEdges=1 -- cgit v1.2.3 From 22505d9527edce59cd3cf90c5241e4bd809eb8a2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 20 Mar 2012 02:10:40 -0500 Subject: Always call TRACE_CTOR at the end of constructors --- src/amount.cc | 10 +++++----- src/amount.h | 8 ++++---- src/annotate.h | 2 +- src/balance.cc | 6 +++--- src/balance.h | 6 +++--- src/csv.h | 2 +- src/draft.h | 2 +- src/expr.cc | 4 ++-- src/filters.cc | 12 ++++++------ src/filters.h | 15 +++++++-------- src/format.h | 2 +- src/generate.cc | 3 +-- src/global.cc | 4 ++-- src/item.h | 4 ++-- src/iterators.h | 14 +++++++------- src/journal.cc | 6 +++--- src/journal.h | 2 +- src/mask.cc | 2 +- src/option.h | 2 +- src/org.cc | 4 ++-- src/output.cc | 8 ++++---- src/pool.cc | 2 +- src/post.h | 2 +- src/pstream.h | 8 ++++---- src/pyfstream.h | 8 ++++---- src/query.h | 8 +++++--- src/scope.h | 2 +- src/session.cc | 4 ++-- src/times.h | 5 +++-- src/unistring.h | 4 ++-- src/utils.cc | 12 +++++++----- src/value.h | 34 ++++++++++++++++++---------------- 32 files changed, 106 insertions(+), 101 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 5fa58528..8c5ae574 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -63,16 +63,16 @@ struct amount_t::bigint_t : public supports_flags<> #define MP(bigint) ((bigint)->val) bigint_t() : prec(0), refc(1) { - TRACE_CTOR(bigint_t, ""); mpq_init(val); + TRACE_CTOR(bigint_t, ""); } bigint_t(const bigint_t& other) : supports_flags<>(static_cast (other.flags() & ~BIGINT_BULK_ALLOC)), prec(other.prec), refc(1) { - TRACE_CTOR(bigint_t, "copy"); mpq_init(val); mpq_set(val, other.val); + TRACE_CTOR(bigint_t, "copy"); } ~bigint_t() { TRACE_DTOR(bigint_t); @@ -349,24 +349,24 @@ void amount_t::_release() amount_t::amount_t(const double val) : commodity_(NULL) { - TRACE_CTOR(amount_t, "const double"); quantity = new bigint_t; mpq_set_d(MP(quantity), val); quantity->prec = extend_by_digits; // an approximation + TRACE_CTOR(amount_t, "const double"); } amount_t::amount_t(const unsigned long val) : commodity_(NULL) { - TRACE_CTOR(amount_t, "const unsigned long"); quantity = new bigint_t; mpq_set_ui(MP(quantity), val, 1); + TRACE_CTOR(amount_t, "const unsigned long"); } amount_t::amount_t(const long val) : commodity_(NULL) { - TRACE_CTOR(amount_t, "const long"); quantity = new bigint_t; mpq_set_si(MP(quantity), val, 1); + TRACE_CTOR(amount_t, "const long"); } diff --git a/src/amount.h b/src/amount.h index 903a01cd..10e83552 100644 --- a/src/amount.h +++ b/src/amount.h @@ -155,17 +155,17 @@ public: commodity_t::null_commodity. The number may be of infinite precision. */ explicit amount_t(const string& val) : quantity(NULL) { - TRACE_CTOR(amount_t, "const string&"); parse(val); + TRACE_CTOR(amount_t, "const string&"); } /** Parse a pointer to a C string as an (optionally commoditized) amount. If no commodity is present, the resulting commodity is \c commodity_t::null_commodity. The number may be of infinite precision. */ explicit amount_t(const char * val) : quantity(NULL) { - TRACE_CTOR(amount_t, "const char *"); assert(val); parse(val); + TRACE_CTOR(amount_t, "const char *"); } /*@}*/ @@ -195,21 +195,21 @@ public: same memory used by the original via reference counting. The \c amount_t::bigint_t class in amount.cc maintains the reference. */ amount_t(const amount_t& amt) : quantity(NULL) { - TRACE_CTOR(amount_t, "copy"); if (amt.quantity) _copy(amt); else commodity_ = NULL; + TRACE_CTOR(amount_t, "copy"); } /** Copy an amount object, applying the given commodity annotation details afterward. This is equivalent to doing a normal copy (@see amount_t(const amount_t&)) and then calling amount_t::annotate(). */ amount_t(const amount_t& amt, const annotation_t& details) : quantity(NULL) { - TRACE_CTOR(amount_t, "const amount_t&, const annotation_t&"); assert(amt.quantity); _copy(amt); annotate(details); + TRACE_CTOR(amount_t, "const amount_t&, const annotation_t&"); } /** Assign an amount object. This is like copying if the amount was null beforehand, otherwise the previous value's reference is must diff --git a/src/annotate.h b/src/annotate.h index 163ffac5..85a34662 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -226,9 +226,9 @@ protected: explicit annotated_commodity_t(commodity_t * _ptr, const annotation_t& _details) : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) { - TRACE_CTOR(annotated_commodity_t, "commodity_t *, annotation_t"); annotated = true; qualified_symbol = _ptr->qualified_symbol; + TRACE_CTOR(annotated_commodity_t, "commodity_t *, annotation_t"); } public: diff --git a/src/balance.cc b/src/balance.cc index f87e8bbd..5c57cd99 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -41,23 +41,23 @@ namespace ledger { balance_t::balance_t(const double val) { - TRACE_CTOR(balance_t, "const double"); amounts.insert (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val)); + TRACE_CTOR(balance_t, "const double"); } balance_t::balance_t(const unsigned long val) { - TRACE_CTOR(balance_t, "const unsigned long"); amounts.insert (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val)); + TRACE_CTOR(balance_t, "const unsigned long"); } balance_t::balance_t(const long val) { - TRACE_CTOR(balance_t, "const long"); amounts.insert (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val)); + TRACE_CTOR(balance_t, "const long"); } balance_t& balance_t::operator+=(const balance_t& bal) diff --git a/src/balance.h b/src/balance.h index 5f0d52ed..11c370bb 100644 --- a/src/balance.h +++ b/src/balance.h @@ -108,26 +108,26 @@ public: TRACE_CTOR(balance_t, ""); } balance_t(const amount_t& amt) { - TRACE_CTOR(balance_t, "const amount_t&"); if (amt.is_null()) throw_(balance_error, _("Cannot initialize a balance from an uninitialized amount")); if (! amt.is_realzero()) amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); + TRACE_CTOR(balance_t, "const amount_t&"); } balance_t(const double val); balance_t(const unsigned long val); balance_t(const long val); explicit balance_t(const string& val) { - TRACE_CTOR(balance_t, "const string&"); amount_t temp(val); amounts.insert(amounts_map::value_type(&temp.commodity(), temp)); + TRACE_CTOR(balance_t, "const string&"); } explicit balance_t(const char * val) { - TRACE_CTOR(balance_t, "const char *"); amount_t temp(val); amounts.insert(amounts_map::value_type(&temp.commodity(), temp)); + TRACE_CTOR(balance_t, "const char *"); } /** diff --git a/src/csv.h b/src/csv.h index d98c0567..7d5098d2 100644 --- a/src/csv.h +++ b/src/csv.h @@ -91,8 +91,8 @@ public: cost_mask("cost"), total_mask("total"), note_mask("note") { - TRACE_CTOR(csv_reader, "parse_context_t&"); read_index(*context.stream.get()); + TRACE_CTOR(csv_reader, "parse_context_t&"); } ~csv_reader() { TRACE_DTOR(csv_reader); diff --git a/src/draft.h b/src/draft.h index e5d29134..46aa26e1 100644 --- a/src/draft.h +++ b/src/draft.h @@ -92,9 +92,9 @@ class draft_t : public expr_base_t public: draft_t(const value_t& args) : base_type() { - TRACE_CTOR(draft_t, "value_t"); if (! args.empty()) parse_args(args); + TRACE_CTOR(draft_t, "value_t"); } virtual ~draft_t() throw() { TRACE_DTOR(draft_t); diff --git a/src/expr.cc b/src/expr.cc index b22572f8..25967207 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -55,16 +55,16 @@ expr_t::expr_t(ptr_op_t _ptr, scope_t * _context) expr_t::expr_t(const string& _str, const parse_flags_t& flags) : base_type() { - TRACE_CTOR(expr_t, "string, parse_flags_t"); if (! _str.empty()) parse(_str, flags); + TRACE_CTOR(expr_t, "string, parse_flags_t"); } expr_t::expr_t(std::istream& in, const parse_flags_t& flags) : base_type() { - TRACE_CTOR(expr_t, "std::istream&, parse_flags_t"); parse(in, flags); + TRACE_CTOR(expr_t, "std::istream&, parse_flags_t"); } expr_t::~expr_t() { diff --git a/src/filters.cc b/src/filters.cc index 73ee200d..5915ad3c 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -512,8 +512,8 @@ display_filter_posts::display_filter_posts(post_handler_ptr handler, display_total_expr(report.HANDLER(display_total_).expr), show_rounding(_show_rounding) { - TRACE_CTOR(display_filter_posts, "post_handler_ptr, report_t&, bool"); create_accounts(); + TRACE_CTOR(display_filter_posts, "post_handler_ptr, report_t&, bool"); } bool display_filter_posts::output_rounding(post_t& post) @@ -598,9 +598,6 @@ changed_value_posts::changed_value_posts show_unrealized(_show_unrealized), last_post(NULL), display_filter(_display_filter) { - TRACE_CTOR(changed_value_posts, - "post_handler_ptr, report_t&, bool, bool, display_filter_posts *"); - string gains_equity_account_name; if (report.HANDLED(unrealized_gains_)) gains_equity_account_name = report.HANDLER(unrealized_gains_).str(); @@ -620,6 +617,9 @@ changed_value_posts::changed_value_posts losses_equity_account->add_flags(ACCOUNT_GENERATED); create_accounts(); + + TRACE_CTOR(changed_value_posts, + "post_handler_ptr, report_t&, bool, bool, display_filter_posts *"); } void changed_value_posts::flush() @@ -1417,8 +1417,6 @@ inject_posts::inject_posts(post_handler_ptr handler, account_t * master) : item_handler(handler) { - TRACE_CTOR(inject_posts, "post_handler_ptr, string, account_t *"); - scoped_array buf(new char[tag_list.length() + 1]); std::strcpy(buf.get(), tag_list.c_str()); @@ -1435,6 +1433,8 @@ inject_posts::inject_posts(post_handler_ptr handler, tags_list.push_back (tags_list_pair(q, tag_mapping_pair(account, tag_injected_set()))); } + + TRACE_CTOR(inject_posts, "post_handler_ptr, string, account_t *"); } void inject_posts::operator()(post_t& post) diff --git a/src/filters.h b/src/filters.h index 3e766863..ab226429 100644 --- a/src/filters.h +++ b/src/filters.h @@ -75,8 +75,8 @@ public: expr_t& _group_by_expr) : post_chain(_post_chain), report(_report), group_by_expr(_group_by_expr) { + preflush_func = bind(&post_splitter::print_title, this, _1); TRACE_CTOR(post_splitter, "scope_t&, post_handler_ptr, expr_t"); - preflush_func = bind(&post_splitter::print_title, this, _1); } virtual ~post_splitter() { TRACE_DTOR(post_splitter); @@ -154,8 +154,6 @@ class pass_down_posts : public item_handler public: pass_down_posts(post_handler_ptr handler, Iterator& iter) : item_handler(handler) { - TRACE_CTOR(pass_down_posts, "post_handler_ptr, posts_iterator"); - while (post_t * post = *iter) { try { item_handler::operator()(*post); @@ -168,6 +166,8 @@ public: } item_handler::flush(); + + TRACE_CTOR(pass_down_posts, "post_handler_ptr, posts_iterator"); } virtual ~pass_down_posts() { @@ -448,8 +448,8 @@ public: only_predicate(_only_predicate), count(0), last_xact(NULL), last_post(NULL), only_collapse_if_zero(_only_collapse_if_zero), report(_report) { - TRACE_CTOR(collapse_posts, "post_handler_ptr, ..."); create_accounts(); + TRACE_CTOR(collapse_posts, "post_handler_ptr, ..."); } virtual ~collapse_posts() { TRACE_DTOR(collapse_posts); @@ -499,8 +499,7 @@ public: const bool _also_matching = false) : item_handler(handler), also_matching(_also_matching) { - TRACE_CTOR(related_posts, - "post_handler_ptr, const bool"); + TRACE_CTOR(related_posts, "post_handler_ptr, const bool"); } virtual ~related_posts() throw() { TRACE_DTOR(related_posts); @@ -722,9 +721,9 @@ public: : subtotal_posts(_handler, amount_expr), start_interval(_interval), interval(start_interval), exact_periods(_exact_periods), generate_empty_posts(_generate_empty_posts) { + create_accounts(); TRACE_CTOR(interval_posts, "post_handler_ptr, expr_t&, date_interval_t, bool, bool"); - create_accounts(); } virtual ~interval_posts() throw() { TRACE_DTOR(interval_posts); @@ -774,8 +773,8 @@ public: posts_as_equity(post_handler_ptr _handler, report_t& _report, expr_t& amount_expr) : subtotal_posts(_handler, amount_expr), report(_report) { - TRACE_CTOR(posts_as_equity, "post_handler_ptr, expr_t&"); create_accounts(); + TRACE_CTOR(posts_as_equity, "post_handler_ptr, expr_t&"); } virtual ~posts_as_equity() throw() { TRACE_DTOR(posts_as_equity); diff --git a/src/format.h b/src/format.h index 60ae2abe..cc48bdda 100644 --- a/src/format.h +++ b/src/format.h @@ -125,9 +125,9 @@ public: } format_t(const string& _str, scope_t * context = NULL) : base_type(context) { - TRACE_CTOR(format_t, "const string&"); if (! _str.empty()) parse_format(_str); + TRACE_CTOR(format_t, "const string&"); } virtual ~format_t() { TRACE_DTOR(format_t); diff --git a/src/generate.cc b/src/generate.cc index edd58632..8769c99c 100644 --- a/src/generate.cc +++ b/src/generate.cc @@ -63,8 +63,6 @@ generate_posts_iterator::generate_posts_iterator neg_number_range(-10000, -1), neg_number_gen(rnd_gen, neg_number_range), pos_number_range(1, 10000), pos_number_gen(rnd_gen, pos_number_range) { - TRACE_CTOR(generate_posts_iterator, "bool"); - std::ostringstream next_date_buf; generate_date(next_date_buf); next_date = parse_date(next_date_buf.str()); @@ -73,6 +71,7 @@ generate_posts_iterator::generate_posts_iterator generate_date(next_aux_date_buf); next_aux_date = parse_date(next_aux_date_buf.str()); + TRACE_CTOR(generate_posts_iterator, "bool"); } void generate_posts_iterator::generate_string(std::ostream& out, int len, diff --git a/src/global.cc b/src/global.cc index b5ceb614..6dc8d150 100644 --- a/src/global.cc +++ b/src/global.cc @@ -47,8 +47,6 @@ static bool args_only = false; global_scope_t::global_scope_t(char ** envp) { - TRACE_CTOR(global_scope_t, ""); - epoch = CURRENT_TIME(); #if defined(HAVE_BOOST_PYTHON) @@ -89,6 +87,8 @@ global_scope_t::global_scope_t(char ** envp) } else { session().HANDLER(price_db_).off(); } + + TRACE_CTOR(global_scope_t, ""); } global_scope_t::~global_scope_t() diff --git a/src/item.h b/src/item.h index e7415918..a1160329 100644 --- a/src/item.h +++ b/src/item.h @@ -60,8 +60,8 @@ struct position_t TRACE_CTOR(position_t, ""); } position_t(const position_t& pos) { - TRACE_CTOR(position_t, "copy"); *this = pos; + TRACE_CTOR(position_t, "copy"); } ~position_t() throw() { TRACE_DTOR(position_t); @@ -125,8 +125,8 @@ public: } item_t(const item_t& item) : supports_flags(), scope_t() { - TRACE_CTOR(item_t, "copy"); copy_details(item); + TRACE_CTOR(item_t, "copy"); } virtual ~item_t() { TRACE_DTOR(item_t); diff --git a/src/iterators.h b/src/iterators.h index 922ebccd..53814666 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -100,8 +100,8 @@ public: } xact_posts_iterator(xact_t& xact) : posts_uninitialized(true) { - TRACE_CTOR(xact_posts_iterator, "xact_t&"); reset(xact); + TRACE_CTOR(xact_posts_iterator, "xact_t&"); } xact_posts_iterator(const xact_posts_iterator& i) : iterator_facade_base& _prepend_format) : report(_report), last_xact(NULL), last_post(NULL) { - TRACE_CTOR(posts_to_org_table, "report&, optional"); - first_line_format.parse_format ("|%(format_date(date))" "|%(code)" @@ -79,6 +77,8 @@ posts_to_org_table::posts_to_org_table(report_t& _report, if (_prepend_format) prepend_format.parse_format(*_prepend_format); + + TRACE_CTOR(posts_to_org_table, "report&, optional"); } void posts_to_org_table::flush() diff --git a/src/output.cc b/src/output.cc index aaf81f60..742000bd 100644 --- a/src/output.cc +++ b/src/output.cc @@ -47,8 +47,6 @@ format_posts::format_posts(report_t& _report, : report(_report), prepend_width(_prepend_width), last_xact(NULL), last_post(NULL), first_report_title(true) { - TRACE_CTOR(format_posts, "report&, const string&, bool"); - const char * f = format.c_str(); if (const char * p = std::strstr(f, "%/")) { @@ -70,6 +68,8 @@ format_posts::format_posts(report_t& _report, if (_prepend_format) prepend_format.parse_format(*_prepend_format); + + TRACE_CTOR(format_posts, "report&, const string&, bool"); } void format_posts::flush() @@ -131,8 +131,6 @@ format_accounts::format_accounts(report_t& _report, : report(_report), prepend_width(_prepend_width), disp_pred(), first_report_title(true) { - TRACE_CTOR(format_accounts, "report&, const string&"); - const char * f = format.c_str(); if (const char * p = std::strstr(f, "%/")) { @@ -154,6 +152,8 @@ format_accounts::format_accounts(report_t& _report, if (_prepend_format) prepend_format.parse_format(*_prepend_format); + + TRACE_CTOR(format_accounts, "report&, const string&"); } std::size_t format_accounts::post_account(account_t& account, const bool flat) diff --git a/src/pool.cc b/src/pool.cc index be26de3b..61b5bef2 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -47,9 +47,9 @@ commodity_pool_t::commodity_pool_t() quote_leeway(86400), get_quotes(false), get_commodity_quote(commodity_quote_from_script) { - TRACE_CTOR(commodity_pool_t, ""); null_commodity = create(""); null_commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); + TRACE_CTOR(commodity_pool_t, ""); } commodity_t * commodity_pool_t::create(const string& symbol) diff --git a/src/post.h b/src/post.h index d6004c9f..76a89a33 100644 --- a/src/post.h +++ b/src/post.h @@ -96,8 +96,8 @@ public: assigned_amount(post.assigned_amount), xdata_(post.xdata_) { - TRACE_CTOR(post_t, "copy"); copy_details(post); + TRACE_CTOR(post_t, "copy"); } virtual ~post_t() { TRACE_DTOR(post_t); diff --git a/src/pstream.h b/src/pstream.h index dfb27056..6e38158a 100644 --- a/src/pstream.h +++ b/src/pstream.h @@ -58,14 +58,14 @@ class ptristream : public std::istream public: ptrinbuf(char * _ptr, std::size_t _len) : ptr(_ptr), len(_len) { - TRACE_CTOR(ptrinbuf, "char *, std::size_t"); - if (*ptr && len == 0) len = std::strlen(ptr); - setg(ptr, // beginning of putback area - ptr, // read position + setg(ptr, // beginning of putback area + ptr, // read position ptr+len); // end position + + TRACE_CTOR(ptrinbuf, "char *, std::size_t"); } ~ptrinbuf() throw() { TRACE_DTOR(ptrinbuf); diff --git a/src/pyfstream.h b/src/pyfstream.h index 972f976f..18f28bc4 100644 --- a/src/pyfstream.h +++ b/src/pyfstream.h @@ -86,8 +86,8 @@ protected: public: pyofstream (PyFileObject * fo) : std::ostream(0), buf(fo) { - TRACE_CTOR(pyofstream, "PyFileObject *"); rdbuf(&buf); + TRACE_CTOR(pyofstream, "PyFileObject *"); } ~pyofstream() throw() { TRACE_DTOR(pyofstream); @@ -121,11 +121,11 @@ public: * => force underflow() */ pyinbuf (PyFileObject * _fo) : fo(_fo) { - TRACE_CTOR(pyinbuf, "PyFileObject *"); - setg (buffer+pbSize, // beginning of putback area buffer+pbSize, // read position buffer+pbSize); // end position + + TRACE_CTOR(pyinbuf, "PyFileObject *"); } ~pyinbuf() throw() { TRACE_DTOR(pyinbuf); @@ -191,8 +191,8 @@ protected: public: pyifstream (PyFileObject * fo) : std::istream(0), buf(fo) { - TRACE_CTOR(pyifstream, "PyFileObject *"); rdbuf(&buf); + TRACE_CTOR(pyifstream, "PyFileObject *"); } ~pyifstream() throw() { TRACE_DTOR(pyifstream); diff --git a/src/query.h b/src/query.h index c694d099..fe52eb35 100644 --- a/src/query.h +++ b/src/query.h @@ -204,10 +204,11 @@ public: consume_whitespace(false), consume_next_arg(false), multiple_args(_multiple_args) { - TRACE_CTOR(query_t::lexer_t, ""); assert(begin != end); arg_i = (*begin).as_string().begin(); arg_end = (*begin).as_string().end(); + + TRACE_CTOR(query_t::lexer_t, ""); } lexer_t(const lexer_t& lexer) : begin(lexer.begin), end(lexer.end), @@ -302,18 +303,19 @@ public: query_t(const string& arg, const keep_details_t& what_to_keep = keep_details_t(), bool multiple_args = true) { - TRACE_CTOR(query_t, "string, keep_details_t, bool"); if (! arg.empty()) { value_t temp(string_value(arg)); parse_args(temp.to_sequence(), what_to_keep, multiple_args); } + TRACE_CTOR(query_t, "string, keep_details_t, bool"); } query_t(const value_t& args, const keep_details_t& what_to_keep = keep_details_t(), bool multiple_args = true) { - TRACE_CTOR(query_t, "value_t, keep_details_t, bool"); if (! args.empty()) parse_args(args, what_to_keep, multiple_args); + + TRACE_CTOR(query_t, "value_t, keep_details_t, bool"); } virtual ~query_t() { TRACE_DTOR(query_t); diff --git a/src/scope.h b/src/scope.h index 9318fc5c..acaf7194 100644 --- a/src/scope.h +++ b/src/scope.h @@ -209,9 +209,9 @@ public: explicit bind_scope_t(scope_t& _parent, scope_t& _grandchild) : child_scope_t(_parent), grandchild(_grandchild) { - TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&"); DEBUG("scope.symbols", "Binding scope " << &_parent << " with " << &_grandchild); + TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&"); } virtual ~bind_scope_t() { TRACE_DTOR(bind_scope_t); diff --git a/src/session.cc b/src/session.cc index 5c9e4fd4..9a77d341 100644 --- a/src/session.cc +++ b/src/session.cc @@ -62,14 +62,14 @@ void set_session_context(session_t * session) session_t::session_t() : flush_on_next_data_file(false), journal(new journal_t) { - TRACE_CTOR(session_t, ""); - if (const char * home_var = std::getenv("HOME")) HANDLER(price_db_).on(none, (path(home_var) / ".pricedb").string()); else HANDLER(price_db_).on(none, path("./.pricedb").string()); parsing_context.push(); + + TRACE_CTOR(session_t, ""); } std::size_t session_t::read_data(const string& master_account) diff --git a/src/times.h b/src/times.h index e3134665..3bb95903 100644 --- a/src/times.h +++ b/src/times.h @@ -307,13 +307,14 @@ public: } date_specifier_t(const date_t& date, const optional& traits = none) { - TRACE_CTOR(date_specifier_t, "date_t, date_traits_t"); if (! traits || traits->has_year) year = date.year(); if (! traits || traits->has_month) month = date.month(); if (! traits || traits->has_day) day = date.day(); + + TRACE_CTOR(date_specifier_t, "date_t, date_traits_t"); } date_specifier_t(const date_specifier_t& other) : year(other.year), month(other.month), @@ -538,8 +539,8 @@ public: TRACE_CTOR(date_interval_t, ""); } date_interval_t(const string& str) : aligned(false) { - TRACE_CTOR(date_interval_t, "const string&"); parse(str); + TRACE_CTOR(date_interval_t, "const string&"); } date_interval_t(const date_interval_t& other) : range(other.range), diff --git a/src/unistring.h b/src/unistring.h index a33c6e3f..b2278796 100644 --- a/src/unistring.h +++ b/src/unistring.h @@ -64,14 +64,14 @@ public: } unistring(const std::string& input) { - TRACE_CTOR(unistring, "std::string"); - const char * p = input.c_str(); std::size_t len = input.length(); assert(len < 1024); VERIFY(utf8::is_valid(p, p + len)); utf8::unchecked::utf8to32(p, p + len, std::back_inserter(utf32chars)); + + TRACE_CTOR(unistring, "std::string"); } ~unistring() { TRACE_DTOR(unistring); diff --git a/src/utils.cc b/src/utils.cc index 5a364008..17118904 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -278,16 +278,18 @@ namespace { buf << num; + string number(buf.str()); + int integer_digits = 0; // Count the number of integer digits - for (const char * p = buf.str().c_str(); *p; p++) { + for (const char * p = number.c_str(); *p; p++) { if (*p == '.') break; else if (*p != '-') integer_digits++; } - for (const char * p = buf.str().c_str(); *p; p++) { + for (const char * p = number.c_str(); *p; p++) { if (*p == '.') { obuf << *p; assert(integer_digits <= 3); @@ -335,7 +337,7 @@ namespace { void report_count_map(std::ostream& out, object_count_map& the_map) { foreach (object_count_map::value_type& pair, the_map) { - out << " " << std::right << std::setw(12); + out << " " << std::right << std::setw(18); stream_commified_number(out, pair.second.first); out << " " << std::right << std::setw(7); stream_memory_size(out, pair.second.second); @@ -433,7 +435,7 @@ void report_memory(std::ostream& out, bool report_all) out << "Live memory:" << std::endl; foreach (const memory_map::value_type& pair, *live_memory) { - out << " " << std::right << std::setw(12) << pair.first + out << " " << std::right << std::setw(18) << pair.first << " " << std::right << std::setw(7); stream_memory_size(out, pair.second.second); out << " " << std::left << pair.second.first @@ -455,7 +457,7 @@ void report_memory(std::ostream& out, bool report_all) out << "Live objects:" << std::endl; foreach (const objects_map::value_type& pair, *live_objects) { - out << " " << std::right << std::setw(12) << pair.first + out << " " << std::right << std::setw(18) << pair.first << " " << std::right << std::setw(7); stream_memory_size(out, pair.second.second); out << " " << std::left << pair.second.first diff --git a/src/value.h b/src/value.h index a95968c2..d128bb89 100644 --- a/src/value.h +++ b/src/value.h @@ -179,8 +179,8 @@ public: */ explicit storage_t(const storage_t& rhs) : type(rhs.type), refc(0) { - TRACE_CTOR(value_t::storage_t, "copy"); *this = rhs; + TRACE_CTOR(value_t::storage_t, "copy"); } storage_t& operator=(const storage_t& rhs); @@ -290,73 +290,75 @@ public: } value_t(const bool val) { - TRACE_CTOR(value_t, "const bool"); set_boolean(val); + TRACE_CTOR(value_t, "const bool"); } value_t(const datetime_t& val) { - TRACE_CTOR(value_t, "const datetime_t&"); set_datetime(val); + TRACE_CTOR(value_t, "const datetime_t&"); } value_t(const date_t& val) { - TRACE_CTOR(value_t, "const date_t&"); set_date(val); + TRACE_CTOR(value_t, "const date_t&"); } value_t(const long val) { - TRACE_CTOR(value_t, "const long"); set_long(val); + TRACE_CTOR(value_t, "const long"); } value_t(const unsigned long val) { - TRACE_CTOR(value_t, "const unsigned long"); set_amount(val); + TRACE_CTOR(value_t, "const unsigned long"); } value_t(const double val) { - TRACE_CTOR(value_t, "const double"); set_amount(val); + TRACE_CTOR(value_t, "const double"); } value_t(const amount_t& val) { - TRACE_CTOR(value_t, "const amount_t&"); set_amount(val); + TRACE_CTOR(value_t, "const amount_t&"); } value_t(const balance_t& val) { - TRACE_CTOR(value_t, "const balance_t&"); set_balance(val); + TRACE_CTOR(value_t, "const balance_t&"); } value_t(const mask_t& val) { - TRACE_CTOR(value_t, "const mask_t&"); set_mask(val); + TRACE_CTOR(value_t, "const mask_t&"); } explicit value_t(const string& val, bool literal = false) { - TRACE_CTOR(value_t, "const string&, bool"); if (literal) set_string(val); else set_amount(amount_t(val)); + + TRACE_CTOR(value_t, "const string&, bool"); } explicit value_t(const char * val, bool literal = false) { - TRACE_CTOR(value_t, "const char *"); if (literal) set_string(val); else set_amount(amount_t(val)); + + TRACE_CTOR(value_t, "const char *"); } value_t(const sequence_t& val) { - TRACE_CTOR(value_t, "const sequence_t&"); set_sequence(val); + TRACE_CTOR(value_t, "const sequence_t&"); } explicit value_t(scope_t * item) { - TRACE_CTOR(value_t, "scope_t *"); set_scope(item); + TRACE_CTOR(value_t, "scope_t *"); } #if 0 template explicit value_t(T& item) { - TRACE_CTOR(value_t, "T&"); set_any(item); + TRACE_CTOR(value_t, "T&"); } #endif @@ -375,8 +377,8 @@ public: * object. A true copy is only ever made prior to modification. */ value_t(const value_t& val) { - TRACE_CTOR(value_t, "copy"); *this = val; + TRACE_CTOR(value_t, "copy"); } value_t& operator=(const value_t& val) { if (! (this == &val || storage == val.storage)) -- cgit v1.2.3 From f76d458ab7f200bd52676fe8103fbfaae4f10488 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 20 Mar 2012 03:15:42 -0500 Subject: Strip annotations in display_filter_posts Fixes #718 --- src/filters.cc | 6 ++++-- test/regress/A560FDAD.test | 50 ++++++++++++++++++++++++++++++++++++++++++++++ test/regress/BFD3FBE1.test | 3 +-- 3 files changed, 55 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 73ee200d..b5f68907 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -522,7 +522,8 @@ bool display_filter_posts::output_rounding(post_t& post) value_t new_display_total; if (show_rounding) { - new_display_total = display_total_expr.calc(bound_scope); + new_display_total = (display_total_expr.calc(bound_scope) + .strip_annotations(report.what_to_keep())); DEBUG("filters.changed_value.rounding", "rounding.new_display_total = " << new_display_total); @@ -539,7 +540,8 @@ bool display_filter_posts::output_rounding(post_t& post) return true; } - if (value_t repriced_amount = display_amount_expr.calc(bound_scope)) { + if (value_t repriced_amount = (display_amount_expr.calc(bound_scope) + .strip_annotations(report.what_to_keep()))) { if (! last_display_total.is_null()) { DEBUG("filters.changed_value.rounding", "rounding.repriced_amount = " << repriced_amount); diff --git a/test/regress/A560FDAD.test b/test/regress/A560FDAD.test index 053f5aaf..b30ea086 100644 --- a/test/regress/A560FDAD.test +++ b/test/regress/A560FDAD.test @@ -33,4 +33,54 @@ Assets:Current test reg -X EUR -H +12-Jan-01 Opening balance Assets:Current 17.43 EUR 17.43 EUR + Assets:Investments 4959.80 EUR 4977.23 EUR + Assets:Investments 1438.34 EUR 6415.57 EUR + Equity:Opening balance -6409.77 EUR 5.80 EUR +12-Jan-01 Opening balance Assets:Pension 785.44 GBP 5.80 EUR + 785.44 GBP + Assets:Pension 97.0017 H2 5.80 EUR + 785.44 GBP + 97.0017 H2 + Assets:Pension 4.3441 H1 5.80 EUR + 785.44 GBP + 4.3441 H1 + 97.0017 H2 + Equity:Opening balance -1326.70 GBP 5.80 EUR + -541.26 GBP + 4.3441 H1 + 97.0017 H2 +12-Jan-01 Opening balance: misc Assets:Piggy bank 3.51 GBP 5.80 EUR + -537.75 GBP + 4.3441 H1 + 97.0017 H2 + Equity:Opening balance -3.51 GBP 5.80 EUR + -541.26 GBP + 4.3441 H1 + 97.0017 H2 +12-Jan-01 Opening balance Assets:Rewards 9836 AAdvantage 9836 AAdvantage + 5.80 EUR + -541.26 GBP + 4.3441 H1 + 97.0017 H2 + Equity:Opening balance -9836 AAdvantage 5.80 EUR + -541.26 GBP + 4.3441 H1 + 97.0017 H2 +12-Jan-03 Commodities revalued 0 5.80 EUR +12-Jan-03 Receivable Assets:Current 172.71 EUR 178.51 EUR + Assets:Receivable -161.06 EUR 17.45 EUR + Assets:Receivable -11.65 EUR 5.80 EUR +12-Jan-27 Test 0.01 EUR 5.81 EUR + Income:Test -3218.04 EUR -3212.23 EUR + -0.01 EUR -3212.24 EUR + Income:Test -129.16 EUR -3341.40 EUR + Assets:Foo 402.99 EUR -2938.41 EUR + Expenses:Test 19.21 EUR -2919.20 EUR + Expenses:Test 7.38 EUR -2911.82 EUR + 0.01 EUR -2911.81 EUR + Expenses:Test 304.82 EUR -2606.99 EUR + -0.01 EUR -2607.00 EUR + Assets:Current 2612.80 EUR 5.80 EUR + 0.01 EUR 5.82 EUR end test diff --git a/test/regress/BFD3FBE1.test b/test/regress/BFD3FBE1.test index 5b2f8b42..0dbda2c7 100644 --- a/test/regress/BFD3FBE1.test +++ b/test/regress/BFD3FBE1.test @@ -11,7 +11,6 @@ test reg -X EUR -H 11-Jan-01 Opening balance Assets:Investment 1658.90 EUR 1658.90 EUR Assets:Investments 124.00 EUR 1782.90 EUR Equity:Opening balance -1782.90 EUR 0 -11-Feb-10 Reimbursement: Taxi.. -0.01 EUR -0.01 EUR - Assets:A 1.80 EUR 1.80 EUR +11-Feb-10 Reimbursement: Taxi.. Assets:A 1.80 EUR 1.80 EUR Assets:B -1.80 EUR 0 end test -- cgit v1.2.3 From e3248ee5a6b2c29e1c35eb0315fd66370a117784 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 20 Mar 2012 04:56:03 -0500 Subject: Fix problems with postings --- src/annotate.cc | 3 ++- src/commodity.cc | 2 ++ src/filters.cc | 9 ++++++--- src/temps.cc | 8 ++++++-- src/temps.h | 3 ++- test/regress/A560FDAD.test | 1 - 6 files changed, 18 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/annotate.cc b/src/annotate.cc index 25f0e582..98635ad7 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -328,7 +328,8 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) if ((keep_price && details.price) || (keep_date && details.date) || (keep_tag && details.tag) || - details.value_expr) + (details.value_expr && + ! details.has_flags(ANNOTATION_VALUE_EXPR_CALCULATED))) { new_comm = pool().find_or_create (referent(), annotation_t(keep_price ? details.price : none, diff --git a/src/commodity.cc b/src/commodity.cc index 5335d8a8..51b8f29c 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -221,6 +221,8 @@ commodity_t& commodity_t::nail_down(const expr_t& expr) { annotation_t new_details; new_details.value_expr = expr; + new_details.add_flags(ANNOTATION_VALUE_EXPR_CALCULATED); + commodity_t * new_comm = commodity_pool_t::current_pool->find_or_create(symbol(), new_details); return *new_comm; diff --git a/src/filters.cc b/src/filters.cc index 02dc392b..58e5fcaf 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -340,9 +340,10 @@ namespace { const bool act_date_p = true, const value_t& total = value_t(), const bool direct_amount = false, - const bool mark_visited = false) + const bool mark_visited = false, + const bool bidir_link = true) { - post_t& post = temps.create_post(*xact, account); + post_t& post = temps.create_post(*xact, account, bidir_link); post.add_flags(ITEM_GENERATED); // If the account for this post is all virtual, then report the post as @@ -566,7 +567,9 @@ bool display_filter_posts::output_rounding(post_t& post) /* date= */ date_t(), /* act_date_p= */ true, /* total= */ precise_display_total, - /* direct_amount= */ true); + /* direct_amount= */ true, + /* mark_visited= */ false, + /* bidir_link= */ false); } } if (show_rounding) diff --git a/src/temps.cc b/src/temps.cc index cb471d41..881077f6 100644 --- a/src/temps.cc +++ b/src/temps.cc @@ -81,7 +81,8 @@ post_t& temporaries_t::copy_post(post_t& origin, xact_t& xact, return temp; } -post_t& temporaries_t::create_post(xact_t& xact, account_t * account) +post_t& temporaries_t::create_post(xact_t& xact, account_t * account, + bool bidir_link) { if (! post_temps) post_temps = std::list(); @@ -93,7 +94,10 @@ post_t& temporaries_t::create_post(xact_t& xact, account_t * account) temp.account = account; temp.account->add_post(&temp); - xact.add_post(&temp); + if (bidir_link) + xact.add_post(&temp); + else + temp.xact = &xact; return temp; } diff --git a/src/temps.h b/src/temps.h index f41c487c..daa1493b 100644 --- a/src/temps.h +++ b/src/temps.h @@ -66,7 +66,8 @@ public: } post_t& copy_post(post_t& origin, xact_t& xact, account_t * account = NULL); - post_t& create_post(xact_t& xact, account_t * account); + post_t& create_post(xact_t& xact, account_t * account, + bool bidir_link = true); post_t& last_post() { return post_temps->back(); } diff --git a/test/regress/A560FDAD.test b/test/regress/A560FDAD.test index b30ea086..ee19e71e 100644 --- a/test/regress/A560FDAD.test +++ b/test/regress/A560FDAD.test @@ -82,5 +82,4 @@ test reg -X EUR -H Expenses:Test 304.82 EUR -2606.99 EUR -0.01 EUR -2607.00 EUR Assets:Current 2612.80 EUR 5.80 EUR - 0.01 EUR 5.82 EUR end test -- cgit v1.2.3 From 3fb7490bc91e65050e090ab7fa2dcae230d1697e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 20 Mar 2012 05:03:18 -0500 Subject: Never output calculate commodity valuation expressions --- src/annotate.cc | 8 ++------ src/post.cc | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/annotate.cc b/src/annotate.cc index 98635ad7..c9fd6d3f 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -212,8 +212,7 @@ void annotation_t::print(std::ostream& out, bool keep_base, (! no_computed_annotations || ! has_flags(ANNOTATION_TAG_CALCULATED))) out << " (" << *tag << ')'; - if (value_expr && (! no_computed_annotations || - ! has_flags(ANNOTATION_VALUE_EXPR_CALCULATED))) + if (value_expr && ! has_flags(ANNOTATION_VALUE_EXPR_CALCULATED)) out << " ((" << *value_expr << "))"; } @@ -327,10 +326,7 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) if ((keep_price && details.price) || (keep_date && details.date) || - (keep_tag && details.tag) || - (details.value_expr && - ! details.has_flags(ANNOTATION_VALUE_EXPR_CALCULATED))) - { + (keep_tag && details.tag)) { new_comm = pool().find_or_create (referent(), annotation_t(keep_price ? details.price : none, keep_date ? details.date : none, diff --git a/src/post.cc b/src/post.cc index d5e0d9bc..2b84fc06 100644 --- a/src/post.cc +++ b/src/post.cc @@ -688,6 +688,7 @@ void extend_post(post_t& post, journal_t& journal) if (! details) { annotation_t new_details; new_details.value_expr = value_expr; + commodity_t * new_comm = commodity_pool_t::current_pool->find_or_create(comm, new_details); post.amount.set_commodity(*new_comm); -- cgit v1.2.3 From 5addacfbf21250204b8db25f0a4890c1299cb891 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 22 Mar 2012 23:42:18 -0500 Subject: Fixed an interaction with equity and virtual accounts Fixes #686 --- src/filters.cc | 20 ++++++++++++++++++-- src/filters.h | 17 ++++++++++++----- test/regress/012ADB60.test | 24 ++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 test/regress/012ADB60.test (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 58e5fcaf..32b3ec3a 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -900,11 +900,19 @@ void subtotal_posts::operator()(post_t& post) #if defined(DEBUG_ON) std::pair result = #endif - values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp))); + values.insert(values_pair + (acct->fullname(), + acct_value_t(acct, temp, post.has_flags(POST_VIRTUAL), + post.has_flags(POST_MUST_BALANCE)))); #if defined(DEBUG_ON) assert(result.second); #endif } else { + if (post.has_flags(POST_VIRTUAL) != (*i).second.is_virtual) + throw_(std::logic_error, + _("'equity' cannot accept virtual and " + "non-virtual postings to the same account")); + post.add_to_value((*i).second.value, amount_expr); } @@ -1062,10 +1070,17 @@ void posts_as_equity::report_subtotal() /* act_date_p= */ false); } } - total += value; + + if (! pair.second.is_virtual || pair.second.must_balance) + total += value; } values.clear(); +#if 1 + // This last part isn't really needed, since an Equity:Opening + // Balances posting with a null amount will automatically balance with + // all the other postings generated. But it does make the full + // balancing amount clearer to the user. if (! total.is_zero()) { if (total.is_balance()) { foreach (const balance_t::amounts_map::value_type& pair, @@ -1082,6 +1097,7 @@ void posts_as_equity::report_subtotal() (*handler)(balance_post); } } +#endif } void by_payee_posts::flush() diff --git a/src/filters.h b/src/filters.h index ab226429..1dad8852 100644 --- a/src/filters.h +++ b/src/filters.h @@ -640,15 +640,22 @@ protected: public: account_t * account; value_t value; + bool is_virtual; + bool must_balance; - acct_value_t(account_t * a) : account(a) { - TRACE_CTOR(acct_value_t, "account_t *"); + acct_value_t(account_t * a, bool _is_virtual = false, + bool _must_balance = false) + : account(a), is_virtual(_is_virtual), must_balance(_must_balance) { + TRACE_CTOR(acct_value_t, "account_t *, bool, bool"); } - acct_value_t(account_t * a, value_t& v) : account(a), value(v) { - TRACE_CTOR(acct_value_t, "account_t *, value_t&"); + acct_value_t(account_t * a, value_t& v, bool _is_virtual = false, + bool _must_balance = false) + : account(a), value(v), is_virtual(_is_virtual), + must_balance(_must_balance) { + TRACE_CTOR(acct_value_t, "account_t *, value_t&, bool, bool"); } acct_value_t(const acct_value_t& av) - : account(av.account), value(av.value) { + : account(av.account), value(av.value), is_virtual(av.is_virtual) { TRACE_CTOR(acct_value_t, "copy"); } ~acct_value_t() throw() { diff --git a/test/regress/012ADB60.test b/test/regress/012ADB60.test new file mode 100644 index 00000000..443b9e5b --- /dev/null +++ b/test/regress/012ADB60.test @@ -0,0 +1,24 @@ +2005/01/03 * Pay Credit card + Liabilities:CredCard $1,000.00 ; Electronic/ACH Debit + Assets:Current:Checking ; Electronic/ACH Debit + (Virtualaccount) $1,000.00 + +2006/01/03 Gift shop + Expenses:Gifts $46.50 + * Liabilities:CredCard + +2006/01/03 Bike shop + Expenses:Misc $199.00 + * Liabilities:CredCard + (testvirtual) $184.72 + +2006/01/04 Store + Expenses:Misc $49.95 + * Liabilities:CredCard + +test equity -e 2006 +2005/01/03 Opening Balances + Assets:Current:Checking $-1,000.00 + Liabilities:CredCard $1,000.00 + (Virtualaccount) $1,000.00 +end test -- cgit v1.2.3 From 4b057599626538a0d1463c63f2110d2fe5baa2e6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 23 Mar 2012 16:46:11 -0500 Subject: Fixed sorting of equity output --- src/balance.cc | 102 +++++++++++++++++++++++++++++------------- src/balance.h | 8 ++++ src/filters.cc | 56 ++++++++++++++++------- test/baseline/opt-equity.test | 4 +- 4 files changed, 121 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/balance.cc b/src/balance.cc index 5c57cd99..cd25eede 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -240,17 +240,8 @@ balance_t::strip_annotations(const keep_details_t& what_to_keep) const return temp; } -void balance_t::print(std::ostream& out, - const int first_width, - const int latter_width, - const uint_least8_t flags) const +void balance_t::map_sorted_amounts(function fn) const { - bool first = true; - int lwidth = latter_width; - - if (lwidth == -1) - lwidth = first_width; - typedef std::vector amounts_array; amounts_array sorted; @@ -261,30 +252,79 @@ void balance_t::print(std::ostream& out, std::stable_sort(sorted.begin(), sorted.end(), commodity_t::compare_by_commodity()); - foreach (const amount_t * amount, sorted) { - int width; - if (! first) { - out << std::endl; - width = lwidth; - } else { - first = false; - width = first_width; + foreach (const amount_t * amount, sorted) + fn(*amount); +} + +namespace { + struct print_amount_from_balance + { + std::ostream& out; + bool& first; + int fwidth; + int lwidth; + uint_least8_t flags; + + explicit print_amount_from_balance(std::ostream& _out, + bool& _first, + int _fwidth, int _lwidth, + uint_least8_t _flags) + : out(_out), first(_first), fwidth(_fwidth), lwidth(_lwidth), + flags(_flags) { + TRACE_CTOR(print_amount_from_balance, + "ostream&, int, int, uint_least8_t"); + } + print_amount_from_balance(const print_amount_from_balance& other) + : out(other.out), first(other.first), fwidth(other.fwidth), + lwidth(other.lwidth), flags(other.flags) { + TRACE_CTOR(print_amount_from_balance, "copy"); + } + ~print_amount_from_balance() throw() { + TRACE_DTOR(print_amount_from_balance); } - std::ostringstream buf; - amount->print(buf, flags); - justify(out, buf.str(), width, flags & AMOUNT_PRINT_RIGHT_JUSTIFY, - flags & AMOUNT_PRINT_COLORIZE && amount->sign() < 0); - } + void operator()(const amount_t& amount) { + int width; + if (! first) { + out << std::endl; + width = lwidth; + } else { + first = false; + width = fwidth; + } + + std::ostringstream buf; + amount.print(buf, flags); + + justify(out, buf.str(), width, + flags & AMOUNT_PRINT_RIGHT_JUSTIFY, + flags & AMOUNT_PRINT_COLORIZE && amount.sign() < 0); + } - if (first) { - out.width(first_width); - if (flags & AMOUNT_PRINT_RIGHT_JUSTIFY) - out << std::right; - else - out << std::left; - out << 0; - } + void close() { + out.width(fwidth); + if (flags & AMOUNT_PRINT_RIGHT_JUSTIFY) + out << std::right; + else + out << std::left; + out << 0; + } + }; +} + +void balance_t::print(std::ostream& out, + const int first_width, + const int latter_width, + const uint_least8_t flags) const +{ + bool first = true; + print_amount_from_balance + amount_printer(out, first, first_width, + latter_width == 1 ? first_width : latter_width, flags); + map_sorted_amounts(amount_printer); + + if (first) + amount_printer.close(); } void to_xml(std::ostream& out, const balance_t& bal) diff --git a/src/balance.h b/src/balance.h index 11c370bb..704b4072 100644 --- a/src/balance.h +++ b/src/balance.h @@ -506,6 +506,14 @@ public: */ balance_t strip_annotations(const keep_details_t& what_to_keep) const; + /** + * Iteration primitives. `map_sorted_amounts' allows one to visit + * each amount in balance in the proper order for displaying to the + * user. Mostly used by `print' and other routinse where the sort + * order of the amounts' commodities is significant. + */ + void map_sorted_amounts(function fn) const; + /** * Printing methods. A balance may be output to a stream using the * `print' method. There is also a global operator<< defined which diff --git a/src/filters.cc b/src/filters.cc index 32b3ec3a..a665f9e6 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1030,6 +1030,40 @@ void interval_posts::flush() subtotal_posts::flush(); } +namespace { + struct create_post_from_amount + { + post_handler_ptr handler; + xact_t& xact; + account_t& balance_account; + temporaries_t& temps; + + explicit create_post_from_amount(post_handler_ptr _handler, + xact_t& _xact, + account_t& _balance_account, + temporaries_t& _temps) + : handler(_handler), xact(_xact), + balance_account(_balance_account), temps(_temps) { + TRACE_CTOR(create_post_from_amount, + "post_handler_ptr, xact_t&, account_t&, temporaries_t&"); + } + create_post_from_amount(const create_post_from_amount& other) + : handler(other.handler), xact(other.xact), + balance_account(other.balance_account), temps(other.temps) { + TRACE_CTOR(create_post_from_amount, "copy"); + } + ~create_post_from_amount() throw() { + TRACE_DTOR(create_post_from_amount); + } + + void operator()(const amount_t& amount) { + post_t& balance_post = temps.create_post(xact, &balance_account); + balance_post.amount = - amount; + (*handler)(balance_post); + } + }; +} + void posts_as_equity::report_subtotal() { date_t finish; @@ -1076,28 +1110,18 @@ void posts_as_equity::report_subtotal() } values.clear(); -#if 1 // This last part isn't really needed, since an Equity:Opening // Balances posting with a null amount will automatically balance with // all the other postings generated. But it does make the full // balancing amount clearer to the user. if (! total.is_zero()) { - if (total.is_balance()) { - foreach (const balance_t::amounts_map::value_type& pair, - total.as_balance().amounts) { - if (! pair.second.is_zero()) { - post_t& balance_post = temps.create_post(xact, balance_account); - balance_post.amount = - pair.second; - (*handler)(balance_post); - } - } - } else { - post_t& balance_post = temps.create_post(xact, balance_account); - balance_post.amount = - total.to_amount(); - (*handler)(balance_post); - } + create_post_from_amount post_creator(handler, xact, + *balance_account, temps); + if (total.is_balance()) + total.as_balance_lval().map_sorted_amounts(post_creator); + else + post_creator(total.to_amount()); } -#endif } void by_payee_posts::flush() diff --git a/test/baseline/opt-equity.test b/test/baseline/opt-equity.test index 511333cc..35ea6b1e 100644 --- a/test/baseline/opt-equity.test +++ b/test/baseline/opt-equity.test @@ -41,9 +41,9 @@ test equity --lot-prices Assets:Bank -3.80 GBP Assets:Broker 2 AAA {0.90 GBP} Assets:Broker 2 AAA {1.00 GBP} - Equity:Opening Balances 3.80 GBP Equity:Opening Balances -2 AAA {0.90 GBP} Equity:Opening Balances -2 AAA {1.00 GBP} + Equity:Opening Balances 3.80 GBP end test test equity --lots @@ -51,8 +51,8 @@ test equity --lots Assets:Bank -3.80 GBP Assets:Broker 2 AAA {0.90 GBP} [2011/03/04] Assets:Broker 2 AAA {1.00 GBP} [2011/03/05] - Equity:Opening Balances 3.80 GBP Equity:Opening Balances -2 AAA {0.90 GBP} [2011/03/04] Equity:Opening Balances -2 AAA {1.00 GBP} [2011/03/05] + Equity:Opening Balances 3.80 GBP end test -- cgit v1.2.3 From cee47cc49577cc39863e020809dec9efc507c640 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 23 Mar 2012 16:59:06 -0500 Subject: Simplified some code in xact.cc --- src/balance.cc | 27 ++++++++++----- src/xact.cc | 104 +++++++++++++++++++++++---------------------------------- 2 files changed, 60 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/balance.cc b/src/balance.cc index cd25eede..ded3d38a 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -242,18 +242,27 @@ balance_t::strip_annotations(const keep_details_t& what_to_keep) const void balance_t::map_sorted_amounts(function fn) const { - typedef std::vector amounts_array; - amounts_array sorted; + if (! amounts.empty()) { + if (amounts.size() == 1) { + const amount_t& amount((*amounts.begin()).second); + if (amount) + fn(amount); + } + else { + typedef std::vector amounts_array; + amounts_array sorted; - foreach (const amounts_map::value_type& pair, amounts) - if (pair.second) - sorted.push_back(&pair.second); + foreach (const amounts_map::value_type& pair, amounts) + if (pair.second) + sorted.push_back(&pair.second); - std::stable_sort(sorted.begin(), sorted.end(), - commodity_t::compare_by_commodity()); + std::stable_sort(sorted.begin(), sorted.end(), + commodity_t::compare_by_commodity()); - foreach (const amount_t * amount, sorted) - fn(*amount); + foreach (const amount_t * amount, sorted) + fn(*amount); + } + } } namespace { diff --git a/src/xact.cc b/src/xact.cc index f514776f..8085b104 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -117,6 +117,38 @@ namespace { return (std::isdigit(name[len - 1]) || name[len - 1] == ')' || name[len - 1] == '}' || name[len - 1] == ']'); } + + struct add_balancing_post + { + bool first; + xact_base_t& xact; + post_t * null_post; + + explicit add_balancing_post(xact_base_t& _xact, post_t * _null_post) + : first(true), xact(_xact), null_post(_null_post) { + TRACE_CTOR(add_balancing_post, "xact_base_t&, post_t *"); + } + add_balancing_post(const add_balancing_post& other) + : first(other.first), xact(other.xact), null_post(other.null_post) { + TRACE_CTOR(add_balancing_post, "copy"); + } + ~add_balancing_post() throw() { + TRACE_DTOR(add_balancing_post); + } + + void operator()(const amount_t& amount) { + if (first) { + null_post->amount = amount.negated(); + null_post->add_flags(POST_CALCULATED); + first = false; + } else { + unique_ptr p(new post_t(null_post->account, amount.negated(), + ITEM_GENERATED | POST_CALCULATED)); + p->set_state(null_post->state()); + xact.add_post(p.release()); + } + } + }; } bool xact_base_t::finalize() @@ -347,69 +379,17 @@ bool xact_base_t::finalize() // generated to balance them all. DEBUG("xact.finalize", "there was a null posting"); - - if (balance.is_balance()) { - const balance_t& bal(balance.as_balance()); -#if 1 - typedef std::map, - amount_t> sorted_amounts_map; - sorted_amounts_map samp; - foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { -#if defined(DEBUG_ON) - std::pair result = -#endif - samp.insert(sorted_amounts_map::value_type - (sorted_amounts_map::key_type - (pair.first->symbol(), - pair.first->has_annotation() ? - as_annotated_commodity(*pair.first).details : - annotation_t()), - pair.second)); -#if defined(DEBUG_ON) - assert(result.second); -#endif - } - - bool first = true; - foreach (sorted_amounts_map::value_type& pair, samp) { - if (first) { - null_post->amount = pair.second.negated(); - null_post->add_flags(POST_CALCULATED); - first = false; - } else { - post_t * p = new post_t(null_post->account, pair.second.negated(), - ITEM_GENERATED | POST_CALCULATED); - p->set_state(null_post->state()); - add_post(p); - } - } -#else - bool first = true; - foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { - if (first) { - null_post->amount = pair.second.negated(); - null_post->add_flags(POST_CALCULATED); - first = false; - } else { - post_t * p = new post_t(null_post->account, pair.second.negated(), - ITEM_GENERATED | POST_CALCULATED); - p->set_state(null_post->state()); - add_post(p); - } - } -#endif - } - else if (balance.is_amount()) { - null_post->amount = balance.as_amount().negated(); - null_post->add_flags(POST_CALCULATED); - } - else if (balance.is_long()) { - null_post->amount = amount_t(- balance.as_long()); - null_post->add_flags(POST_CALCULATED); - } - else if (! balance.is_null() && ! balance.is_realzero()) { + add_balancing_post post_adder(*this, null_post); + + if (balance.is_balance()) + balance.as_balance_lval().map_sorted_amounts(post_adder); + else if (balance.is_amount()) + post_adder(balance.as_amount_lval()); + else if (balance.is_long()) + post_adder(balance.to_amount()); + else if (! balance.is_null() && ! balance.is_realzero()) throw_(balance_error, _("Transaction does not balance")); - } + balance = NULL_VALUE; } -- cgit v1.2.3 From a47625be3571262f0c36324d6793c55645172dc1 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Sun, 25 Mar 2012 11:50:16 +0100 Subject: Make pricesdb an alias for pricedb for backwards compatibility with ledger 2 ledger 2.2 introduced the pricesdb command. This was renamed to pricedb in ledger 3. Allow pricesdb as an alternative to pricedb to provide backwards compatibility. Fixes bug #728 --- src/report.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 21fc9c1b..97d2df02 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1616,7 +1616,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, else if (is_eq(p, "prices")) { return FORMATTED_COMMODITIES_REPORTER(prices_format_); } - else if (is_eq(p, "pricedb")) { + else if (is_eq(p, "pricedb") || is_eq(p, "pricesdb")) { return FORMATTED_COMMODITIES_REPORTER(pricedb_format_); } else if (is_eq(p, "pricemap")) { -- cgit v1.2.3 From 902ab72eb44296ff41a78fcda15a26a99ef87b8d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 28 Mar 2012 03:02:31 -0500 Subject: Forgot to copy two members in post_t's copy-ctor --- src/post.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/post.h b/src/post.h index 76a89a33..e2d9499f 100644 --- a/src/post.h +++ b/src/post.h @@ -73,8 +73,7 @@ public: post_t(account_t * _account = NULL, flags_t _flags = ITEM_NORMAL) - : item_t(_flags), - xact(NULL), account(_account) + : item_t(_flags), xact(NULL), account(_account) { TRACE_CTOR(post_t, "account_t *, flags_t"); } @@ -82,8 +81,7 @@ public: const amount_t& _amount, flags_t _flags = ITEM_NORMAL, const optional& _note = none) - : item_t(_flags, _note), - xact(NULL), account(_account), amount(_amount) + : item_t(_flags, _note), xact(NULL), account(_account), amount(_amount) { TRACE_CTOR(post_t, "account_t *, const amount_t&, flags_t, const optional&"); } @@ -94,6 +92,8 @@ public: amount(post.amount), cost(post.cost), assigned_amount(post.assigned_amount), + checkin(post.checkin), + checkout(post.checkout), xdata_(post.xdata_) { copy_details(post); -- cgit v1.2.3 From 08f65eeadc288ecde4b8fb281e477958c8ae7cd5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 29 Mar 2012 15:35:20 -0500 Subject: Allow serialization to be enabled again --- acprep | 6 ++ src/scope.h | 2 +- src/system.hh.in | 13 ++++- src/xact.h | 15 +++++ tools/configure.ac | 168 +++++++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 169 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/acprep b/acprep index e79fae5b..ddf7639d 100755 --- a/acprep +++ b/acprep @@ -508,6 +508,12 @@ class PrepareBuild(CommandLineApp): op.add_option('', '--no-python', action='store_true', dest='no_python', default=False, help='Do not enable Python support by default') + op.add_option('', '--cache', action='store_true', + dest='enable_cache', default=False, + help='Enable use of Boost.Serialization (--cache)') + op.add_option('', '--doxygen', action='store_true', + dest='enable_doxygen', default=False, + help='Enable use of Doxygen to build ref manual ("make docs")') op.add_option('', '--enable-cache', action='store_true', dest='enable_cache', default=False, help='Enable use of Boost.Serialization (--cache)') diff --git a/src/scope.h b/src/scope.h index acaf7194..c43d73d6 100644 --- a/src/scope.h +++ b/src/scope.h @@ -483,7 +483,7 @@ public: #if defined(HAVE_BOOST_SERIALIZATION) protected: - explicit call_scope_t() { + explicit call_scope_t() : depth(0) { TRACE_CTOR(call_scope_t, ""); } diff --git a/src/system.hh.in b/src/system.hh.in index a38deb1f..552a591a 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -246,11 +246,18 @@ void serialize(Archive& ar, boost::intrusive_ptr& ptr, const unsigned int) } } -template -void serialize(Archive&, boost::function&, const unsigned int) -{ +template +void serialize(Archive&, boost::any&, const unsigned int) { + // jww (2012-03-29): Should we really ignore any fields entirely? + // These occur inside value_t::storage_t::data's variant. } +template +void serialize(Archive&, boost::blank&, const unsigned int) {} + +template +void serialize(Archive&, boost::function&, const unsigned int) {} + template void serialize(Archive& ar, istream_pos_type& pos, const unsigned int) { diff --git a/src/xact.h b/src/xact.h index df82258d..ce00242e 100644 --- a/src/xact.h +++ b/src/xact.h @@ -168,6 +168,21 @@ public: bool _overwrite_existing) : tag_data(_tag_data), overwrite_existing(_overwrite_existing), apply_to_post(NULL) {} + +#if defined(HAVE_BOOST_SERIALIZATION) +private: + /** Serialization. */ + deferred_tag_data_t() : apply_to_post(NULL) {} + + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int /* version */) { + ar & tag_data; + ar & overwrite_existing; + ar & apply_to_post; + } +#endif // HAVE_BOOST_SERIALIZATION }; typedef std::list deferred_notes_list; diff --git a/tools/configure.ac b/tools/configure.ac index e078ebc4..f7623f62 100644 --- a/tools/configure.ac +++ b/tools/configure.ac @@ -332,37 +332,143 @@ else fi # check for boost_serialization -#AC_CACHE_CHECK( -# [if boost_serialization is available], -# [boost_serialization_cpplib_avail_cv_], -# [boost_serialization_save_libs=$LIBS -# LIBS="-lboost_serialization$BOOST_SUFFIX -lboost_system$BOOST_SUFFIX $LIBS" -# AC_LANG_PUSH(C++) -# AC_LINK_IFELSE( -# [AC_LANG_PROGRAM( -# [[#include -# #include -# struct foo { -# int a; -# template -# void serialize(Archive & ar, const unsigned int) { -# ar & a; -# } -# };]], -# [[boost::archive::binary_oarchive oa(std::cout); -# foo x; -# oa << x;]])], -# [boost_serialization_cpplib_avail_cv_=true], -# [boost_serialization_cpplib_avail_cv_=false]) -# AC_LANG_POP -# LIBS=$boost_serialization_save_libs]) -# -#if [test x$boost_serialization_cpplib_avail_cv_ = xtrue -a x$cache = xtrue]; then -# AC_DEFINE([HAVE_BOOST_SERIALIZATION], [1], [Whether Boost.Serialization is available]) -# LIBS="-lboost_serialization$BOOST_SUFFIX $LIBS" -#fi -#AM_CONDITIONAL(HAVE_BOOST_SERIALIZATION, test x$boost_serialization_cpplib_avail_cv_ = xtrue -a x$cache = xtrue) -AM_CONDITIONAL(HAVE_BOOST_SERIALIZATION, false) +AC_CACHE_CHECK( + [if boost_serialization is available], + [boost_serialization_cpplib_avail_cv_], + [boost_serialization_save_libs=$LIBS + LIBS="-lboost_serialization$BOOST_SUFFIX -lboost_system$BOOST_SUFFIX $LIBS" + AC_LANG_PUSH(C++) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include + struct foo { + int a; + template + void serialize(Archive & ar, const unsigned int) { + ar & a; + } + };]], + [[boost::archive::binary_oarchive oa(std::cout); + foo x; + oa << x;]])], + [boost_serialization_cpplib_avail_cv_=true], + [boost_serialization_cpplib_avail_cv_=false]) + AC_LANG_POP + LIBS=$boost_serialization_save_libs]) + +if [test x$boost_serialization_cpplib_avail_cv_ = xtrue -a x$cache = xtrue]; then + AC_DEFINE([HAVE_BOOST_SERIALIZATION], [1], [Whether Boost.Serialization is available]) + LIBS="-lboost_serialization$BOOST_SUFFIX $LIBS" +fi +AM_CONDITIONAL(HAVE_BOOST_SERIALIZATION, test x$boost_serialization_cpplib_avail_cv_ = xtrue -a x$cache = xtrue) + +# check for expat or xmlparse +AC_ARG_ENABLE(xml, + [ --enable-xml Turn on support for XML parsing], + [case "${enableval}" in + yes) xml=true ;; + no) xml=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-xml) ;; + esac],[xml=true]) +AM_CONDITIONAL(USE_XML, test x$xml = xtrue) + +if [test x$xml = xtrue ]; then + AC_CACHE_CHECK( + [if libexpat is available], + [libexpat_avail_cv_], + [libexpat_save_libs=$LIBS + LIBS="-lexpat $LIBS" + AC_LANG_PUSH(C++) + AC_TRY_LINK( + [#include + extern "C" { + #include // expat XML parser + }], + [XML_Parser parser = XML_ParserCreate(NULL); + return parser != NULL;], + [libexpat_avail_cv_=true], + [libexpat_avail_cv_=false]) + AC_LANG_POP + LIBS=$libexpat_save_libs]) + + if [test x$libexpat_avail_cv_ = xtrue ]; then + AM_CONDITIONAL(HAVE_EXPAT, true) + LIBS="-lexpat $LIBS" + else + AM_CONDITIONAL(HAVE_EXPAT, false) + fi +else + AM_CONDITIONAL(HAVE_EXPAT, false) +fi + +if [test x$xml = xtrue ]; then + if [test x$libexpat_avail_cv_ = xfalse ]; then + AC_CACHE_CHECK( + [if libxmlparse is available], + [libxmlparse_avail_cv_], + [libxmlparse_save_libs=$LIBS + LIBS="-lxmlparse -lxmltok $LIBS" + AC_LANG_PUSH(C++) + AC_TRY_LINK( + [#include + extern "C" { + #include // expat XML parser + }], + [XML_Parser parser = XML_ParserCreate(NULL); + return parser != NULL;], + [libxmlparse_avail_cv_=true], + [libxmlparse_avail_cv_=false]) + AC_LANG_POP + LIBS=$libxmlparse_save_libs]) + + if [test x$libxmlparse_avail_cv_ = xtrue ]; then + AM_CONDITIONAL(HAVE_XMLPARSE, true) + LIBS="-lxmlparse -lxmltok $LIBS" + else + AM_CONDITIONAL(HAVE_XMLPARSE, false) + fi + else + AM_CONDITIONAL(HAVE_XMLPARSE, false) + fi +else + AM_CONDITIONAL(HAVE_XMLPARSE, false) +fi + +# check for libofx +AC_ARG_ENABLE(ofx, + [ --enable-ofx Turn on support for OFX/OCF parsing], + [case "${enableval}" in + yes) ofx=true ;; + no) ofx=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-ofx) ;; + esac],[ofx=true]) +AM_CONDITIONAL(USE_OFX, test x$ofx = xtrue) + +if [test x$ofx = xtrue ]; then + AC_CACHE_CHECK( + [if libofx is available], + [libofx_avail_cv_], + [libofx_save_libs=$LIBS + LIBS="-lofx $LIBS" + AC_LANG_PUSH(C++) + AC_TRY_LINK( + [#include ], + [ LibofxContextPtr libofx_context = libofx_get_new_context();], + [libofx_avail_cv_=true], + [libofx_avail_cv_=false]) + AC_LANG_POP + LIBS=$libofx_save_libs]) + + if [test x$libofx_avail_cv_ = xtrue ]; then + AM_CONDITIONAL(HAVE_LIBOFX, true) + LIBS="-lofx $LIBS" + else + AM_CONDITIONAL(HAVE_LIBOFX, false) + fi +else + AM_CONDITIONAL(HAVE_LIBOFX, false) +fi # check for Python if [ test x$python = xtrue ]; then -- cgit v1.2.3 From 7422fa5f3e2505dfa8f5494e4ce9e987440a57a1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 29 Mar 2012 15:56:05 -0500 Subject: Allow post.cost to work in Python --- src/py_commodity.cc | 18 ++++++++++++------ src/py_item.cc | 18 ++++++++++++------ src/py_post.cc | 12 ++++++++---- 3 files changed, 32 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/py_commodity.cc b/src/py_commodity.cc index c75b5e64..b283efcc 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -258,8 +258,10 @@ void export_commodity() make_getter(&commodity_pool_t::keep_base), make_setter(&commodity_pool_t::keep_base)) .add_property("price_db", - make_getter(&commodity_pool_t::price_db), - make_setter(&commodity_pool_t::price_db)) + make_getter(&commodity_pool_t::price_db, + return_value_policy()), + make_setter(&commodity_pool_t::price_db, + return_value_policy())) .add_property("quote_leeway", make_getter(&commodity_pool_t::quote_leeway), make_setter(&commodity_pool_t::quote_leeway)) @@ -390,11 +392,15 @@ void export_commodity() .add_property("price", py_price, py_set_price) .add_property("date", - make_getter(&annotation_t::date), - make_setter(&annotation_t::date)) + make_getter(&annotation_t::date, + return_value_policy()), + make_setter(&annotation_t::date, + return_value_policy())) .add_property("tag", - make_getter(&annotation_t::tag), - make_setter(&annotation_t::tag)) + make_getter(&annotation_t::tag, + return_value_policy()), + make_setter(&annotation_t::tag, + return_value_policy())) .def("__nonzero__", &annotation_t::operator bool) diff --git a/src/py_item.cc b/src/py_item.cc index e3e49457..893ddcfa 100644 --- a/src/py_item.cc +++ b/src/py_item.cc @@ -120,14 +120,20 @@ void export_item() #endif .add_property("note", - make_getter(&item_t::note), - make_setter(&item_t::note)) + make_getter(&item_t::note, + return_value_policy()), + make_setter(&item_t::note, + return_value_policy())) .add_property("pos", - make_getter(&item_t::pos), - make_setter(&item_t::pos)) + make_getter(&item_t::pos, + return_value_policy()), + make_setter(&item_t::pos, + return_value_policy())) .add_property("metadata", - make_getter(&item_t::metadata), - make_setter(&item_t::metadata)) + make_getter(&item_t::metadata, + return_value_policy()), + make_setter(&item_t::metadata, + return_value_policy())) .def("copy_details", &item_t::copy_details) diff --git a/src/py_post.cc b/src/py_post.cc index 2789082e..ce3bd71b 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -149,11 +149,15 @@ void export_post() make_getter(&post_t::amount), make_setter(&post_t::amount)) .add_property("cost", - make_getter(&post_t::cost), - make_setter(&post_t::cost)) + make_getter(&post_t::cost, + return_value_policy()), + make_setter(&post_t::cost, + return_value_policy())) .add_property("assigned_amount", - make_getter(&post_t::assigned_amount), - make_setter(&post_t::assigned_amount)) + make_getter(&post_t::assigned_amount, + return_value_policy()), + make_setter(&post_t::assigned_amount, + return_value_policy())) .def("has_tag", py_has_tag_1s) .def("has_tag", py_has_tag_1m) -- cgit v1.2.3 From 0f180b917ae00b7b247db367020fd29823000877 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 29 Mar 2012 16:25:22 -0500 Subject: Improved some error messages --- src/amount.cc | 18 ++++++++---------- src/annotate.h | 8 ++++++++ src/commodity.cc | 2 +- src/commodity.h | 5 +++-- 4 files changed, 20 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 8c5ae574..2f7b434e 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -398,8 +398,8 @@ int amount_t::compare(const amount_t& amt) const if (has_commodity() && amt.has_commodity() && commodity() != amt.commodity()) throw_(amount_error, - _("Cannot compare amounts with different commodities: %1 and %2") - << commodity().symbol() << amt.commodity().symbol()); + _("Cannot compare amounts with different commodities: '%1' and '%2'") + << commodity() << amt.commodity()); return mpq_cmp(MP(quantity), MP(amt.quantity)); } @@ -430,12 +430,11 @@ amount_t& amount_t::operator+=(const amount_t& amt) throw_(amount_error, _("Cannot add two uninitialized amounts")); } - if (has_commodity() && amt.has_commodity() && - commodity() != amt.commodity()) + if (has_commodity() && amt.has_commodity() && commodity() != amt.commodity()) { throw_(amount_error, - _("Adding amounts with different commodities: %1 != %2") - << (has_commodity() ? commodity().symbol() : _("NONE")) - << (amt.has_commodity() ? amt.commodity().symbol() : _("NONE"))); + _("Adding amounts with different commodities: '%1' != '%2'") + << commodity() << amt.commodity()); + } _dup(); @@ -464,9 +463,8 @@ amount_t& amount_t::operator-=(const amount_t& amt) if (has_commodity() && amt.has_commodity() && commodity() != amt.commodity()) throw_(amount_error, - _("Subtracting amounts with different commodities: %1 != %2") - << (has_commodity() ? commodity().symbol() : _("NONE")) - << (amt.has_commodity() ? amt.commodity().symbol() : _("NONE"))); + _("Subtracting amounts with different commodities: '%1' != '%2'") + << commodity() << amt.commodity()); _dup(); diff --git a/src/annotate.h b/src/annotate.h index 85a34662..878d6148 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -262,6 +262,14 @@ public: const datetime_t& oldest = datetime_t()) const; virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); + + virtual void print(std::ostream& out, bool elide_quotes = false, + bool print_annotations = false) const { + commodity_t::print(out, elide_quotes); + if (print_annotations) + write_annotations(out); + } + virtual void write_annotations(std::ostream& out, bool no_computed_annotations = false) const; diff --git a/src/commodity.cc b/src/commodity.cc index 51b8f29c..bc04c3db 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -384,7 +384,7 @@ void commodity_t::parse_symbol(char *& p, string& symbol) throw_(amount_error, _("Failed to parse commodity")); } -void commodity_t::print(std::ostream& out, bool elide_quotes) const +void commodity_t::print(std::ostream& out, bool elide_quotes, bool) const { string sym = symbol(); if (elide_quotes && has_flags(COMMODITY_STYLE_SEPARATED) && diff --git a/src/commodity.h b/src/commodity.h index ba47a572..d6885ee9 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -303,7 +303,8 @@ public: return temp; } - void print(std::ostream& out, bool elide_quotes = false) const; + virtual void print(std::ostream& out, bool elide_quotes = false, + bool print_annotations = false) const; bool valid() const; struct compare_by_commodity { @@ -338,7 +339,7 @@ private: }; inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { - comm.print(out); + comm.print(out, false, true); return out; } -- cgit v1.2.3 From b5e76f005690e1c94245af430bdfae8dd4a8915f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 29 Mar 2012 16:28:53 -0500 Subject: Fix to commodity printing --- src/annotate.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/annotate.h b/src/annotate.h index 878d6148..37ee0685 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -265,9 +265,14 @@ public: virtual void print(std::ostream& out, bool elide_quotes = false, bool print_annotations = false) const { - commodity_t::print(out, elide_quotes); - if (print_annotations) - write_annotations(out); + if (print_annotations) { + std::ostringstream buf; + commodity_t::print(buf, elide_quotes); + write_annotations(buf); + out << buf.str(); + } else { + commodity_t::print(out, elide_quotes); + } } virtual void write_annotations(std::ostream& out, -- cgit v1.2.3 From 8e8c2904f55eb9a43b3eb8057e9f11767a624dff Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 30 Mar 2012 00:38:29 -0500 Subject: Never price commodities using annotated commodities --- src/amount.cc | 6 +++--- src/amount.h | 9 +++++++++ src/annotate.cc | 2 +- src/commodity.cc | 17 ++++++++--------- src/commodity.h | 3 +++ test/regress/25A099C9.test | 20 ++++++++++---------- 6 files changed, 34 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 2f7b434e..50496f2d 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -757,10 +757,10 @@ amount_t::value(const datetime_t& moment, } } - if (! point) { - if (comm && commodity().referent() == comm->referent()) - return *this; + if (comm && commodity().referent() == comm->referent()) + return with_commodity(comm->referent()); + if (! point) { point = commodity().find_price(comm, moment); // Whether a price was found or not, check whether we should attempt diff --git a/src/amount.h b/src/amount.h index 10e83552..cd77a79a 100644 --- a/src/amount.h +++ b/src/amount.h @@ -544,6 +544,15 @@ public: *this = 0L; commodity_ = &comm; } + amount_t with_commodity(const commodity_t& comm) const { + if (commodity_ == &comm) { + return *this; + } else { + amount_t tmp(*this); + tmp.set_commodity(const_cast(comm)); + return tmp; + } + } void clear_commodity() { commodity_ = NULL; } diff --git a/src/annotate.cc b/src/annotate.cc index c9fd6d3f..41e7a752 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -288,7 +288,7 @@ annotated_commodity_t::find_price(const commodity_t * commodity, return find_price_from_expr(const_cast(*details.value_expr), commodity, when); - return commodity_t::find_price(target, moment, oldest); + return commodity_t::find_price(target, when, oldest); } commodity_t& diff --git a/src/commodity.cc b/src/commodity.cc index bc04c3db..a72d85c8 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -56,14 +56,14 @@ void commodity_t::add_price(const datetime_t& date, const amount_t& price, DEBUG("history.find", "Adding price: " << symbol() << " for " << price << " on " << date); - pool().commodity_price_history.add_price(*this, date, price); + pool().commodity_price_history.add_price(referent(), date, price); base->price_map.clear(); // a price was added, invalid the map } void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) { - pool().commodity_price_history.remove_price(*this, commodity, date); + pool().commodity_price_history.remove_price(referent(), commodity, date); DEBUG("history.find", "Removing price: " << symbol() << " on " << date); @@ -83,7 +83,7 @@ void commodity_t::map_prices(function fn, else when = CURRENT_TIME(); - pool().commodity_price_history.map_prices(fn, *this, when, _oldest, + pool().commodity_price_history.map_prices(fn, referent(), when, _oldest, bidirectionally); } @@ -159,9 +159,9 @@ commodity_t::find_price(const commodity_t * commodity, optional point(target ? - pool().commodity_price_history.find_price(*this, *target, + pool().commodity_price_history.find_price(referent(), *target, when, oldest) : - pool().commodity_price_history.find_price(*this, when, oldest)); + pool().commodity_price_history.find_price(referent(), when, oldest)); // Record this price point in the memoization map if (base->price_map.size() > base_t::max_price_map_size) { @@ -206,7 +206,7 @@ commodity_t::check_for_updated_price(const optional& point, DEBUG("commodity.download", "attempting to download a more current quote..."); if (optional quote = - pool().get_commodity_quote(*this, in_terms_of)) { + pool().get_commodity_quote(referent(), in_terms_of)) { if (! in_terms_of || (quote->price.has_commodity() && quote->price.commodity_ptr() == in_terms_of)) @@ -220,12 +220,11 @@ commodity_t::check_for_updated_price(const optional& point, commodity_t& commodity_t::nail_down(const expr_t& expr) { annotation_t new_details; + new_details.value_expr = expr; new_details.add_flags(ANNOTATION_VALUE_EXPR_CALCULATED); - commodity_t * new_comm = - commodity_pool_t::current_pool->find_or_create(symbol(), new_details); - return *new_comm; + return *pool().find_or_create(symbol(), new_details); } commodity_t::operator bool() const diff --git a/src/commodity.h b/src/commodity.h index d6885ee9..bfbabe6b 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -188,6 +188,9 @@ public: return comm == *this; return base.get() == comm.base.get(); } + bool operator==(const string& name) const { + return base_symbol() == name; + } static bool symbol_needs_quotes(const string& symbol); diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index a8a93832..1ef5ebef 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -20,24 +20,24 @@ While parsing file "src/amount.h", line 121: Error: Unexpected whitespace at beginning of line While parsing file "src/amount.h", line 132: Error: Unexpected whitespace at beginning of line -While parsing file "src/amount.h", line 693: +While parsing file "src/amount.h", line 702: Error: Unexpected whitespace at beginning of line -While parsing file "src/amount.h", line 723: +While parsing file "src/amount.h", line 732: Error: Unexpected whitespace at beginning of line -While parsing file "src/amount.h", line 731: +While parsing file "src/amount.h", line 740: Error: Unexpected whitespace at beginning of line -While parsing file "src/amount.h", line 734: +While parsing file "src/amount.h", line 743: Error: Invalid date/time: line amount_t amoun -While parsing file "src/amount.h", line 740: +While parsing file "src/amount.h", line 749: Error: Invalid date/time: line string amount_ -While parsing file "src/amount.h", line 746: +While parsing file "src/amount.h", line 755: Error: Invalid date/time: line string amount_ -While parsing file "src/amount.h", line 752: +While parsing file "src/amount.h", line 761: Error: Invalid date/time: line string amount_ -While parsing file "src/amount.h", line 758: +While parsing file "src/amount.h", line 767: Error: Invalid date/time: line std::ostream& -While parsing file "src/amount.h", line 765: +While parsing file "src/amount.h", line 774: Error: Invalid date/time: line std::istream& -While parsing file "src/amount.h", line 771: +While parsing file "src/amount.h", line 780: Error: Unexpected whitespace at beginning of line end test -- cgit v1.2.3 From a1c33fec022d53c9d5831d00f26ffcc6f69a007d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 30 Mar 2012 00:38:59 -0500 Subject: Allow dates to be passed to Python value() method --- src/py_amount.cc | 7 +++++++ src/py_value.cc | 6 ++++++ 2 files changed, 13 insertions(+) (limited to 'src') diff --git a/src/py_amount.cc b/src/py_amount.cc index ea69dec5..0aa8fee8 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -56,6 +56,11 @@ namespace { const datetime_t& moment) { return amount.value(moment, in_terms_of); } + boost::optional py_value_2d(const amount_t& amount, + const commodity_t * in_terms_of, + const date_t& moment) { + return amount.value(datetime_t(moment), in_terms_of); + } void py_parse_2(amount_t& amount, object in, unsigned char flags) { if (PyFile_Check(in.ptr())) { @@ -238,6 +243,7 @@ internal precision.")) .def("value", py_value_0) .def("value", py_value_1, args("in_terms_of")) .def("value", py_value_2, args("in_terms_of", "moment")) + .def("value", py_value_2d, args("in_terms_of", "moment")) .def("price", &amount_t::price) @@ -267,6 +273,7 @@ internal precision.")) make_function(&amount_t::set_commodity, with_custodian_and_ward<1, 2>())) .def("has_commodity", &amount_t::has_commodity) + .def("with_commodity", &amount_t::with_commodity) .def("clear_commodity", &amount_t::clear_commodity) .def("number", &amount_t::number) diff --git a/src/py_value.cc b/src/py_value.cc index efeb4340..b931f008 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -59,6 +59,11 @@ namespace { const datetime_t& moment) { return value.value(moment, in_terms_of); } + boost::optional py_value_2d(const value_t& value, + const commodity_t * in_terms_of, + const date_t& moment) { + return value.value(datetime_t(moment), in_terms_of); + } PyObject * py_base_type(value_t& value) { @@ -265,6 +270,7 @@ void export_value() .def("value", py_value_0) .def("value", py_value_1, args("in_terms_of")) .def("value", py_value_2, args("in_terms_of", "moment")) + .def("value", py_value_2d, args("in_terms_of", "moment")) //.def("value", &value_t::value, value_overloads()) .def("exchange_commodities", &value_t::exchange_commodities, -- cgit v1.2.3 From 931d6ff3f4c805232fd531da1750ab43befee8d2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 30 Mar 2012 00:50:10 -0500 Subject: Fix to Python value() method handling --- src/py_post.cc | 7 +++---- test/regress/9188F587.py | 27 +++++++++++++++++++++++++++ test/regress/9188F587_py.test | 15 +++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 test/regress/9188F587.py create mode 100644 test/regress/9188F587_py.test (limited to 'src') diff --git a/src/py_post.cc b/src/py_post.cc index ce3bd71b..692542a0 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -166,8 +166,8 @@ void export_post() .def("get_tag", py_get_tag_1m) .def("get_tag", py_get_tag_2m) - .def("date", &post_t::date) - .def("aux_date", &post_t::aux_date) + .add_property("date", &post_t::date) + .add_property("aux_date", &post_t::aux_date) .def("must_balance", &post_t::must_balance) @@ -177,8 +177,7 @@ void export_post() .def("has_xdata", &post_t::has_xdata) .def("clear_xdata", &post_t::clear_xdata) - .def("xdata", py_xdata, - return_internal_reference<>()) + .def("xdata", py_xdata, return_internal_reference<>()) //.def("add_to_value", &post_t::add_to_value) .def("set_reported_account", &post_t::set_reported_account) diff --git a/test/regress/9188F587.py b/test/regress/9188F587.py new file mode 100644 index 00000000..50195252 --- /dev/null +++ b/test/regress/9188F587.py @@ -0,0 +1,27 @@ +import ledger + +eur = ledger.commodities.find_or_create('EUR') + +total_eur = ledger.Amount("0.00 EUR") +total_gbp = ledger.Amount("0.00 GBP") +total = ledger.Amount("0.00 EUR") + +for post in ledger.read_journal("test/regress/78AB4B87.dat").query("^income:"): + print post.amount + print post.amount.commodity + if post.amount.commodity == "EUR": + total_eur += post.amount + elif post.amount.commodity == "GBP": + total_gbp += post.amount + + a = post.amount.value(eur, post.date) + if a: + print "Total is presently: (%s)" % total + print "Converted to EUR: (%s)" % a + total += a + print "Total is now: (%s)" % total + else: + print "Cannot convert '%s'" % post.amount + print + +print total diff --git a/test/regress/9188F587_py.test b/test/regress/9188F587_py.test new file mode 100644 index 00000000..28bb34ff --- /dev/null +++ b/test/regress/9188F587_py.test @@ -0,0 +1,15 @@ +test python test/regress/9188F587.py +-5.00 GBP +GBP +Total is presently: (0.00 EUR) +Converted to EUR: (-6.00 EUR) +Total is now: (-6.00 EUR) + +-5.00 EUR {0.8733 GBP} [2012/01/03] +EUR +Total is presently: (-6.00 EUR) +Converted to EUR: (-5.00 EUR) +Total is now: (-11.00 EUR) + +-11.00 EUR +end test -- cgit v1.2.3 From df1cbc33d15f91fa883a0fc7c048824848e09840 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 30 Mar 2012 04:45:51 -0500 Subject: Whitespace fix --- src/amount.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 50496f2d..5e933215 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -395,11 +395,11 @@ int amount_t::compare(const amount_t& amt) const throw_(amount_error, _("Cannot compare two uninitialized amounts")); } - if (has_commodity() && amt.has_commodity() && - commodity() != amt.commodity()) + if (has_commodity() && amt.has_commodity() && commodity() != amt.commodity()) { throw_(amount_error, _("Cannot compare amounts with different commodities: '%1' and '%2'") << commodity() << amt.commodity()); + } return mpq_cmp(MP(quantity), MP(amt.quantity)); } @@ -460,11 +460,11 @@ amount_t& amount_t::operator-=(const amount_t& amt) throw_(amount_error, _("Cannot subtract two uninitialized amounts")); } - if (has_commodity() && amt.has_commodity() && - commodity() != amt.commodity()) + if (has_commodity() && amt.has_commodity() && commodity() != amt.commodity()) { throw_(amount_error, _("Subtracting amounts with different commodities: '%1' != '%2'") << commodity() << amt.commodity()); + } _dup(); -- cgit v1.2.3 From fcabd64a8981004179fa94c0f58ade7e993399b4 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 4 Apr 2012 01:36:31 -0500 Subject: Whitespace fix --- src/item.cc | 5 ++--- src/item.h | 3 +-- src/post.cc | 29 +++++++++++++---------------- src/post.h | 4 ++-- src/textual.cc | 9 ++++----- 5 files changed, 22 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 33c3110c..9e8ac472 100644 --- a/src/item.cc +++ b/src/item.cc @@ -117,9 +117,8 @@ item_t::set_tag(const string& tag, << (value ? *value : string_value("")) << "'"); optional data = value; - if (data && - (data->is_null() || - (data->is_string() && data->as_string().empty()))) + if (data && (data->is_null() || + (data->is_string() && data->as_string().empty()))) data = none; string_map::iterator i = metadata->find(tag); diff --git a/src/item.h b/src/item.h index a1160329..1e8db1e0 100644 --- a/src/item.h +++ b/src/item.h @@ -118,8 +118,7 @@ public: optional metadata; item_t(flags_t _flags = ITEM_NORMAL, const optional& _note = none) - : supports_flags(_flags), _state(UNCLEARED), - note(_note) + : supports_flags(_flags), _state(UNCLEARED), note(_note) { TRACE_CTOR(item_t, "flags_t, const string&"); } diff --git a/src/post.cc b/src/post.cc index 2b84fc06..0564eaca 100644 --- a/src/post.cc +++ b/src/post.cc @@ -49,9 +49,9 @@ bool post_t::has_tag(const string& tag, bool inherit) const return false; } -bool post_t::has_tag(const mask_t& tag_mask, +bool post_t::has_tag(const mask_t& tag_mask, const optional& value_mask, - bool inherit) const + bool inherit) const { if (item_t::has_tag(tag_mask, value_mask)) return true; @@ -69,9 +69,9 @@ optional post_t::get_tag(const string& tag, bool inherit) const return none; } -optional post_t::get_tag(const mask_t& tag_mask, +optional post_t::get_tag(const mask_t& tag_mask, const optional& value_mask, - bool inherit) const + bool inherit) const { if (optional value = item_t::get_tag(tag_mask, value_mask)) return value; @@ -93,17 +93,11 @@ date_t post_t::date() const return xdata_->date; if (item_t::use_aux_date) { - if (_date_aux) - return *_date_aux; - else if (xact && xact->_date_aux) - return *xact->_date_aux; + if (optional aux = aux_date()) + return *aux; } - if (! _date) { - assert(xact); - return xact->date(); - } - return *_date; + return primary_date(); } date_t post_t::primary_date() const @@ -174,7 +168,8 @@ namespace { return string_value(post.payee()); } - value_t get_note(post_t& post) { + value_t get_note(post_t& post) + { if (post.note || post.xact->note) { string note = post.note ? *post.note : empty_string; note += post.xact->note ? *post.xact->note : empty_string; @@ -188,7 +183,8 @@ namespace { return post.xact->magnitude(); } - value_t get_amount(post_t& post) { + value_t get_amount(post_t& post) + { if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) return post.xdata().compound_value; else if (post.amount.is_null()) @@ -215,7 +211,8 @@ namespace { } } - value_t get_commodity_is_primary(post_t& post) { + value_t get_commodity_is_primary(post_t& post) + { if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) return post.xdata().compound_value.to_amount() diff --git a/src/post.h b/src/post.h index e2d9499f..78928f23 100644 --- a/src/post.h +++ b/src/post.h @@ -120,10 +120,10 @@ public: bool inherit = true) const; virtual optional get_tag(const string& tag, - bool inherit = true) const; + bool inherit = true) const; virtual optional get_tag(const mask_t& tag_mask, const optional& value_mask = none, - bool inherit = true) const; + bool inherit = true) const; virtual date_t value_date() const; virtual date_t date() const; diff --git a/src/textual.cc b/src/textual.cc index 0cb7fb81..d0e4dad2 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1124,11 +1124,10 @@ void instance_t::tag_directive(char * line) if (keyword == "assert" || keyword == "check") { context.journal->tag_check_exprs.insert (tag_check_exprs_map::value_type - (string(p), - expr_t::check_expr_pair(expr_t(b), - keyword == "assert" ? - expr_t::EXPR_ASSERTION : - expr_t::EXPR_CHECK))); + (string(p), expr_t::check_expr_pair(expr_t(b), + keyword == "assert" ? + expr_t::EXPR_ASSERTION : + expr_t::EXPR_CHECK))); } } } -- cgit v1.2.3 From 878afc6d47ee4efa34a7ae1ab58025f6d65d9964 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 4 Apr 2012 02:26:50 -0500 Subject: Added 'lowered' helper function --- src/utils.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/utils.h b/src/utils.h index 2feb73b7..8f11f75a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -290,6 +290,12 @@ inline string to_string(std::size_t num) { return buf.str(); } +inline string lowered(const string& str) { + string tmp(str); + to_lower(tmp); + return tmp; +} + inline string operator+(const char * left, const string& right) { return string(left) + right; } -- cgit v1.2.3 From 807cce12ff7d1f3f950853694c0603f20021bff0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 4 Apr 2012 02:27:26 -0500 Subject: Make status and state valexpr synonyms --- src/item.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 9e8ac472..06b718b0 100644 --- a/src/item.cc +++ b/src/item.cc @@ -475,7 +475,7 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, break; case 's': - if (name == "status") + if (name == "status" || name == "state") return WRAP_FUNCTOR(get_wrapper<&get_status>); else if (name == "seq") return WRAP_FUNCTOR(get_wrapper<&get_seq>); -- cgit v1.2.3 From 5a615ec680190222e25047366f166f9daf89c69f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 4 Apr 2012 02:27:49 -0500 Subject: Make metadata tags case insensitive --- src/item.cc | 12 +++++++++++- src/item.h | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 06b718b0..5262d1db 100644 --- a/src/item.cc +++ b/src/item.cc @@ -103,6 +103,16 @@ optional item_t::get_tag(const mask_t& tag_mask, return none; } +namespace { + struct CaseInsensitiveKeyCompare + : public std::binary_function + { + bool operator()(const string& s1, const string& s2) const { + return boost::algorithm::ilexicographical_compare(s1, s2); + } + }; +} + item_t::string_map::iterator item_t::set_tag(const string& tag, const optional& value, @@ -111,7 +121,7 @@ item_t::set_tag(const string& tag, assert(! tag.empty()); if (! metadata) - metadata = string_map(); + metadata = string_map(CaseInsensitiveKeyCompare()); DEBUG("item.meta", "Setting tag '" << tag << "' to value '" << (value ? *value : string_value("")) << "'"); diff --git a/src/item.h b/src/item.h index 1e8db1e0..c56fba27 100644 --- a/src/item.h +++ b/src/item.h @@ -108,7 +108,8 @@ public: enum state_t { UNCLEARED = 0, CLEARED, PENDING }; typedef std::pair, bool> tag_data_t; - typedef std::map string_map; + typedef std::map > string_map; state_t _state; optional _date; -- cgit v1.2.3 From 322d55823e682766e15adaaf54907a42d5ae4ab2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 4 Apr 2012 02:28:07 -0500 Subject: post.x = y defines metadata tag x --- src/item.cc | 7 +++++++ src/item.h | 2 ++ 2 files changed, 9 insertions(+) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 5262d1db..17d46652 100644 --- a/src/item.cc +++ b/src/item.cc @@ -398,6 +398,13 @@ value_t get_comment(item_t& item) } } +void item_t::define(const symbol_t::kind_t, const string& name, + expr_t::ptr_op_t def) +{ + bind_scope_t bound_scope(*scope_t::default_scope, *this); + set_tag(name, def->calc(bound_scope)); +} + expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, const string& name) { diff --git a/src/item.h b/src/item.h index c56fba27..8d9a79ee 100644 --- a/src/item.h +++ b/src/item.h @@ -216,6 +216,8 @@ public: return _state; } + virtual void define(const symbol_t::kind_t, const string&, + expr_t::ptr_op_t); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); -- cgit v1.2.3 From ad7ace902c6a1c756ca22d2e67edcc58fe07cd40 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 6 Apr 2012 01:01:43 -0500 Subject: Allow --invert to work with --monthly Fixes #770 --- src/filters.cc | 23 ++++++++++++++++++----- src/report.h | 2 +- test/regress/FCE11C8D.test | 7 +++++++ 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 test/regress/FCE11C8D.test (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index a665f9e6..9589958c 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -893,16 +893,28 @@ void subtotal_posts::operator()(post_t& post) account_t * acct = post.reported_account(); assert(acct); +#if 0 + // jww (2012-04-06): The problem with doing this early is that + // fn_display_amount will recalculate this again. For example, if you + // use --invert, it will invert both here and in the display amount, + // effectively negating it. + bind_scope_t bound_scope(*amount_expr.get_context(), post); + value_t amount(amount_expr.calc(bound_scope)); +#else + value_t amount(post.amount); +#endif + + post.xdata().compound_value = amount; + post.xdata().add_flags(POST_EXT_COMPOUND); + values_map::iterator i = values.find(acct->fullname()); if (i == values.end()) { - value_t temp; - post.add_to_value(temp, amount_expr); #if defined(DEBUG_ON) std::pair result = #endif values.insert(values_pair (acct->fullname(), - acct_value_t(acct, temp, post.has_flags(POST_VIRTUAL), + acct_value_t(acct, amount, post.has_flags(POST_VIRTUAL), post.has_flags(POST_MUST_BALANCE)))); #if defined(DEBUG_ON) assert(result.second); @@ -913,7 +925,7 @@ void subtotal_posts::operator()(post_t& post) _("'equity' cannot accept virtual and " "non-virtual postings to the same account")); - post.add_to_value((*i).second.value, amount_expr); + add_or_set_value((*i).second.value, amount); } // If the account for this post is all virtual, mark it as @@ -953,8 +965,9 @@ void interval_posts::operator()(post_t& post) if (interval.duration) { all_posts.push_back(&post); } - else if (interval.find_period(post.date())) + else if (interval.find_period(post.date())) { item_handler::operator()(post); + } } void interval_posts::flush() diff --git a/src/report.h b/src/report.h index 37123377..e7d68dda 100644 --- a/src/report.h +++ b/src/report.h @@ -725,7 +725,7 @@ public: OPTION(report_t, inject_); OPTION_(report_t, invert, DO() { - OTHER(amount_).on(whence, "-amount"); + OTHER(amount_).on(whence, "-amount_expr"); }); OPTION_ diff --git a/test/regress/FCE11C8D.test b/test/regress/FCE11C8D.test new file mode 100644 index 00000000..595edc2d --- /dev/null +++ b/test/regress/FCE11C8D.test @@ -0,0 +1,7 @@ +2012-03-17 Payee + Expenses:Food $20 + Assets:Cash + +test reg --monthly --invert exp +12-Mar-01 - 12-Mar-31 Expenses:Food $-20 $-20 +end test -- cgit v1.2.3 From e3ac24301990a77a296d103388a64bea716ce5cd Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 8 Apr 2012 02:57:18 -0500 Subject: Started work on a "views" report document model --- acprep | 2 + src/views.cc | 259 +++++++++++++++++++++++++++++++ src/views.h | 457 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xact.h | 4 + tools/Makefile.am | 2 + 5 files changed, 724 insertions(+) create mode 100644 src/views.cc create mode 100644 src/views.h (limited to 'src') diff --git a/acprep b/acprep index ddf7639d..72fcd642 100755 --- a/acprep +++ b/acprep @@ -1048,6 +1048,8 @@ class PrepareBuild(CommandLineApp): self.CXXFLAGS.append('-march=nocona') self.CXXFLAGS.append('-msse3') + self.CXXFLAGS.append('-DDOCUMENT_MODEL=1') + self.locate_darwin_libraries() def setup_for_system(self): diff --git a/src/views.cc b/src/views.cc new file mode 100644 index 00000000..bbd58ce2 --- /dev/null +++ b/src/views.cc @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2003-2012, 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. + */ + +#ifdef DOCUMENT_MODEL + +#include + +#include "views.h" +#include "report.h" +#include "journal.h" +#include "xact.h" +#include "post.h" +#include "account.h" + +namespace ledger { + +r_xact_ptr r_journal_t::create_xact(xact_t * xact) +{ + r_xact_ptr x = new r_xact_t(this, xact); + add_xact(x); + assert(xact->data == NULL); + xact->data = &x; + return x; +} + +void r_journal_t::add_xact(r_xact_ptr xact) +{ + xacts.push_back(xact); +} + +r_post_ptr r_journal_t::add_post(post_t * post) +{ + r_xact_ptr x; + if (post->xact->data) + x = *static_cast(post->xact->data); + else + x = create_xact(post->xact); + + r_post_ptr p = create_post(post, x, create_account(post->account)); + return p; +} + +void r_journal_t::add_post(r_post_ptr post) +{ + posts.push_back(post); +} + +r_post_ptr r_journal_t::create_post(post_t * post, r_xact_ptr xact, + r_account_ptr account) +{ + r_post_ptr p = new r_post_t(this, post, xact, account); + + add_post(p); + xact->add_post(p); + account->add_post(p); + + return p; +} + +r_post_ptr r_journal_t::create_post(r_post_ptr post, r_xact_ptr xact, + r_account_ptr account) +{ + r_post_ptr temp(new r_post_t(*post.get())); + + add_post(temp); + + temp->set_xact(xact); + xact->add_post(temp); + + temp->set_account(account); + account->add_post(temp); + + return temp; +} + +r_account_ptr r_journal_t::create_account(account_t * account) +{ + return create_account(account->fullname()); +} + +r_account_ptr r_journal_t::create_account(const std::string& name) +{ + return master_ptr->create_account(name); +} + + +const optional r_item_t::position() const +{ + return ptr()->pos; +} + +date_t r_item_t::date() const +{ + return ptr()->date(); +} + +void r_item_t::set_date(const date_t& when) +{ +} + +item_t::state_t r_item_t::state() const +{ + return ptr()->state(); +} + +void r_item_t::set_state(item_t::state_t val) +{ +} + +string r_item_t::payee() const +{ + if (optional desc = get_tag(_("Payee"))) + return desc->as_string(); + else + return empty_string; +} + +void r_item_t::set_payee(const string& name) +{ +} + +void r_item_t::define(const symbol_t::kind_t, const string& name, + expr_t::ptr_op_t def) +{ + bind_scope_t bound_scope(*scope_t::default_scope, *this); + set_tag(name, def->calc(bound_scope)); +} + +expr_t::ptr_op_t r_item_t::lookup(const symbol_t::kind_t kind, + const string& name) +{ + if (kind != symbol_t::FUNCTION) + return NULL; + + switch (name[0]) { + } + + return base_item->lookup(kind, name); +} + + +string r_xact_t::description() +{ + return ptr()->description(); +} + +void r_xact_t::add_post(r_post_ptr post) +{ + posts.push_back(post); +} + +string r_xact_t::payee() const +{ + string desc(r_item_t::payee()); + if (desc.empty()) + return ptr()->payee; + else + return desc; +} + + +string r_post_t::description() +{ + return ptr()->description(); +} + +string r_post_t::payee() const +{ + string desc(r_item_t::payee()); + if (desc.empty()) + return const_cast(this)->xact()->payee(); + else + return desc; +} + + +string r_account_t::description() +{ + return string(_("account ")) + fullname(); +} + +void r_account_t::add_post(r_post_ptr post) +{ + posts.push_back(post); +} + +r_account_ptr r_account_t::create_account(const std::string& fname) +{ + string::size_type sep = fname.find(':'); + string head, tail; + if (sep == string::npos) { + head = fname; + } else { + head = string(fname, 0, sep); + tail = string(fname, sep + 1); + } + + std::pair result = + accounts.insert(r_accounts_map::value_type + (head, new r_account_t(journal_ptr, this, name))); + + r_account_ptr acct((*result.first).second); + if (tail.empty()) + return acct; + else + return acct->create_account(tail); +} + +string r_account_t::fullname() const +{ + if (! _fullname.empty()) { + return _fullname; + } else { + r_account_ptr first = NULL; + string fname = name; + + while (! first || first->parent_ptr) { + first = first ? first->parent_ptr : parent_ptr; + if (! first->name.empty()) + fname = first->name + ":" + fname; + } + + _fullname = fname; + + return fname; + } +} + +} // namespace ledger + +#endif /* DOCUMENT_MODEL */ diff --git a/src/views.h b/src/views.h new file mode 100644 index 00000000..f9a007b7 --- /dev/null +++ b/src/views.h @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2003-2012, 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 views + */ + +/** + * @file views.h + * @author John Wiegley + * + * @ingroup views + */ +#ifndef _VIEWS_H +#define _VIEWS_H + +#include "utils.h" + +#ifdef DOCUMENT_MODEL + +#include "scope.h" +#include "item.h" +#include "report.h" +#include "post.h" +#include "predicate.h" + +namespace ledger { + +class journal_t; +class xact_t; +class post_t; +class account_t; +class report_t; + +class r_base_t : public supports_flags, + public scope_t +{ +public: + r_base_t() : refc(0) { + TRACE_CTOR(r_base_t, ""); + } + r_base_t(const r_base_t& other) : refc(0) { + TRACE_CTOR(r_base_t, "copy"); + } + virtual ~r_base_t() { + TRACE_DTOR(r_base_t); + } + +protected: + /** + * `refc' holds the current reference count for each object. + */ + mutable int refc; + + /** + * Reference counting methods. The intrusive_ptr_* methods are used + * by boost::intrusive_ptr to manage the calls to acquire and release. + */ + void acquire() const { + VERIFY(refc >= 0); + refc++; + } + void release() const { + VERIFY(refc > 0); + if (--refc == 0) + checked_delete(this); + } + + friend inline void intrusive_ptr_add_ref(r_base_t * r_ptr) { + r_ptr->acquire(); + } + friend inline void intrusive_ptr_release(r_base_t * r_ptr) { + r_ptr->release(); + } +}; + +class r_journal_t; +class r_item_t; +class r_xact_t; +class r_post_t; +class r_account_t; + +typedef intrusive_ptr r_journal_ptr; +typedef intrusive_ptr r_item_ptr; +typedef intrusive_ptr r_xact_ptr; +typedef intrusive_ptr r_post_ptr; +typedef intrusive_ptr r_account_ptr; + +typedef std::list r_xacts_list; +typedef std::list r_posts_list; + +class r_journal_t : public r_base_t +{ + journal_t * base_journal; + + journal_t * ptr() { + return base_journal; + } + const journal_t * ptr() const { + return base_journal; + } + + r_account_ptr master_ptr; + r_xacts_list xacts; + r_posts_list posts; + + void set_master(r_account_ptr ptr) { + master_ptr = ptr; + } + +public: + r_journal_t(journal_t * _journal, r_account_ptr _master) + : r_base_t(), base_journal(_journal), master_ptr(_master) { + TRACE_CTOR(r_journal_t, "journal_t *, account_t *"); + } + r_journal_t(const r_journal_t& other) + : r_base_t(other), + base_journal(other.base_journal), + master_ptr(other.master_ptr), + xacts(other.xacts), + posts(other.posts) { + TRACE_CTOR(r_journal_t, "copy"); + } + virtual ~r_journal_t() { + TRACE_DTOR(r_journal_t); + } + + r_xact_ptr create_xact(xact_t * xact = NULL); + + void add_xact(r_xact_ptr xact); + + r_xacts_list::iterator xacts_begin() { + return xacts.begin(); + } + r_xacts_list::iterator xacts_end() { + return xacts.end(); + } + + r_post_ptr add_post(post_t * post); + void add_post(r_post_ptr post); + + r_post_ptr create_post(post_t * post = NULL, r_xact_ptr xact = NULL, + r_account_ptr account = NULL); + r_post_ptr create_post(r_post_ptr post, r_xact_ptr xact = NULL, + r_account_ptr account = NULL); + + r_posts_list::iterator posts_begin() { + return posts.begin(); + } + r_posts_list::iterator posts_end() { + return posts.end(); + } + + r_account_ptr create_account(account_t * account = NULL); + r_account_ptr create_account(const std::string& name); + + friend void to_xml(std::ostream& out, r_journal_ptr journal); +}; + +class r_item_t : public r_base_t +{ +protected: + item_t * base_item; + + item_t * ptr() { + return base_item; + } + const item_t * ptr() const { + return base_item; + } + + r_journal_ptr journal_ptr; + +public: + r_item_t(r_journal_ptr _journal_ptr, item_t * _item) + : r_base_t(), base_item(_item), journal_ptr(_journal_ptr) { + TRACE_CTOR(r_item_t, "r_journal_ptr, item_t *"); + } + r_item_t(const r_item_t& other) + : r_base_t(other), + base_item(other.base_item), + journal_ptr(other.journal_ptr) { + TRACE_CTOR(r_item_t, "copy"); + } + virtual ~r_item_t() { + TRACE_DTOR(r_item_t); + } + + const optional position() const; + + string id() const { + return ptr()->id(); + } + std::size_t seq() const { + return ptr()->seq(); + } + + date_t date() const; + void set_date(const date_t& when); + + item_t::state_t state() const; + void set_state(item_t::state_t val); + + string payee() const; + void set_payee(const string& name); + + optional note() const { + return ptr()->note; + } + + bool has_tag(const string& tag) const { + return ptr()->has_tag(tag); + } + bool has_tag(const mask_t& tag_mask, + const optional& value_mask = none) const { + return ptr()->has_tag(tag_mask, value_mask); + } + + optional get_tag(const string& tag) const { + return ptr()->get_tag(tag); + } + optional get_tag(const mask_t& tag_mask, + const optional& value_mask = none) const { + return ptr()->get_tag(tag_mask, value_mask); + } + + void set_tag(const string& tag, + const optional& value = none, + const bool overwrite_existing = true) { + ptr()->set_tag(tag, value, overwrite_existing); + } + + /** + * Symbol scope methods. + */ + virtual void define(const symbol_t::kind_t, const string&, + expr_t::ptr_op_t); + virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, + const string& name); + + friend class r_journal_t; + friend void to_xml(std::ostream& out, r_item_ptr item); +}; + +class r_xact_t : public r_item_t +{ + xact_t * ptr() { + return reinterpret_cast(base_item); + } + const xact_t * ptr() const { + return reinterpret_cast(base_item); + } + + r_posts_list posts; + +public: + r_xact_t(r_journal_ptr journal_ptr, xact_t * _xact) + : r_item_t(journal_ptr, reinterpret_cast(_xact)) { + TRACE_CTOR(r_xact_t, "r_journal_ptr, xact_t *"); + } + r_xact_t(const r_xact_t& other) + : r_item_t(other), + posts(other.posts) { + TRACE_CTOR(r_xact_t, "copy"); + } + virtual ~r_xact_t() { + TRACE_DTOR(r_xact_t); + } + + virtual string description(); + + void add_post(r_post_ptr post); + +#if 0 + r_post_ptr create_post(post_t * post = NULL, r_account_ptr account = NULL); + r_post_ptr create_post(r_post_ptr post, r_account_ptr account = NULL); +#endif + + r_posts_list::iterator posts_begin() { + return posts.begin(); + } + r_posts_list::iterator posts_end() { + return posts.end(); + } + + string code() const; + string payee() const; + + friend class r_journal_t; + friend void to_xml(std::ostream& out, r_xact_ptr xact); +}; + +class r_post_t : public r_item_t +{ + post_t * ptr() { + return reinterpret_cast(base_item); + } + const post_t * ptr() const { + return reinterpret_cast(base_item); + } + + r_xact_ptr xact_ptr; + r_account_ptr account_ptr; + + void set_xact(r_xact_ptr ptr) { + xact_ptr = ptr; + } + void set_account(r_account_ptr ptr) { + account_ptr = ptr; + } + +public: + r_post_t(r_journal_ptr journal_ptr, post_t * _post, + r_xact_ptr _xact_ptr, r_account_ptr _account_ptr) + : r_item_t(journal_ptr, reinterpret_cast(_post)), + xact_ptr(_xact_ptr), account_ptr(_account_ptr) { + TRACE_CTOR(r_post_t, "r_journal_ptr, post_t *, r_xact_ptr, r_account_ptr"); + } + r_post_t(const r_post_t& other) + : r_item_t(other), + xact_ptr(other.xact_ptr), + account_ptr(other.account_ptr) { + TRACE_CTOR(r_post_t, "copy"); + } + virtual ~r_post_t() { + TRACE_DTOR(r_post_t); + } + + virtual string description(); + + string payee() const; + + r_xact_ptr xact(); + r_account_ptr account(); + + value_t amount() const; + value_t cost() const; + + std::size_t count() const; + value_t running_total() const; + + optional checkin() const; + optional checkout() const; + + friend class r_journal_t; + friend void to_xml(std::ostream& out, r_post_ptr post); +}; + +typedef std::map r_accounts_map; + +class r_account_t : public r_base_t +{ + r_journal_ptr journal_ptr; + r_account_ptr parent_ptr; + r_accounts_map accounts; + r_posts_list posts; + + string name; + + mutable string _fullname; + +public: + r_account_t(r_journal_ptr _journal_ptr, r_account_ptr _parent_ptr, + string _name) + : r_base_t(), journal_ptr(_journal_ptr), parent_ptr(_parent_ptr), + name(_name) { + TRACE_CTOR(r_account_t, "r_journal_ptr, r_account_ptr, string"); + } + r_account_t(const r_account_t& other) + : r_base_t(other), + journal_ptr(other.journal_ptr), + parent_ptr(other.parent_ptr), + accounts(other.accounts), + posts(other.posts), + name(other.name), + _fullname(other._fullname) { + TRACE_CTOR(r_account_t, "copy"); + } + virtual ~r_account_t() { + TRACE_DTOR(r_account_t); + } + + virtual string description(); + + void add_post(r_post_ptr post); + + r_posts_list::iterator posts_begin() { + return posts.begin(); + } + r_posts_list::iterator posts_end() { + return posts.end(); + } + + r_account_ptr create_account(const std::string& name); + + string fullname() const; + + /** + * Symbol scope methods. + */ + virtual void define(const symbol_t::kind_t, const string&, + expr_t::ptr_op_t) {} + virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, + const string& fname) { + return NULL; + } + + friend class r_journal_t; + friend void to_xml(std::ostream& out, r_account_ptr account); +}; + +template +void populate_journal(r_journal_ptr journal, report_t& report, + PostsIterator iter, predicate_t& pred) +{ + while (post_t * post = *iter) { + bind_scope_t bound_scope(report, *post); + if (pred.calc(bound_scope)) + journal->add_post(post); + + iter.increment(); + } +} + +} // namespace ledger + +#endif /* DOCUMENT_MODEL */ + +#endif // _VIEWS_H diff --git a/src/xact.h b/src/xact.h index ce00242e..0000e9c6 100644 --- a/src/xact.h +++ b/src/xact.h @@ -109,6 +109,10 @@ public: optional code; string payee; +#ifdef DOCUMENT_MODEL + void * data; +#endif + xact_t() { TRACE_CTOR(xact_t, ""); } diff --git a/tools/Makefile.am b/tools/Makefile.am index 232ce768..04f64530 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -79,6 +79,7 @@ libledger_report_la_SOURCES = \ src/chain.cc \ src/filters.cc \ src/report.cc \ + src/views.cc \ src/select.cc \ src/session.cc @@ -131,6 +132,7 @@ pkginclude_HEADERS = \ \ src/session.h \ src/select.h \ + src/views.h \ src/report.h \ src/filters.h \ src/chain.h \ -- cgit v1.2.3 From 6a5360e2b570d21b321f901ca5584780a6111121 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 12 Apr 2012 02:39:04 -0500 Subject: Make --detail a synonym for --rich-data --- src/report.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 97d2df02..c7559cf7 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1215,7 +1215,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(revalued); else OPT(revalued_only); else OPT(revalued_total_); - else OPT(rich_data); + else OPT_ALT(rich_data, detail); break; case 's': OPT(sort_); -- cgit v1.2.3 From 1e572d508d27738142dd5e80ea6745043eed8f18 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 12 Apr 2012 02:39:23 -0500 Subject: Add 'data' member to account/journal objects --- src/account.h | 17 ++++++++++++++--- src/xact.cc | 3 +++ src/xact.h | 8 ++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/account.h b/src/account.h index fd98a9ac..c0e3e1f7 100644 --- a/src/account.h +++ b/src/account.h @@ -70,13 +70,20 @@ public: optional value_expr; mutable string _fullname; +#ifdef DOCUMENT_MODEL + mutable void * data; +#endif account_t(account_t * _parent = NULL, const string& _name = "", const optional& _note = none) : supports_flags<>(), scope_t(), parent(_parent), name(_name), note(_note), - depth(static_cast(parent ? parent->depth + 1 : 0)) { + depth(static_cast(parent ? parent->depth + 1 : 0)) +#ifdef DOCUMENT_MODEL + , data(NULL) +#endif + { TRACE_CTOR(account_t, "account_t *, const string&, const string&"); } account_t(const account_t& other) @@ -85,10 +92,14 @@ public: name(other.name), note(other.note), depth(other.depth), - accounts(other.accounts) { + accounts(other.accounts) +#ifdef DOCUMENT_MODEL + , data(NULL) +#endif + { TRACE_CTOR(account_t, "copy"); } - ~account_t(); + virtual ~account_t(); virtual string description() { return string(_("account ")) + fullname(); diff --git a/src/xact.cc b/src/xact.cc index 8085b104..3f4b753c 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -487,6 +487,9 @@ bool xact_base_t::verify() xact_t::xact_t(const xact_t& e) : xact_base_t(e), code(e.code), payee(e.payee) +#ifdef DOCUMENT_MODEL + , data(NULL) +#endif { TRACE_CTOR(xact_t, "copy"); } diff --git a/src/xact.h b/src/xact.h index 0000e9c6..59430285 100644 --- a/src/xact.h +++ b/src/xact.h @@ -110,10 +110,14 @@ public: string payee; #ifdef DOCUMENT_MODEL - void * data; + mutable void * data; #endif - xact_t() { + xact_t() +#ifdef DOCUMENT_MODEL + : data(NULL) +#endif + { TRACE_CTOR(xact_t, ""); } xact_t(const xact_t& e); -- cgit v1.2.3 From ee641f353c9a2216533800464d26afe86f1b028e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 15 Apr 2012 01:17:01 -0500 Subject: Resolve some issues with proof builds --- acprep | 5 ++++- src/draft.cc | 14 +++++++------- src/draft.h | 9 +++++++++ src/xact.cc | 3 ++- test/LedgerHarness.py | 4 ++-- test/RegressTests.py | 3 ++- 6 files changed, 26 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/acprep b/acprep index 0d7f3cd0..4ebd6914 100755 --- a/acprep +++ b/acprep @@ -1643,7 +1643,10 @@ class PrepareBuild(CommandLineApp): isdir(self.build_directory()): self.log.info('=== Wiping build directory %s ===' % self.build_directory()) - shutil.rmtree(self.build_directory()) + try: + shutil.rmtree(self.build_directory()) + except: + self.execute('rm', '-fr', self.build_directory()) def phase_distcheck(self, *args): self.log.info('Executing phase: distcheck') diff --git a/src/draft.cc b/src/draft.cc index 7edf7edc..43c214cb 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -507,7 +507,6 @@ value_t template_command(call_scope_t& args) out << std::endl << std::endl; draft_t draft(args.value()); - out << _("--- Transaction template ---") << std::endl; draft.dump(out); @@ -517,15 +516,16 @@ value_t template_command(call_scope_t& args) value_t xact_command(call_scope_t& args) { report_t& report(find_scope(args)); - draft_t draft(args.value()); + draft_t draft(args.value()); - xact_t * new_xact = draft.insert(*report.session.journal.get()); + unique_ptr new_xact(draft.insert(*report.session.journal.get())); + if (new_xact.get()) { + // Only consider actual postings for the "xact" command + report.HANDLER(limit_).on("#xact", "actual"); - // Only consider actual postings for the "xact" command - report.HANDLER(limit_).on("#xact", "actual"); + report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact.get()); + } - if (new_xact) - report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact); return true; } diff --git a/src/draft.h b/src/draft.h index 46aa26e1..9023e6da 100644 --- a/src/draft.h +++ b/src/draft.h @@ -81,6 +81,15 @@ class draft_t : public expr_base_t xact_template_t() { TRACE_CTOR(xact_template_t, ""); } + xact_template_t(const xact_template_t& other) + : date(other.date), + code(other.code), + note(other.note), + payee_mask(other.payee_mask), + posts(other.posts) + { + TRACE_CTOR(xact_template_t, "copy"); + } ~xact_template_t() throw() { TRACE_DTOR(xact_template_t); } diff --git a/src/xact.cc b/src/xact.cc index 3f4b753c..226fd5ab 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -56,7 +56,8 @@ xact_base_t::~xact_base_t() // temporary is. assert(! post->has_flags(ITEM_TEMP)); - post->account->remove_post(post); + if (post->account) + post->account->remove_post(post); checked_delete(post); } } diff --git a/test/LedgerHarness.py b/test/LedgerHarness.py index 3477e720..3e797e85 100755 --- a/test/LedgerHarness.py +++ b/test/LedgerHarness.py @@ -44,8 +44,8 @@ class LedgerHarness: print "Cannot find source path at '%s'" % argv[2] sys.exit(1) - self.ledger = argv[1] - self.sourcepath = argv[2] + self.ledger = os.path.abspath(argv[1]) + self.sourcepath = os.path.abspath(argv[2]) self.succeeded = 0 self.failed = 0 self.verify = '--verify' in argv diff --git a/test/RegressTests.py b/test/RegressTests.py index a7d51ada..792c2d3e 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -184,7 +184,8 @@ if __name__ == '__main__': tests = [os.path.join(tests, x) for x in os.listdir(tests) if (x.endswith('.test') and - (not '_py.test' in x or harness.python))] + (not '_py.test' in x or (harness.python and + not harness.verify)))] if pool: pool.map(do_test, tests, 1) else: -- cgit v1.2.3 From 548af968f6a45e42937dd35f70fc601dee8bb38c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 25 Apr 2012 03:08:23 -0500 Subject: #if 0'd out some unused code --- src/lookup.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/lookup.cc b/src/lookup.cc index ce22529d..6f0c33ea 100644 --- a/src/lookup.cc +++ b/src/lookup.cc @@ -186,11 +186,13 @@ lookup_probable_account(const string& ident, if (in_order_match && pos - index < 3) addend++; +#if 0 #if !defined(HAVE_BOOST_REGEX_UNICODE) if (pos == 0 || (pos > 0 && !std::isalnum(value_key[pos - 1]))) addend++; #else // jww (2010-03-07): Not yet implemented +#endif #endif last_match_pos = pos; -- cgit v1.2.3