diff options
-rwxr-xr-x | acprep | 11 | ||||
-rw-r--r-- | src/account.cc | 6 | ||||
-rw-r--r-- | src/amount.cc | 39 | ||||
-rw-r--r-- | src/amount.h | 8 | ||||
-rw-r--r-- | src/annotate.cc | 2 | ||||
-rw-r--r-- | src/commodity.cc | 203 | ||||
-rw-r--r-- | src/commodity.h | 41 | ||||
-rw-r--r-- | src/exprbase.h | 4 | ||||
-rw-r--r-- | src/filters.cc | 9 | ||||
-rw-r--r-- | src/filters.h | 12 | ||||
-rw-r--r-- | src/global.cc | 2 | ||||
-rw-r--r-- | src/item.cc | 6 | ||||
-rw-r--r-- | src/iterators.cc | 4 | ||||
-rw-r--r-- | src/op.cc | 2 | ||||
-rw-r--r-- | src/parser.h | 6 | ||||
-rw-r--r-- | src/pool.cc | 79 | ||||
-rw-r--r-- | src/pool.h | 8 | ||||
-rw-r--r-- | src/post.h | 11 | ||||
-rw-r--r-- | src/print.cc | 1 | ||||
-rw-r--r-- | src/py_utils.cc | 2 | ||||
-rw-r--r-- | src/report.cc | 47 | ||||
-rw-r--r-- | src/report.h | 1 | ||||
-rw-r--r-- | src/session.h | 1 | ||||
-rw-r--r-- | src/textual.cc | 10 | ||||
-rw-r--r-- | src/times.cc | 2 | ||||
-rw-r--r-- | src/utils.h | 14 | ||||
-rw-r--r-- | src/value.cc | 28 | ||||
-rw-r--r-- | src/value.h | 2 | ||||
-rw-r--r-- | src/xact.cc | 2 | ||||
-rw-r--r-- | test/regress/25A099C9.test | 14 | ||||
-rwxr-xr-x | tools/pre-commit | 14 |
31 files changed, 439 insertions, 152 deletions
@@ -124,7 +124,9 @@ class BoostInfo(object): return None def locate_boost(self): - for path in map(lambda x: join(x, 'lib'), search_prefixes): + lib64_dirs = map(lambda x: join(x, 'lib64'), search_prefixes) + lib_dirs = map(lambda x: join(x, 'lib'), search_prefixes) + for path in lib64_dirs + lib_dirs: self.log.info('Looking for Boost in %s...' % path) self.suffix = self.find_boost_in_directory(path) if self.suffix is not None: @@ -685,12 +687,16 @@ class PrepareBuild(CommandLineApp): self.current_version()) version_m4.close() + def copytimes(self, src, dest): + os.utime(dest, (os.stat(src)[ST_ATIME], os.stat(src)[ST_MTIME])) + def phase_autogen(self, *args): self.log.info('Executing phase: autogen') if not exists('autogen.sh') or \ self.isnewer('tools/autogen.sh', 'autogen.sh'): shutil.copyfile('tools/autogen.sh', 'autogen.sh') + self.copytimes('tools/autogen.sh', 'autogen.sh') self.execute('sh', 'tools/autogen.sh') @@ -704,10 +710,12 @@ class PrepareBuild(CommandLineApp): if not exists('configure.ac') or \ self.isnewer('tools/configure.ac', 'configure.ac'): shutil.copyfile('tools/configure.ac', 'configure.ac') + self.copytimes('tools/configure.ac', 'configure.ac') if not exists('Makefile.am') or \ self.isnewer('tools/Makefile.am', 'Makefile.am'): shutil.copyfile('tools/Makefile.am', 'Makefile.am') + self.copytimes('tools/Makefile.am', 'Makefile.am') reason = self.need_to_prepare_autotools() if reason: @@ -1159,6 +1167,7 @@ class PrepareBuild(CommandLineApp): self.LDFLAGS.append('-g') def setup_flavor_opt(self): + self.CPPFLAGS.append('-DNDEBUG=1') if self.darwin_gcc: self.option_no_pch() if '--disable-shared' in self.configure_args: diff --git a/src/account.cc b/src/account.cc index 8d4341e7..e577c48e 100644 --- a/src/account.cc +++ b/src/account.cc @@ -218,6 +218,10 @@ namespace { return true; } + value_t get_addr(account_t& account) { + return long(&account); + } + value_t get_depth_spacer(account_t& account) { std::size_t depth = 0; @@ -296,6 +300,8 @@ expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_account>); else if (name == "account_base") return WRAP_FUNCTOR(get_wrapper<&get_account_base>); + else if (name == "addr") + return WRAP_FUNCTOR(get_wrapper<&get_addr>); else if (name == "any") return WRAP_FUNCTOR(&fn_any); else if (name == "all") diff --git a/src/amount.cc b/src/amount.cc index 9b7c3676..7eb94442 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -149,10 +149,6 @@ namespace { mpfr_set_prec(tempfb, num_prec + den_prec); mpfr_div(tempfb, tempfnum, tempfden, GMP_RNDN); - char bigbuf[4096]; - mpfr_sprintf(bigbuf, "%.RNf", tempfb); - DEBUG("amount.convert", "num/den = " << bigbuf); - if (mpfr_asprintf(&buf, "%.*RNf", precision, tempfb) < 0) throw_(amount_error, _("Cannot output amount to a floating-point representation")); @@ -244,14 +240,18 @@ void amount_t::initialize() // in terms of seconds, but reported as minutes or hours. if (commodity_t * commodity = commodity_pool_t::current_pool->create("s")) commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); +#if !defined(NO_ASSERTS) else assert(false); +#endif // Add a "percentile" commodity if (commodity_t * commodity = commodity_pool_t::current_pool->create("%")) commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); +#if !defined(NO_ASSERTS) else assert(false); +#endif is_initialized = true; } @@ -472,7 +472,7 @@ amount_t& amount_t::operator-=(const amount_t& amt) return *this; } -amount_t& amount_t::operator*=(const amount_t& amt) +amount_t& amount_t::multiply(const amount_t& amt, bool ignore_commodity) { VERIFY(amt.valid()); @@ -491,7 +491,7 @@ amount_t& amount_t::operator*=(const amount_t& amt) quantity->prec = static_cast<precision_t>(quantity->prec + amt.quantity->prec); - if (! has_commodity()) + if (! has_commodity() && ! ignore_commodity) commodity_ = amt.commodity_; if (has_commodity() && ! keep_precision()) { @@ -741,24 +741,33 @@ amount_t::value(const bool primary_only, #endif if (has_commodity() && (! primary_only || ! commodity().has_flags(COMMODITY_PRIMARY))) { - if (in_terms_of && commodity() == *in_terms_of) { + if (in_terms_of && + commodity().referent() == in_terms_of->referent()) { return *this; } else if (has_annotation() && annotation().price && annotation().has_flags(ANNOTATION_PRICE_FIXATED)) { - return (*annotation().price * number()).rounded(); + amount_t price(*annotation().price); + price.multiply(*this, true); + price.in_place_round(); + return price; } else { optional<price_point_t> point = commodity().find_price(in_terms_of, moment); - // Whether a price was found or not, check whether we should attempt - // to download a price from the Internet. This is done if (a) no - // price was found, or (b) the price is "stale" according to the - // setting of --price-exp. - point = commodity().check_for_updated_price(point, moment, in_terms_of); - if (point) - return (point->price * number()).rounded(); + // Whether a price was found or not, check whether we should + // attempt to download a price from the Internet. This is done + // if (a) no price was found, or (b) the price is "stale" + // according to the setting of --price-exp. + point = commodity().check_for_updated_price(point, moment, + in_terms_of); + if (point) { + amount_t price(point->price); + price.multiply(*this, true); + price.in_place_round(); + return price; + } } } } else { diff --git a/src/amount.h b/src/amount.h index ae0e5a69..49f33417 100644 --- a/src/amount.h +++ b/src/amount.h @@ -62,7 +62,6 @@ namespace ledger { class commodity_t; class annotation_t; class keep_details_t; -class commodity_pool_t; DECLARE_EXCEPTION(amount_error, std::runtime_error); @@ -275,7 +274,10 @@ public: amount_t& operator+=(const amount_t& amt); amount_t& operator-=(const amount_t& amt); - amount_t& operator*=(const amount_t& amt); + amount_t& operator*=(const amount_t& amt) { + return multiply(amt); + } + amount_t& multiply(const amount_t& amt, bool ignore_commodity = false); /** Divide two amounts while extending the precision to preserve the accuracy of the result. For example, if \c 10 is divided by \c 3, @@ -511,7 +513,7 @@ public: amount's commodity: commodity() returns an amount's commodity. If the amount has no - commodity, the value returned is `current_pool->null_commodity'. + commodity, the value returned is the `null_commodity'. has_commodity() returns true if the amount has a commodity. diff --git a/src/annotate.cc b/src/annotate.cc index 99c12fc3..feb3b3ca 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -190,7 +190,7 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) keep_date ? details.date : none, keep_tag ? details.tag : none)); } else { - new_comm = pool().find_or_create(base_symbol()); + new_comm = &referent(); } assert(new_comm); diff --git a/src/commodity.cc b/src/commodity.cc index 1b85910f..e45332b2 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -120,12 +120,16 @@ commodity_t::history_t::find_price(const optional<datetime_t>& moment, 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) DEBUG_INDENT("commodity.prices.find", indent); @@ -136,38 +140,34 @@ commodity_t::history_t::find_price(const optional<datetime_t>& moment, if (oldest) { DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " but no older than: " << *oldest); + DEBUG("commodity.prices.find", "but no older than: " << *oldest); } #endif if (prices.size() == 0) { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " there are no prices in this history"); -#endif + DEBUG("commodity.prices.find", "there are no prices in this history"); return none; } if (! moment) { history_map::const_reverse_iterator r = prices.rbegin(); - point.when = (*r).first; + point.when = (*r).first; point.price = (*r).second; found = true; -#if defined(DEBUG_ON) + DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " using most recent price"); -#endif + DEBUG("commodity.prices.find", "using most recent price"); } else { - history_map::const_iterator i = prices.lower_bound(*moment); + 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; -#if defined(DEBUG_ON) + DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " using last price"); -#endif + DEBUG("commodity.prices.find", "using last price"); } else { point.when = (*i).first; if (*moment < point.when) { @@ -181,40 +181,31 @@ commodity_t::history_t::find_price(const optional<datetime_t>& moment, point.price = (*i).second; found = true; } -#if defined(DEBUG_ON) + DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " using found price"); -#endif + DEBUG("commodity.prices.find", "using found price"); } } if (! found) { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " could not find a price"); -#endif + DEBUG("commodity.prices.find", "could not find a price"); return none; } else if (moment && point.when > *moment) { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " price is too young "); -#endif + DEBUG("commodity.prices.find", "price is too young "); return none; } else if (oldest && point.when < *oldest) { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " price is too old "); -#endif + DEBUG("commodity.prices.find", "price is too old "); return none; } else { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent); DEBUG("commodity.prices.find", - " returning price: " << point.when << ", " << point.price); -#endif + "returning price: " << point.when << ", " << point.price); return point; } } @@ -232,7 +223,13 @@ commodity_t::varied_history_t::find_price(const commodity_t& source, optional<price_point_t> point; optional<datetime_t> limit = oldest; - assert(! commodity || source != *commodity); +#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); @@ -240,18 +237,18 @@ commodity_t::varied_history_t::find_price(const commodity_t& source, DEBUG_INDENT("commodity.prices.find", indent); if (commodity) - DEBUG("commodity.prices.find", " looking for: commodity '" << *commodity << "'"); + DEBUG("commodity.prices.find", "looking for: commodity '" << *commodity << "'"); else - DEBUG("commodity.prices.find", " looking for: any commodity"); + DEBUG("commodity.prices.find", "looking for: any commodity"); if (moment) { DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", " time index: " << *moment); + 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); + DEBUG("commodity.prices.find", "only consider prices younger than: " << *oldest); } #endif @@ -262,16 +259,18 @@ commodity_t::varied_history_t::find_price(const commodity_t& source, price_point_t best; bool found = false; - foreach (history_by_commodity_map::value_type hist, histories) { + foreach (const history_by_commodity_map::value_type& hist, histories) { commodity_t& comm(*hist.first); if (comm == source) continue; -#if defined(DEBUG_ON) + // Only value secondary commodities in terms of primary ones + if (! commodity && ! comm.has_flags(COMMODITY_PRIMARY)) + continue; + DEBUG_INDENT("commodity.prices.find", indent + 1); DEBUG("commodity.prices.find", - " searching for price via commodity '" << comm << "'"); -#endif + "searching for price via commodity '" << comm << "'"); point = hist.second.find_price(moment, limit #if defined(DEBUG_ON) @@ -284,68 +283,59 @@ commodity_t::varied_history_t::find_price(const commodity_t& source, optional<price_point_t> xlat; if (commodity && comm != *commodity) { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", " looking for translation price"); -#endif + DEBUG("commodity.prices.find", "looking for translation price"); - xlat = comm.find_price(commodity, moment, limit + xlat = comm.find_price(commodity, moment, limit, true #if defined(DEBUG_ON) , indent + 2 #endif ); if (xlat) { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", " found translated price " + DEBUG("commodity.prices.find", "found translated price " << xlat->price << " from " << xlat->when); -#endif point->price = xlat->price * point->price; if (xlat->when < point->when) { point->when = xlat->when; -#if defined(DEBUG_ON) + DEBUG_INDENT("commodity.prices.find", indent + 1); DEBUG("commodity.prices.find", - " adjusting date of result back to " << point->when); -#endif + "adjusting date of result back to " << point->when); } } else { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", " saw no translated price there"); -#endif + DEBUG("commodity.prices.find", "saw no translated price there"); continue; } } assert(! commodity || point->price.commodity() == *commodity); -#if defined(DEBUG_ON) + DEBUG_INDENT("commodity.prices.find", indent + 1); DEBUG("commodity.prices.find", - " saw a price there: " << point->price << " from " << point->when); -#endif + "saw a price there: " << point->price << " from " << point->when); + if (! limit || point->when > *limit) { limit = point->when; best = *point; found = true; + + DEBUG_INDENT("commodity.prices.find", indent + 1); + DEBUG("commodity.prices.find", + "search limit adjusted to " << *limit); } } else { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent + 1); - DEBUG("commodity.prices.find", " saw no price there"); -#endif + DEBUG("commodity.prices.find", "saw no price there"); } } if (found) { -#if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", - " found price " << best.price << " from " << best.when); DEBUG("commodity.download", "found price " << best.price << " from " << best.when); -#endif return best; } return none; @@ -371,6 +361,97 @@ commodity_t::varied_history_t::history(const optional<commodity_t&>& commodity) } optional<price_point_t> +commodity_t::find_price(const optional<commodity_t&>& commodity, + const optional<datetime_t>& moment, + const optional<datetime_t>& oldest, + const bool nested +#if defined(DEBUG_ON) + , const int indent +#endif + ) const +{ + if (! has_flags(COMMODITY_WALKED) && base->varied_history && + (commodity || ! has_flags(COMMODITY_PRIMARY))) { + optional<base_t::time_and_commodity_t> pair; +#if defined(VERIFY_ON) + optional<price_point_t> 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<price_point_t> point; + + const_cast<commodity_t&>(*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<commodity_t&>(*this).drop_flags(COMMODITY_WALKED); + throw; + } + const_cast<commodity_t&>(*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; + } + return none; +} + +optional<price_point_t> commodity_t::check_for_updated_price(const optional<price_point_t>& point, const optional<datetime_t>& moment, const optional<commodity_t&>& in_terms_of) @@ -428,9 +509,7 @@ namespace { { switch (buf[0]) { case 'a': - return (std::strcmp(buf, "and") == 0 || - std::strcmp(buf, "any") == 0 || - std::strcmp(buf, "all") == 0); + return std::strcmp(buf, "and") == 0; case 'd': return std::strcmp(buf, "div") == 0; case 'e': diff --git a/src/commodity.h b/src/commodity.h index 53e3033f..d8aad10d 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -50,6 +50,7 @@ namespace ledger { class keep_details_t; +class commodity_pool_t; DECLARE_EXCEPTION(commodity_error, std::runtime_error); @@ -58,6 +59,10 @@ struct price_point_t datetime_t when; amount_t price; + bool operator==(const price_point_t& other) const { + return when == other.when && price == other.price; + } + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ @@ -174,6 +179,16 @@ protected: optional<amount_t> smaller; optional<amount_t> larger; + typedef std::pair<optional<datetime_t>, + optional<datetime_t> > optional_time_pair_t; + typedef std::pair<optional_time_pair_t, + commodity_t *> time_and_commodity_t; + typedef std::map<time_and_commodity_t, + optional<price_point_t> > memoized_price_map; + + static const std::size_t max_price_map_size = 16; + mutable memoized_price_map price_map; + mutable bool searched; public: @@ -333,36 +348,28 @@ public: 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) + 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; } optional<price_point_t> find_price(const optional<commodity_t&>& commodity = none, const optional<datetime_t>& moment = none, - const optional<datetime_t>& oldest = none + const optional<datetime_t>& oldest = none, + const bool nested = false #if defined(DEBUG_ON) , const int indent = 0 #endif - ) const { - if (base->varied_history && ! has_flags(COMMODITY_WALKED)) { - const_cast<commodity_t&>(*this).add_flags(COMMODITY_WALKED); - optional<price_point_t> point = - base->varied_history->find_price(*this, commodity, moment, oldest -#if defined(DEBUG_ON) - , indent -#endif - ); - const_cast<commodity_t&>(*this).drop_flags(COMMODITY_WALKED); - return point; - } - return none; - } + ) const; optional<price_point_t> check_for_updated_price(const optional<price_point_t>& point, diff --git a/src/exprbase.h b/src/exprbase.h index 0dbf3bff..cf81a0a7 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -155,17 +155,21 @@ public: result_type calc(scope_t& scope) { if (! compiled) { +#if defined(DEBUG_ON) if (SHOW_DEBUG("expr.compile")) { DEBUG("expr.compile", "Before compilation:"); dump(*_log_stream); } +#endif // defined(DEBUG_ON) compile(scope); +#if defined(DEBUG_ON) if (SHOW_DEBUG("expr.compile")) { DEBUG("expr.compile", "After compilation:"); dump(*_log_stream); } +#endif // defined(DEBUG_ON) } return real_calc(scope); diff --git a/src/filters.cc b/src/filters.cc index ad4b88a0..07278500 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -50,7 +50,7 @@ void post_splitter::print_title(const value_t& val) void post_splitter::flush() { - foreach (value_to_posts_map::value_type pair, posts_map) { + foreach (value_to_posts_map::value_type& pair, posts_map) { preflush_func(pair.first); foreach (post_t * post, pair.second) @@ -256,6 +256,7 @@ void anonymize_posts::operator()(post_t& post) create_temp_account_from_path(account_names, temps, xact.journal->master); post_t& temp = temps.copy_post(post, xact, new_account); temp.note = none; + temp.add_flags(POST_ANONYMIZED); (*handler)(temp); } @@ -894,7 +895,7 @@ void posts_as_equity::report_subtotal() value_t total = 0L; foreach (values_map::value_type& pair, values) { if (pair.second.value.is_balance()) { - foreach (balance_t::amounts_map::value_type amount_pair, + foreach (const balance_t::amounts_map::value_type& amount_pair, pair.second.value.as_balance().amounts) handle_value(amount_pair.second, pair.second.account, &xact, temps, handler); @@ -907,7 +908,7 @@ void posts_as_equity::report_subtotal() values.clear(); if (total.is_balance()) { - foreach (balance_t::amounts_map::value_type pair, + 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; @@ -1166,8 +1167,10 @@ void forecast_posts::flush() } date_t& begin = *(*least).first.start; +#if !defined(NO_ASSERTS) if ((*least).first.finish) assert(begin < *(*least).first.finish); +#endif // If the next date in the series for this periodic posting is more than 5 // years beyond the last valid post we generated, drop it from further diff --git a/src/filters.h b/src/filters.h index a66d8c47..327499fb 100644 --- a/src/filters.h +++ b/src/filters.h @@ -226,16 +226,12 @@ class sort_posts : public item_handler<post_t> public: sort_posts(post_handler_ptr handler, const expr_t& _sort_order) - : item_handler<post_t>(handler), - sort_order(_sort_order) { - TRACE_CTOR(sort_posts, - "post_handler_ptr, const value_expr&"); + : item_handler<post_t>(handler), sort_order(_sort_order) { + TRACE_CTOR(sort_posts, "post_handler_ptr, const value_expr&"); } sort_posts(post_handler_ptr handler, const string& _sort_order) - : item_handler<post_t>(handler), - sort_order(_sort_order) { - TRACE_CTOR(sort_posts, - "post_handler_ptr, const string&"); + : item_handler<post_t>(handler), sort_order(_sort_order) { + TRACE_CTOR(sort_posts, "post_handler_ptr, const string&"); } virtual ~sort_posts() { TRACE_DTOR(sort_posts); diff --git a/src/global.cc b/src/global.cc index 170509b0..35651ddb 100644 --- a/src/global.cc +++ b/src/global.cc @@ -391,11 +391,13 @@ global_scope_t::read_command_arguments(scope_t& scope, strings_list args) void global_scope_t::normalize_session_options() { +#if defined(LOGGING_ON) INFO("Initialization file is " << HANDLER(init_file_).str()); INFO("Price database is " << session().HANDLER(price_db_).str()); foreach (const path& pathname, session().HANDLER(file_).data_files) INFO("Journal file is " << pathname.string()); +#endif // defined(LOGGING_ON) } expr_t::func_t global_scope_t::look_for_precommand(scope_t& scope, diff --git a/src/item.cc b/src/item.cc index 0a22b260..fea73066 100644 --- a/src/item.cc +++ b/src/item.cc @@ -323,6 +323,10 @@ namespace { return item.pos ? long(item.pos->sequence) : 0L; } + value_t get_addr(item_t& item) { + return long(&item); + } + value_t get_depth(item_t&) { return 0L; } @@ -376,6 +380,8 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_actual>); else if (name == "actual_date") return WRAP_FUNCTOR(get_wrapper<&get_actual_date>); + else if (name == "addr") + return WRAP_FUNCTOR(get_wrapper<&get_addr>); break; case 'b': diff --git a/src/iterators.cc b/src/iterators.cc index f3a68978..ade1d4b3 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -94,9 +94,9 @@ void posts_commodities_iterator::reset(journal_t& journal) comm->varied_history()) { account_t * account = journal.master->find_account(comm->symbol()); - foreach (commodity_t::history_by_commodity_map::value_type pair, + foreach (commodity_t::history_by_commodity_map::value_type& pair, history->histories) { - foreach (commodity_t::history_map::value_type hpair, + foreach (commodity_t::history_map::value_type& hpair, pair.second.prices) { xact_t * xact; string symbol = hpair.second.commodity().symbol(); @@ -78,10 +78,12 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) // 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. +#if defined(DEBUG_ON) if (SHOW_DEBUG("expr.compile")) { DEBUG("expr.compile", "Found definition:"); def->dump(*_log_stream, 0); } +#endif // defined(DEBUG_ON) return copy(def); } else if (left()) { diff --git a/src/parser.h b/src/parser.h index aab48830..2693fc79 100644 --- a/src/parser.h +++ b/src/parser.h @@ -61,10 +61,16 @@ class expr_t::parser_t : public noncopyable return lookahead; } +#if !defined(NO_ASSERTS) void push_token(const token_t& tok) const { assert(&tok == &lookahead); use_lookahead = true; } +#else + void push_token(const token_t&) const { + use_lookahead = true; + } +#endif // !defined(NO_ASSERTS) void push_token() const { use_lookahead = true; } diff --git a/src/pool.cc b/src/pool.cc index 8d9f0c28..ad97a9c6 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -122,8 +122,11 @@ string commodity_pool_t::make_qualified_name(const commodity_t& comm, comm.print(name); details.print(name, comm.pool().keep_base); - DEBUG("amounts.commodities", "make_qualified_name for " - << *comm.qualified_symbol << std::endl << details); +#if defined(DEBUG_ON) + if (comm.qualified_symbol) + DEBUG("amounts.commodities", "make_qualified_name for " + << *comm.qualified_symbol << std::endl << details); +#endif DEBUG("amounts.commodities", "qualified_name is " << name.str()); return name.str(); @@ -361,4 +364,76 @@ 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<datetime_t>& moment) +{ + typedef std::map<commodity_t *, commodity_t *> 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<commodity_t::varied_history_t&> 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 @@ -86,7 +86,7 @@ public: virtual ~commodity_pool_t() { TRACE_DTOR(commodity_pool_t); - foreach (commodities_map::value_type pair, commodities) + foreach (commodities_map::value_type& pair, commodities) checked_delete(pair.second); } @@ -131,6 +131,12 @@ public: const bool add_prices = true, const optional<datetime_t>& 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<datetime_t>& moment = none); + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ @@ -52,11 +52,12 @@ class account_t; class post_t : public item_t { public: -#define POST_VIRTUAL 0x08 // the account was specified with (parens) -#define POST_MUST_BALANCE 0x10 // posting must balance in the transaction -#define POST_CALCULATED 0x20 // posting's amount was calculated -#define POST_COST_CALCULATED 0x40 // posting's cost was calculated -#define POST_COST_IN_FULL 0x80 // cost specified using @@ +#define POST_VIRTUAL 0x04 // the account was specified with (parens) +#define POST_MUST_BALANCE 0x08 // posting must balance in the transaction +#define POST_CALCULATED 0x10 // posting's amount was calculated +#define POST_COST_CALCULATED 0x20 // posting's cost was calculated +#define POST_COST_IN_FULL 0x40 // cost specified using @@ +#define POST_ANONYMIZED 0x80 // a temporary, anonymous posting xact_t * xact; // only set for posts of regular xacts account_t * account; diff --git a/src/print.cc b/src/print.cc index f93f5fe8..703e885c 100644 --- a/src/print.cc +++ b/src/print.cc @@ -120,6 +120,7 @@ namespace { foreach (post_t * post, xact.posts) { if (post->has_flags(ITEM_TEMP | ITEM_GENERATED) && + ! post->has_flags(POST_ANONYMIZED) && ! report.HANDLED(print_virtual)) continue; diff --git a/src/py_utils.cc b/src/py_utils.cc index 4b364e5d..952416cc 100644 --- a/src/py_utils.cc +++ b/src/py_utils.cc @@ -115,8 +115,10 @@ struct string_from_python utf8::unchecked::utf16to8(value, value + size, std::back_inserter(str)); else if (sizeof(Py_UNICODE) == 4) // UTF-32 utf8::unchecked::utf32to8(value, value + size, std::back_inserter(str)); +#if !defined(NO_ASSERTS) else assert(! "Py_UNICODE has an unexpected size"); +#endif if (value == 0) throw_error_already_set(); void* storage = diff --git a/src/report.cc b/src/report.cc index cc652f8d..f0923b33 100644 --- a/src/report.cc +++ b/src/report.cc @@ -842,6 +842,18 @@ value_t report_t::echo_command(call_scope_t& scope) return true; } +value_t report_t::pricemap_command(call_scope_t& scope) +{ + interactive_t args(scope, "&s"); + std::ostream& out(output_stream); + + commodity_pool_t::current_pool->print_pricemap + (out, what_to_keep(), args.has(0) ? + optional<datetime_t>(datetime_t(parse_date(args.get<string>(0)))) : none); + + return true; +} + option_t<report_t> * report_t::lookup_option(const char * p) { switch (*p) { @@ -1318,9 +1330,10 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case symbol_t::COMMAND: switch (*p) { case 'a': - if (is_eq(p, "accounts")) + if (is_eq(p, "accounts")) { return WRAP_FUNCTOR(reporter<>(new report_accounts(*this), *this, "#accounts")); + } break; case 'b': @@ -1381,48 +1394,60 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, HANDLER(print_virtual).on_only(string("#equity")); return WRAP_FUNCTOR(reporter<>(new print_xacts(*this), *this, "#equity")); } - else if (is_eq(p, "entry")) + else if (is_eq(p, "entry")) { return WRAP_FUNCTOR(xact_command); - else if (is_eq(p, "emacs")) + } + else if (is_eq(p, "emacs")) { return WRAP_FUNCTOR (reporter<>(new format_emacs_posts(output_stream), *this, "#emacs")); - else if (is_eq(p, "echo")) + } + else if (is_eq(p, "echo")) { return MAKE_FUNCTOR(report_t::echo_command); + } break; case 'p': - if (*(p + 1) == '\0' || is_eq(p, "print")) + if (*(p + 1) == '\0' || is_eq(p, "print")) { return WRAP_FUNCTOR (reporter<>(new print_xacts(*this, HANDLED(raw)), *this, "#print")); - else if (is_eq(p, "prices")) + } + else if (is_eq(p, "prices")) { return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::commodities_report> (new format_posts(*this, report_format(HANDLER(prices_format_)), maybe_format(HANDLER(prepend_format_)), HANDLER(prepend_width_).value.to_long()), *this, "#prices")); - else if (is_eq(p, "pricedb")) + } + else if (is_eq(p, "pricedb")) { return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::commodities_report> (new format_posts(*this, report_format(HANDLER(pricedb_format_)), maybe_format(HANDLER(prepend_format_)), HANDLER(prepend_width_).value.to_long()), *this, "#pricedb")); - else if (is_eq(p, "payees")) + } + 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")); + } break; case 'r': - if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) + 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_long()), *this, "#register")); - else if (is_eq(p, "reload")) + } + else if (is_eq(p, "reload")) { return MAKE_FUNCTOR(report_t::reload_command); + } break; case 's': @@ -1448,6 +1473,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'e': if (is_eq(p, "eval")) return WRAP_FUNCTOR(eval_command); + else if (is_eq(p, "expr")) + return WRAP_FUNCTOR(parse_command); break; case 'f': if (is_eq(p, "format")) diff --git a/src/report.h b/src/report.h index 6fa238f0..99b8781b 100644 --- a/src/report.h +++ b/src/report.h @@ -198,6 +198,7 @@ public: value_t reload_command(call_scope_t&); value_t echo_command(call_scope_t& scope); + value_t pricemap_command(call_scope_t& scope); keep_details_t what_to_keep() { bool lots = HANDLED(lots) || HANDLED(lots_actual); diff --git a/src/session.h b/src/session.h index 10f636bb..3916b964 100644 --- a/src/session.h +++ b/src/session.h @@ -50,7 +50,6 @@ namespace ledger { -class commodity_pool_t; class xact_t; class session_t : public symbol_scope_t diff --git a/src/textual.cc b/src/textual.cc index 9a49edd4..85b1a14b 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -695,8 +695,10 @@ void instance_t::master_account_directive(char * line) { if (account_t * acct = context.top_account()->find_account(line)) context.state_stack.push_front(acct); +#if !defined(NO_ASSERTS) else assert(! "Failed to create account"); +#endif } void instance_t::end_directive(char * kind) @@ -763,8 +765,12 @@ void instance_t::payee_mapping_directive(char * line) (payee_mapping_t(mask_t(regex), payee)); 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) @@ -786,8 +792,12 @@ void instance_t::account_mapping_directive(char * line) 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) diff --git a/src/times.cc b/src/times.cc index a7906aee..60e8e7cc 100644 --- a/src/times.cc +++ b/src/times.cc @@ -333,10 +333,12 @@ date_t date_specifier_t::begin(const optional_year& current_year) const month_type the_month = month ? *month : date_t::month_type(1); day_type the_day = day ? *day : date_t::day_type(1); +#if !defined(NO_ASSERTS) if (day) assert(! wday); else if (wday) assert(! day); +#endif // jww (2009-11-16): Handle wday. If a month is set, find the most recent // wday in that month; if the year is set, then in that year. diff --git a/src/utils.h b/src/utils.h index a0c3f49f..9135abde 100644 --- a/src/utils.h +++ b/src/utils.h @@ -49,17 +49,17 @@ */ /*@{*/ +#define TIMERS_ON 1 + #if defined(DEBUG_MODE) #define VERIFY_ON 1 #define TRACING_ON 1 #define DEBUG_ON 1 -#define TIMERS_ON 1 #elif defined(NDEBUG) #define NO_ASSERTS 1 -#define NO_LOGGING 1 +//#define NO_LOGGING 1 #else #define TRACING_ON 1 // use --trace X to enable -#define TIMERS_ON 1 #endif /*@}*/ @@ -434,8 +434,8 @@ void finish_timer(const char * name); (SHOW_TRACE(lvl) ? ledger::finish_timer(#name) : ((void)0)) #else #define TRACE_START(name, lvl, msg) -#define TRACE_STOP(name) -#define TRACE_FINISH(name) +#define TRACE_STOP(name, lvl) +#define TRACE_FINISH(name, lvl) #endif #if defined(DEBUG_ON) @@ -474,8 +474,8 @@ void finish_timer(const char * name); #else // ! (LOGGING_ON && TIMERS_ON) #define TRACE_START(lvl, msg, name) -#define TRACE_STOP(name) -#define TRACE_FINISH(name) +#define TRACE_STOP(name, lvl) +#define TRACE_FINISH(name, lvl) #define DEBUG_START(name, msg) #define DEBUG_START_(name, cat, msg) diff --git a/src/value.cc b/src/value.cc index a967eeb8..e9313f0c 100644 --- a/src/value.cc +++ b/src/value.cc @@ -935,6 +935,20 @@ bool value_t::is_less_than(const value_t& val) const } return ! no_amounts; } + case SEQUENCE: { + sequence_t::const_iterator i = as_sequence().begin(); + sequence_t::const_iterator j = val.as_sequence().begin(); + for (; (i != as_sequence().end() && + j != val.as_sequence().end()); i++, j++) { + if (! ((*i) < (*j))) + return false; + } + if (i == as_sequence().end()) + return true; + else + return false; + break; + } default: break; } @@ -1041,6 +1055,20 @@ bool value_t::is_greater_than(const value_t& val) const } return ! no_amounts; } + case SEQUENCE: { + sequence_t::const_iterator i = as_sequence().begin(); + sequence_t::const_iterator j = val.as_sequence().begin(); + for (; (i != as_sequence().end() && + j != val.as_sequence().end()); i++, j++) { + if (! ((*i) > (*j))) + return false; + } + if (i == as_sequence().end()) + return false; + else + return true; + break; + } default: break; } diff --git a/src/value.h b/src/value.h index 3252ed65..2e3998f3 100644 --- a/src/value.h +++ b/src/value.h @@ -878,7 +878,6 @@ public: sequence_t::iterator begin() { return as_sequence_lval().begin(); } - sequence_t::iterator end() { return as_sequence_lval().end(); } @@ -886,7 +885,6 @@ public: sequence_t::const_iterator begin() const { return as_sequence().begin(); } - sequence_t::const_iterator end() const { return as_sequence().end(); } diff --git a/src/xact.cc b/src/xact.cc index 569e5869..6c10ac04 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -62,10 +62,12 @@ xact_base_t::~xact_base_t() void xact_base_t::add_post(post_t * post) { +#if !defined(NO_ASSERTS) // You can add temporary postings to transactions, but not real postings to // temporary transactions. if (! post->has_flags(ITEM_TEMP)) assert(! has_flags(ITEM_TEMP)); +#endif posts.push_back(post); } diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index a74b0601..251b0f24 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -2,18 +2,18 @@ <<< >>>1 >>>2 -While parsing file "$sourcepath/src/amount.h", line 67: +While parsing file "$sourcepath/src/amount.h", line 66: Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 718: +While parsing file "$sourcepath/src/amount.h", line 720: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 724: +While parsing file "$sourcepath/src/amount.h", line 726: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 730: +While parsing file "$sourcepath/src/amount.h", line 732: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 736: +While parsing file "$sourcepath/src/amount.h", line 738: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 742: +While parsing file "$sourcepath/src/amount.h", line 744: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 749: +While parsing file "$sourcepath/src/amount.h", line 751: Error: Invalid date/time: line std::istream& === 7 diff --git a/tools/pre-commit b/tools/pre-commit index c049b8f0..50a32dbd 100755 --- a/tools/pre-commit +++ b/tools/pre-commit @@ -5,7 +5,7 @@ set -e # Exit if it's not a branch we're interested in being thorough about if echo $(git rev-parse --symbolic-full-name HEAD) | \ - egrep -q '^refs/heads/(next|t/)'; then + egrep -q '^refs/heads/(t/)'; then exit 0 fi @@ -41,12 +41,12 @@ cd $TMPDIR if [ ! -f Makefile -o \ Makefile.in -nt Makefile -o \ configure -nt Makefile -o \ - Makefile.am -nt Makefile.in -o \ - configure.ac -nt configure -o \ + tools/Makefile.am -nt Makefile.in -o \ + tools/configure.ac -nt configure -o \ \( -f acprep -a acprep -nt Makefile \) ] then if [ -f acprep ]; then - ./acprep default --local + echo Will run acprep in a moment elif [ -f autogen.sh ]; then sh autogen.sh && ./configure else @@ -56,6 +56,10 @@ fi # Finally, (re)build this proposed source tree and see if it passes # muster. -nice -n 20 make check +if [ -f acprep ]; then + nice -n 20 ./acprep default --warn make check +else + nice -n 20 make check +fi exit 0 |