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 --- test/baseline/cmd-script.test | 3 +++ test/baseline/cmd-script_2.test | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 test/baseline/cmd-script.test create mode 100644 test/baseline/cmd-script_2.test (limited to 'test') 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 f3e1c8ded03be3d6cdbb4819178e8b446c1b4798 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 21 Feb 2012 03:55:13 -0600 Subject: Fixed cmd-script_2.test --- test/baseline/cmd-script_2.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/baseline/cmd-script_2.test b/test/baseline/cmd-script_2.test index a6f6f37a..51ea41fb 100644 --- a/test/baseline/cmd-script_2.test +++ b/test/baseline/cmd-script_2.test @@ -1,3 +1,3 @@ -test eval 'x=total_expr;x=x/count;x=amount_expr-x*count;x' -0 +test eval 'total_expr=$100;amount_expr=$15;x=total_expr;x=x/5;x=amount_expr-x*5;x' +$-85 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 'test') 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 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 'test') 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 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 'test') 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 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 'test') 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 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 'test') 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 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 'test') 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 'test') 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 'test') 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 'test') 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 9240403011f275c2a2b6b02141635359a8a6ae17 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 12:38:31 -0600 Subject: Improved a test --- test/baseline/dir-account.test | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/baseline/dir-account.test b/test/baseline/dir-account.test index b049ae83..39831663 100644 --- a/test/baseline/dir-account.test +++ b/test/baseline/dir-account.test @@ -1,3 +1,9 @@ +--explicit +--pedantic + +commodity $ + format $1,000.00 + account Assets:Cash assert abs(amount) <= 20 check commodity == '$' @@ -6,9 +12,6 @@ account Expenses:Food alias food payee KFC -commodity $ - format $1,000.00 - 2012-02-27 KFC Expenses:Unknown $20.00 Assets:Cash @@ -17,7 +20,7 @@ commodity $ food $20.00 Assets:Cash -test reg --strict +test reg 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 -- cgit v1.2.3 From 69da09e85f54d811eb8a28e3e9487b1c350dcee9 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 19:49:53 -0600 Subject: Added regression test for #657 --- test/regress/DB490507.test | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/regress/DB490507.test (limited to 'test') diff --git a/test/regress/DB490507.test b/test/regress/DB490507.test new file mode 100644 index 00000000..24443d2a --- /dev/null +++ b/test/regress/DB490507.test @@ -0,0 +1,25 @@ +2001/11/07=2001/11/04 * Autoroutes du Sud de la France + Dépense:Vacances:Voyage ; 14F Tlse-Montauban, 8F Montauban-Caussade, 8F Caussade-Montauban, 14F Montauban-Tlse + Actif:Courant:BnpCc -6,71 € + +2008/01/20 * La Poste + Equity + Actif:Courant:LaPosteLivretA 10,00 € + +2008/01/20 * La Poste + Revenu:Invest:Exonéré + Actif:Courant:LaPosteLivretA 25,24 € = 35,24 € + +test print --decimal-comma --columns=999 +2001/11/07=2001/11/04 * Autoroutes du Sud de la France + Dépense:Vacances:Voyage ; 14F Tlse-Montauban, 8F Montauban-Caussade, 8F Caussade-Montauban, 14F Montauban-Tlse + Actif:Courant:BnpCc -6,71 € + +2008/01/20 * La Poste + Equity + Actif:Courant:LaPosteLivretA 10,00 € + +2008/01/20 * La Poste + Revenu:Invest:Exonéré + Actif:Courant:LaPosteLivretA 25,24 € = 35,24 € +end test -- cgit v1.2.3 From 60617bfd6e146bfcdfacd053f51191251b79cfa6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 27 Feb 2012 22:47:53 -0600 Subject: Use "apply tag" instead of "tag" in test/input/sample.dat --- test/input/sample.dat | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/input/sample.dat b/test/input/sample.dat index 12ac4cb4..5b411edd 100644 --- a/test/input/sample.dat +++ b/test/input/sample.dat @@ -33,7 +33,7 @@ N $ Русский язык:Активы:Русский язык:Русский язык $1000.00 Income:Salary -tag foo +apply tag foo 2004/05/27 Book Store Expenses:Books $20.00 @@ -41,7 +41,7 @@ tag foo Expenses:Docs $30.00 Liabilities:MasterCard -end tag +end apply tag 2004/05/27 (100) Credit card company ; This is an xact note! @@ -52,3 +52,5 @@ end tag ; :MyTag: Assets:Bank:Checking ; :AnotherTag: + +;;; sample.dat ends here -- 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 'test') 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 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 'test') 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 'test') 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 'test') 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 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 'test') 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 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 'test') 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 'test') 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 f29fc1eb12376f1bb447f8e397fb98daa7ac3327 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 29 Feb 2012 22:13:07 -0600 Subject: Added skeletons for Python unit tests --- test/PyUnitTests.py | 11 +++++++++-- test/python/JournalTest.py | 25 +++++++++++++++++++++++++ test/python/PostingTest.py | 25 +++++++++++++++++++++++++ test/python/TransactionTest.py | 25 +++++++++++++++++++++++++ test/python/UnitTests.py | 12 ++++++++++++ tools/Makefile.am | 4 ++-- 6 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 test/python/JournalTest.py create mode 100644 test/python/PostingTest.py create mode 100644 test/python/TransactionTest.py create mode 100644 test/python/UnitTests.py (limited to 'test') diff --git a/test/PyUnitTests.py b/test/PyUnitTests.py index a77d99ad..2aed48b9 100755 --- a/test/PyUnitTests.py +++ b/test/PyUnitTests.py @@ -1,6 +1,13 @@ -#!/bin/sh +#!/bin/bash + +set -e + +PYTHONPATH="%builddir%/.libs":$PYTHONPATH \ +LD_LIBRARY_PATH="%builddir%/.libs":$LD_LIBRARY_PATH \ +DYLD_LIBRARY_PATH="%builddir%/.libs":$DYLD_LIBRARY_PATH \ + %python% "%builddir%"/test/python/ConvertedTests.py PYTHONPATH="%builddir%/.libs":$PYTHONPATH \ LD_LIBRARY_PATH="%builddir%/.libs":$LD_LIBRARY_PATH \ DYLD_LIBRARY_PATH="%builddir%/.libs":$DYLD_LIBRARY_PATH \ - %python% "%builddir%"/test/python/UnitTests.py + %python% "%srcdir%"/test/python/UnitTests.py diff --git a/test/python/JournalTest.py b/test/python/JournalTest.py new file mode 100644 index 00000000..66447f87 --- /dev/null +++ b/test/python/JournalTest.py @@ -0,0 +1,25 @@ +# -*- 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 + + def test_(self): + pass + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(JournalTestCase) + +if __name__ == '__main__': + unittest.main() diff --git a/test/python/PostingTest.py b/test/python/PostingTest.py new file mode 100644 index 00000000..f191253e --- /dev/null +++ b/test/python/PostingTest.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +import unittest +import exceptions +import operator + +from ledger import * +from StringIO import * +from datetime import * + +class PostingTestCase(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def test_(self): + pass + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(PostingTestCase) + +if __name__ == '__main__': + unittest.main() diff --git a/test/python/TransactionTest.py b/test/python/TransactionTest.py new file mode 100644 index 00000000..66447f87 --- /dev/null +++ b/test/python/TransactionTest.py @@ -0,0 +1,25 @@ +# -*- 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 + + def test_(self): + pass + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(JournalTestCase) + +if __name__ == '__main__': + unittest.main() diff --git a/test/python/UnitTests.py b/test/python/UnitTests.py new file mode 100644 index 00000000..388e2229 --- /dev/null +++ b/test/python/UnitTests.py @@ -0,0 +1,12 @@ +from unittest import TextTestRunner, TestSuite + +import JournalTest +import TransactionTest +import PostingTest + +suites = [ + JournalTest.suite(), + TransactionTest.suite(), + PostingTest.suite() +] +TextTestRunner().run(TestSuite(suites)) diff --git a/tools/Makefile.am b/tools/Makefile.am index 5bb41cc0..fe0681e5 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -321,7 +321,7 @@ all_py_tests_sources = \ test/python/%.py: test/unit/%.cc test/convert.py $(PYTHON) $(srcdir)/test/convert.py $< $@ -test/python/UnitTests.py: $(all_py_tests_sources) +test/python/ConvertedTests.py: $(all_py_tests_sources) @echo "from unittest import TextTestRunner, TestSuite" > $@ @for file in $$(ls $(srcdir)/test/unit/*.cc); do \ base=$$(basename $$file); \ @@ -346,7 +346,7 @@ ESC_distdir=`echo "$(distdir)" | sed 's/\//\\\\\//g'` # jww (2007-05-10): This rule will not be triggered on systems that # define an EXEEXT. -PyUnitTests: test/PyUnitTests.py test/python/UnitTests.py +PyUnitTests: test/PyUnitTests.py test/python/ConvertedTests.py @cat $(srcdir)/test/PyUnitTests.py \ | sed "s/%python%/$(ESC_python)/" \ | sed "s/%srcdir%/$(ESC_srcdir)/g" \ -- 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 'test') 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 'test') 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 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 'test') 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 576aa90bb6b052d096948eaa61cd890286e62a38 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 21:21:25 -0600 Subject: Fixed an explicit path within a unit test --- test/baseline/dir-tag.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/baseline/dir-tag.test b/test/baseline/dir-tag.test index 496334a0..cf668f29 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: "/Users/johnw/Projects/ledger/test/baseline/dir-tag.test", line 8: Metadata check failed for (Happy: Summer): (value == "Valley") +Warning: "$sourcepath/test/baseline/dir-tag.test", line 8: Metadata check failed for (Happy: Summer): (value == "Valley") end test -- cgit v1.2.3 From a0c9ab08dccf65259474b5da97e4f5b092741779 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 22:20:20 -0600 Subject: Added more baseline testing, for valexprs and Python --- test/baseline/cmd-script.test | 9 +++++++++ test/baseline/cmd-script_2.test | 3 --- test/baseline/feat-value_py.test | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) delete mode 100644 test/baseline/cmd-script_2.test create mode 100644 test/baseline/feat-value_py.test (limited to 'test') diff --git a/test/baseline/cmd-script.test b/test/baseline/cmd-script.test index ed665dcc..12e9c868 100644 --- a/test/baseline/cmd-script.test +++ b/test/baseline/cmd-script.test @@ -1,3 +1,12 @@ test eval 'foo(w, u)=(z=w+u;z*2); (a=1 + 1; foo(10, 15))' 50 end test + +test eval 'foo(x, y, z)=print(x, y, z); bar(x)=x; foo(1, 2, 3); bar(3)' +123 +3 +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 diff --git a/test/baseline/cmd-script_2.test b/test/baseline/cmd-script_2.test deleted file mode 100644 index 51ea41fb..00000000 --- a/test/baseline/cmd-script_2.test +++ /dev/null @@ -1,3 +0,0 @@ -test eval 'total_expr=$100;amount_expr=$15;x=total_expr;x=x/5;x=amount_expr-x*5;x' -$-85 -end test diff --git a/test/baseline/feat-value_py.test b/test/baseline/feat-value_py.test new file mode 100644 index 00000000..5efe315d --- /dev/null +++ b/test/baseline/feat-value_py.test @@ -0,0 +1,23 @@ +python + def print_type(val): + print type(val), val + +eval print_type(true) +eval print_type([2010/08/10]) +eval print_type(10) +eval print_type($10.00) +eval print_type($10.00 + CAD 30) +eval print_type("Hello!") +eval print_type(/Hello!/) +;eval print_type((1, 2, 3)) + +test reg + True + 2010-08-10 + 10 + $10.00 + $10.00 +CAD 30 + Hello! + Hello! +end test -- 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 'test') 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 'test') 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 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 'test') 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 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 'test') 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 'test') 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 05725eab3fdbbf82b2c702ca8c1615dea9cc8a1b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 15:42:33 -0600 Subject: Look for valgrind in /opt/local/bin also --- test/fullcheck.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/fullcheck.sh b/test/fullcheck.sh index f3c20dd2..f8c671e0 100755 --- a/test/fullcheck.sh +++ b/test/fullcheck.sh @@ -1,7 +1,7 @@ #!/bin/sh VALGRIND='' -if [ -x /usr/bin/valgrind ]; then +if [ -x /usr/bin/valgrind -o -x /opt/local/bin/valgrind ]; then VALGRIND=valgrind fi -- 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 'test') 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 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 'test') 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 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 'test') 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 5a027166cc0d5d72f52911665f27915f6fc8b061 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 03:31:46 -0600 Subject: Added balance assertion test --- test/baseline/feat-balance_assert.test | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/baseline/feat-balance_assert.test (limited to 'test') diff --git a/test/baseline/feat-balance_assert.test b/test/baseline/feat-balance_assert.test new file mode 100644 index 00000000..a03cbb0e --- /dev/null +++ b/test/baseline/feat-balance_assert.test @@ -0,0 +1,13 @@ +2012-01-01 Opening Balance + Assets:Checking $100 + Equity + +2012-01-01 Reconciliation + [Assets:Checking] = $100 + +test balance + $100 Assets:Checking + $-100 Equity +-------------------- + 0 +end test -- cgit v1.2.3 From 04f884b14fd7cc396c9d442685df8489ae7e8a15 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 7 Mar 2012 05:38:28 -0600 Subject: Added test for value directives and annotations --- test/baseline/feat-value-expr.test | 103 +++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 test/baseline/feat-value-expr.test (limited to 'test') diff --git a/test/baseline/feat-value-expr.test b/test/baseline/feat-value-expr.test new file mode 100644 index 00000000..690febf9 --- /dev/null +++ b/test/baseline/feat-value-expr.test @@ -0,0 +1,103 @@ +;; A valuation function receives three arguments: +;; +;; 'source' A string identifying the commodity whose price +;; is being asked for (example: "EUR") +;; +;; 'date' The reference date the price should be relative. +;; +;; 'target' A string identifying the "target" commodity, or +;; the commodity the returned price should be in. +;; This argument is null if -V was used instead of -X. +;; +;; The valuation function should return an amount. If you've written your +;; function in Python, you can return something like Amount("$100"). If the +;; function returns an explicit value, that value is always used, regardless +;; of the commodity, the date, or the desired target commodity. + +define myfunc_seven(s, d, t) = 7 EUR + +;; In order to specific a fixed price, but still valuate that price into the +;; target commodity, use something like this: + +define myfunc_five(s, d, t) = market(5 EUR, d, t) + +;; The 'value' directive sets the valuation used for all commodities used in +;; the rest of the daat stream. This is the fallback, if nothing more +;; specific is found. + +value myfunc_seven + +;; You can set a specific valuation function on a per-commodity basis. +;; Instead of defining a function, you can also pass a lambda. + +commodity $ + value s, d, t -> 6 EUR + +;; Each account can also provide a default valuation function for any +;; commodities transferred to that account. + +account Expenses:Food5 + value myfunc_five + +;; The metadata field "Value", if found, overrides the valuation function on a +;; transaction-wide or per-posting basis. + += @XACT and Food + ; Value:: 8 EUR + (Equity) $1 + += @POST and Dining + (Expenses:Food9) $1 + ; Value:: 9 EUR + +;; Lastly, you can specify the valuation function/value for any specific +;; amount using the (( )) commodity annotation. + +2012-03-02 KFC + Expenses:Food2 $1 ((2 EUR)) + Assets:Cash2 + +2012-03-03 KFC + Expenses:Food3 $1 + ; Value:: 3 EUR + Assets:Cash3 + +2012-03-04 KFC + ; Value:: 4 EUR + Expenses:Food4 $1 + Assets:Cash4 + +2012-03-05 KFC + Expenses:Food5 $1 + Assets:Cash5 + +2012-03-06 KFC + Expenses:Food6 $1 + Assets:Cash6 + +2012-03-07 KFC + Expenses:Food7 1 CAD + Assets:Cas7 + +2012-03-08 XACT + Expenses:Food8 $1 + Assets:Cash8 + +2012-03-09 POST + Expenses:Dining9 $1 + Assets:Cash9 + +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 +end test -- 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 'test') 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 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 'test') 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 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 'test') 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 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 'test') 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 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 'test') 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 c813eafbd8aaad5d66c3eb8857393c9fa1c98db6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 03:57:30 -0600 Subject: Display file name when a regression test fails --- test/LedgerHarness.py | 4 +++- test/RegressTests.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/LedgerHarness.py b/test/LedgerHarness.py index 7b4dfa83..564a4d32 100755 --- a/test/LedgerHarness.py +++ b/test/LedgerHarness.py @@ -112,8 +112,10 @@ class LedgerHarness: sys.stdout.write(".") self.succeeded += 1 - def failure(self): + def failure(self, name=None): sys.stdout.write("E") + if name: + sys.stdout.write("[%s]" % name) self.failed += 1 def exit(self): diff --git a/test/RegressTests.py b/test/RegressTests.py index def202e4..da5d92ca 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -148,14 +148,14 @@ class RegressFile(object): if success: harness.success() else: - harness.failure() + harness.failure(os.path.basename(self.filename)) else: if success: print if test['exitcode']: self.notify_user("FAILURE in exit code (%d != %d) from %s:" % (test['exitcode'], p.returncode, self.filename), test) - harness.failure() + harness.failure(os.path.basename(self.filename)) def run_tests(self): test = self.read_test() -- 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 'test') 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 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 'test') 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 'test') 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 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 'test') 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 'test') 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 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 'test') 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 'test') 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 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 'test') 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 'test') 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 'test') 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 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 'test') 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 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 'test') 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 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 'test') 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 fbcbbeb987806137f054cf7bc81d8ab2be18112c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 14 Mar 2012 17:21:21 -0500 Subject: Added two regressions tests from Thierry Daucourt --- test/regress/4509F714.test | 25 +++++++++++++ test/regress/47C579B8.test | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 test/regress/4509F714.test create mode 100644 test/regress/47C579B8.test (limited to 'test') diff --git a/test/regress/4509F714.test b/test/regress/4509F714.test new file mode 100644 index 00000000..5c5985f2 --- /dev/null +++ b/test/regress/4509F714.test @@ -0,0 +1,25 @@ +P 2008/01/01 $ €1 + +2008/01/11 LIAT + Expenses:Travel:Airfare $40.00 + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto €240.38 + Liabilities:MasterCard + +test bal --exchange=€ + €280.38 Expenses:Travel + €40.00 Airfare + €240.38 Auto + €-280.38 Liabilities:MasterCard +-------------------- + 0 +end test + +test bal --exchange=€ --percent + 100.00% Expenses:Travel + 14.27% Airfare + 85.73% Auto + 100.00% Liabilities:MasterCard +end test diff --git a/test/regress/47C579B8.test b/test/regress/47C579B8.test new file mode 100644 index 00000000..f1f9579f --- /dev/null +++ b/test/regress/47C579B8.test @@ -0,0 +1,92 @@ +2008/01/11 LIAT + Expenses:Travel:Airfare 40,00 € + Liabilities:MasterCard + +2008/01/14 cheaptickets.com + Expenses:Travel:Airfare 182,19 € + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto 240,38 € + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare 238,80 € + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare 238,80 € + Liabilities:MasterCard + +2008/02/22 BUDGET RENT-A-CAR + Expenses:Travel:Auto 40,59 € + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare 1231,60 € + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare 1231,60 € + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare 155,86 € + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare 155,86 € + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare 437,21 € + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare 437,21 € + Liabilities:MasterCard + +2008/08/08 BCIS I-131 FILING FEE- + Expenses:Travel:Passport 170,00 € + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare 912,60 € + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare 912,60 € + Liabilities:MasterCard + +2008/09/22 AGNT FEE + Expenses:Travel:Airfare 70,00 € + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare 806,20 € + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare 806,20 € + Liabilities:MasterCard + +2008/09/22 LIAT 1974 LIMITED + Expenses:Travel:Airfare 418,34 € + Liabilities:MasterCard + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport 127,00 € + Assets:Checking + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport 127,00 € + Assets:Checking + +test --decimal-comma --percent balance + 100.00% Assets:Checking + 100.00% Expenses:Travel + 92.15% Airfare + 3.13% Auto + 4.72% Passport + 100.00% Liabilities:MasterCard +end test -- cgit v1.2.3 From d9fc09097d3ea290a4bdf0c9b46c8ead3e48043c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 14 Mar 2012 17:36:00 -0500 Subject: Added regression test for #438 --- test/regress/3FE26304.test | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test/regress/3FE26304.test (limited to 'test') diff --git a/test/regress/3FE26304.test b/test/regress/3FE26304.test new file mode 100644 index 00000000..7c5b6026 --- /dev/null +++ b/test/regress/3FE26304.test @@ -0,0 +1,74 @@ +N $ +P 2010/09/28 20:43:24 E $3.700 +P 2010/09/28 20:43:25 A $5.230 +P 2010/09/28 20:43:26 D $34.020 +P 2010/09/28 20:43:27 C $12.370 +P 2010/09/28 20:43:28 F $39.700 +P 2010/09/28 20:43:29 B $39.430 +P 2010/09/29 13:50:15 E $3.720 +P 2010/09/29 13:50:15 A $5.240 +P 2010/09/29 13:50:17 D $33.920 +P 2010/09/29 13:50:18 C $12.310 P 2010/09/29 13:50:18 F $39.670 +P 2010/09/29 13:50:19 B $39.830 + +2010/04/04 * Opening Balance + Assets:Sub1 100 A @ $0.01 + Equity:Opening Balances + +2010/04/04 * Opening Balance + Assets:Sub1 100 B @ $32.27 + Equity:Opening Balances + +2010/04/04 * Opening Balance + Assets:Sub1 100 C @ $11.30 + Equity:Opening Balances + +2010/04/04 * Opening Balance + Assets:Sub1 100 D @ $20.30 + Equity:Opening Balances + +2010/04/04 * Opening Balance + Assets:Sub1:Leftovers $6.79 + Equity:Opening Balances + +2010/04/04 * Opening Balance + Assets:Sub1 11 D + Equity:Opening Balances + +2010/04/04 * Opening Balance + Assets:Sub1 100 E @ $2.97 + Equity:Opening Balances + +2010/05/18=2010/05/21 * FOO + Assets:Sub1 200 F @ $27.190 + Expenses:Qux $29.95 + Assets:Sub2 + +2010/07/02 * BAR + Income:D -$169.65 + Assets:Sub2 $32.50 + Assets:Sub1 6 D @ $22.64 + Assets:Sub1:Leftovers + +test bal -X \$ sub1 + $18026.74 Assets:Sub1 + $8.10 Leftovers +-------------------- + $18026.74 +end test + +test reg -X \$ sub1 --now=2012/03/14 +10-Apr-04 Opening Balance Assets:Sub1 $1.00 $1.00 +10-Apr-04 Opening Balance Assets:Sub1 $3227.00 $3228.00 +10-Apr-04 Opening Balance Assets:Sub1 $1130.00 $4358.00 +10-Apr-04 Opening Balance Assets:Sub1 $2030.00 $6388.00 +10-Apr-04 Opening Balance Assets:Sub1:Leftovers $6.79 $6394.79 +10-Apr-04 Opening Balance Assets:Sub1 $223.30 $6618.09 +10-Apr-04 Opening Balance Assets:Sub1 $297.00 $6915.09 +10-May-18 FOO Assets:Sub1 $5438.00 $12353.09 +10-Jul-02 Commodities revalued $259.74 $12612.83 +10-Jul-02 BAR Assets:Sub1 $135.84 $12748.67 + Assets:Sub1:Leftovers $1.31 $12749.98 +10-Sep-29 Commodities revalued $5251.46 $18001.44 +12-Mar-14 Commodities revalued $25.30 $18026.74 +end test -- 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 'test') 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 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 'test') 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 'test') 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 dc91d4f1e6fc06ac5c4e50e3ee650844b7a77a2e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 16 Mar 2012 03:12:18 -0500 Subject: Added regression test for #584 --- test/regress/C9D593B3.test | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 test/regress/C9D593B3.test (limited to 'test') diff --git a/test/regress/C9D593B3.test b/test/regress/C9D593B3.test new file mode 100644 index 00000000..1cb73080 --- /dev/null +++ b/test/regress/C9D593B3.test @@ -0,0 +1,23 @@ +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 + +2012-03-16 KFC + Expenses:Food $20 + Assets:Cash + +test payees +KFC +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 'test') 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 'test') 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 'test') 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 'test') 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 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 'test') 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 2aa37bca36ffc04a48317aaaecf347d20682e44d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 17 Mar 2012 07:01:51 -0500 Subject: Added regression test for A8FCC765 --- test/regress/A8FCC765.dat | 2 ++ test/regress/A8FCC765.test | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 test/regress/A8FCC765.dat create mode 100644 test/regress/A8FCC765.test (limited to 'test') diff --git a/test/regress/A8FCC765.dat b/test/regress/A8FCC765.dat new file mode 100644 index 00000000..abc51a0a --- /dev/null +++ b/test/regress/A8FCC765.dat @@ -0,0 +1,2 @@ +P 2012-03-16 06:47:12 CAD $2.50 +P 2012-03-17 06:47:12 CAD $3.50 diff --git a/test/regress/A8FCC765.test b/test/regress/A8FCC765.test new file mode 100644 index 00000000..1adf6053 --- /dev/null +++ b/test/regress/A8FCC765.test @@ -0,0 +1,8 @@ +2012-03-17 KFC + Expenses:Food 20 CAD + Assets:Cash + +test pricedb --price-db test/regress/A8FCC765.dat +P 2012/03/16 06:47:12 CAD $2.5 +P 2012/03/17 06:47:12 CAD $3.5 +end test -- 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 'test') 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 430ad07db1e46c27d69cdb0fca6c9e98a503079c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 17 Mar 2012 07:02:31 -0500 Subject: Simplified a regression test --- test/regress/25A099C9.test | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'test') diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index df4fef1c..a8a93832 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -1,43 +1,43 @@ -test -f $sourcepath/src/amount.h reg -> 20 +test -f src/amount.h reg -> 20 __ERROR__ -While parsing file "$sourcepath/src/amount.h", line 2: +While parsing file "src/amount.h", line 2: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 33: +While parsing file "src/amount.h", line 33: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 37: +While parsing file "src/amount.h", line 37: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 66: +While parsing file "src/amount.h", line 66: Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 69: +While parsing file "src/amount.h", line 69: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 83: +While parsing file "src/amount.h", line 83: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 93: +While parsing file "src/amount.h", line 93: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 99: +While parsing file "src/amount.h", line 99: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 121: +While parsing file "src/amount.h", line 121: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 132: +While parsing file "src/amount.h", line 132: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 693: +While parsing file "src/amount.h", line 693: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 723: +While parsing file "src/amount.h", line 723: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 731: +While parsing file "src/amount.h", line 731: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 734: +While parsing file "src/amount.h", line 734: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 740: +While parsing file "src/amount.h", line 740: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 746: +While parsing file "src/amount.h", line 746: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 752: +While parsing file "src/amount.h", line 752: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 758: +While parsing file "src/amount.h", line 758: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 765: +While parsing file "src/amount.h", line 765: Error: Invalid date/time: line std::istream& -While parsing file "$sourcepath/src/amount.h", line 771: +While parsing file "src/amount.h", line 771: Error: Unexpected whitespace at beginning of line end test -- cgit v1.2.3 From 06934e8dd68a0dea1f113911b01dc6b7ed406330 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 17 Mar 2012 16:22:03 -0500 Subject: libgmalloc is no longer available on OS X Lion --- test/fullcheck.sh | 18 +++++++++--------- tools/times.sh | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'test') diff --git a/test/fullcheck.sh b/test/fullcheck.sh index f8c671e0..f607f2d9 100755 --- a/test/fullcheck.sh +++ b/test/fullcheck.sh @@ -5,14 +5,14 @@ if [ -x /usr/bin/valgrind -o -x /opt/local/bin/valgrind ]; then VALGRIND=valgrind fi -export MallocGuardEdges=1 -export MallocScribble=1 -export MallocPreScribble=1 -export MallocCheckHeapStart=100 -export MallocCheckHeapEach=100 -export DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib -export MALLOC_PROTECT_BEFORE=1 -export MALLOC_FILL_SPACE=1 -export MALLOC_STRICT_SIZE=1 +#export MallocGuardEdges=1 +#export MallocScribble=1 +#export MallocPreScribble=1 +#export MallocCheckHeapStart=100 +#export MallocCheckHeapEach=100 +#export DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib +#export MALLOC_PROTECT_BEFORE=1 +#export MALLOC_FILL_SPACE=1 +#export MALLOC_STRICT_SIZE=1 exec $VALGRIND $@ diff --git a/tools/times.sh b/tools/times.sh index 444da993..d15431bc 100755 --- a/tools/times.sh +++ b/tools/times.sh @@ -2,5 +2,5 @@ time test/RegressTests.py ./ledger test/regress time test/RegressTests.py ./ledger test/regress --verify -time test/RegressTests.py ./ledger test/regress --gmalloc -time test/RegressTests.py ./ledger test/regress --verify --gmalloc +#time test/RegressTests.py ./ledger test/regress --gmalloc +#time test/RegressTests.py ./ledger test/regress --verify --gmalloc -- cgit v1.2.3 From 1240ce24b3dd59bb2f48a396043b4115cc49c6e1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 17 Mar 2012 16:47:33 -0500 Subject: Passing --verify to regression tests uses valgrind --- test/LedgerHarness.py | 7 +++++++ test/fullcheck.sh | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/LedgerHarness.py b/test/LedgerHarness.py index 564a4d32..3477e720 100755 --- a/test/LedgerHarness.py +++ b/test/LedgerHarness.py @@ -81,6 +81,13 @@ class LedgerHarness: command = re.sub('\$ledger', '%s%s %s' % \ (self.ledger, insert, '--args-only'), command) + valgrind = '/usr/bin/valgrind' + if not os.path.isfile(valgrind): + valgrind = '/opt/local/bin/valgrind' + + if os.path.isfile(valgrind) and '--verify' in insert: + command = valgrind + ' -q ' + command + return Popen(command, shell=True, close_fds=True, env=env, stdin=PIPE, stdout=PIPE, stderr=PIPE) diff --git a/test/fullcheck.sh b/test/fullcheck.sh index f607f2d9..db63921d 100755 --- a/test/fullcheck.sh +++ b/test/fullcheck.sh @@ -2,7 +2,7 @@ VALGRIND='' if [ -x /usr/bin/valgrind -o -x /opt/local/bin/valgrind ]; then - VALGRIND=valgrind + VALGRIND="valgrind -q" fi #export MallocGuardEdges=1 -- 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 'test') 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 'test') 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 'test') 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 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 'test') 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 'test') 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 95ef2e3b65c8bcb9dbcd902d338fb429b42b8689 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 03:51:19 -0500 Subject: Added regression test --- test/regress/1E192DF6.test | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/regress/1E192DF6.test (limited to 'test') diff --git a/test/regress/1E192DF6.test b/test/regress/1E192DF6.test new file mode 100644 index 00000000..7d0f8182 --- /dev/null +++ b/test/regress/1E192DF6.test @@ -0,0 +1,57 @@ +; -*- ledger -*- +D 1000,00 PLN +N $ +N h +N PLN +N zł +C 1,00 PLN = 1,00 + +2010-05-19 * ŁUKASZ STELMACH + Assets:Checking:Konto24 GBP 200,00 GBP @ 4,8799 PLN ; fikimiki + Assets:Checking:Konto<30 -975,98 PLN + +2010-05-19 * ŁUKASZ STELMACH + Assets:Checking:Konto24 GBP 200,00 GBP @ 4,8799 PLN ; fikimiki + Assets:Checking:Konto<30 -975,98 PLN + +2010-05-19 * ŁUKASZ STELMACH + Assets:Checking:Konto<30 -975,98 PLN @ 0,204922 GBP + Assets:Checking:Konto24 GBP 200,00 GBP ; fikimiki + +2010-05-19 * ŁUKASZ STELMACH + Assets:Checking:Konto<30 -975,98 PLN @ 0,204922 GBP + Assets:Checking:Konto24 GBP 200,00 GBP ; fikimiki + +2010-05-19 * ŁUKASZ STELMACH + Assets:Checking:Konto24 GBP 200,00 GBP @ 4,8799 PLN ; fikimiki + Assets:Checking:Konto<30 -975,98 PLN + +2010-05-19 * ŁUKASZ STELMACH + Assets:Checking:Konto24 GBP 200,00 GBP @ 4,8799 PLN ; fikimiki + Assets:Checking:Konto<30 -975,98 PLN + +test reg +10-May-19 ŁUKASZ STELMACH As:Checkin:Konto24 GBP 200,00 GBP 200,00 GBP + Asse:Checking:Konto<30 -975.98 -975.98 + 200,00 GBP +10-May-19 ŁUKASZ STELMACH As:Checkin:Konto24 GBP 200,00 GBP -975.98 + 400,00 GBP + Asse:Checking:Konto<30 -975.98 -1951.96 + 400,00 GBP +10-May-19 ŁUKASZ STELMACH Asse:Checking:Konto<30 -975.98 -2927.94 + 400,00 GBP + As:Checkin:Konto24 GBP 200,00 GBP -2927.94 + 600,00 GBP +10-May-19 ŁUKASZ STELMACH Asse:Checking:Konto<30 -975.98 -3903.92 + 600,00 GBP + As:Checkin:Konto24 GBP 200,00 GBP -3903.92 + 800,00 GBP +10-May-19 ŁUKASZ STELMACH As:Checkin:Konto24 GBP 200,00 GBP -3903.92 + 1000,00 GBP + Asse:Checking:Konto<30 -975.98 -4879.9 + 1000,00 GBP +10-May-19 ŁUKASZ STELMACH As:Checkin:Konto24 GBP 200,00 GBP -4879.9 + 1200,00 GBP + Asse:Checking:Konto<30 -975.98 -5855.88 + 1200,00 GBP +end test -- 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 'test') 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 078c8380072ec7bc07d1e50afb34da041f446781 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 19 Mar 2012 22:14:20 -0500 Subject: Added regression test for A560FDAD --- test/regress/A560FDAD.test | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/regress/A560FDAD.test (limited to 'test') diff --git a/test/regress/A560FDAD.test b/test/regress/A560FDAD.test new file mode 100644 index 00000000..053f5aaf --- /dev/null +++ b/test/regress/A560FDAD.test @@ -0,0 +1,36 @@ +2012-01-01 * Opening balance + Assets:Current 17.43 EUR + Assets:Investments 200 "LU02" @ 24.77 EUR + Assets:Investments 58 "LU02" @ 24.79900855 EUR + Equity:Opening balance + +2012-01-01 * Opening balance + Assets:Pension 785.44 GBP + Assets:Pension 97.0017 "H2" @ 5.342999720204 GBP + Assets:Pension 4.3441 "H1" @ 5.289999915108 GBP + Equity:Opening balance + +2012-01-01 * Opening balance: misc + Assets:Piggy bank 3.51 GBP + Equity:Opening balance + +2012-01-01 * Opening balance + Assets:Rewards 9836 AAdvantage + Equity:Opening balance + +2012-01-03 * Receivable + Assets:Current + Assets:Receivable -161.06 EUR + Assets:Receivable -9.99 GBP @@ 11.65 EUR + +2012-01-27 * Test + Income:Test -2759.50 GBP + Income:Test -110.76 GBP + Assets:Foo 345.57 GBP + Expenses:Test 16.47 GBP + Expenses:Test 6.33 GBP + Expenses:Test 261.39 GBP + Assets:Current + +test reg -X EUR -H +end test -- 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 'test') 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 'test') 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 c69701cf81766b05d494eff372e67cb7983d6bcd Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Thu, 22 Mar 2012 18:27:04 +0000 Subject: Add some regression tests --- test/regress/1A546C4D.test | 13 +++++++++++++ test/regress/6188B0EC.test | 10 ++++++++++ test/regress/640D3205.test | 20 ++++++++++++++++++++ test/regress/89233B6D-a.dat | 4 ++++ test/regress/89233B6D-b.dat | 4 ++++ test/regress/89233B6D.test | 13 +++++++++++++ test/regress/F32DBEF1.test | 19 +++++++++++++++++++ test/regress/FDFBA165.test | 23 +++++++++++++++++++++++ 8 files changed, 106 insertions(+) create mode 100644 test/regress/1A546C4D.test create mode 100644 test/regress/6188B0EC.test create mode 100644 test/regress/640D3205.test create mode 100644 test/regress/89233B6D-a.dat create mode 100644 test/regress/89233B6D-b.dat create mode 100644 test/regress/89233B6D.test create mode 100644 test/regress/F32DBEF1.test create mode 100644 test/regress/FDFBA165.test (limited to 'test') diff --git a/test/regress/1A546C4D.test b/test/regress/1A546C4D.test new file mode 100644 index 00000000..e72fc553 --- /dev/null +++ b/test/regress/1A546C4D.test @@ -0,0 +1,13 @@ +2012/02/22 * Testing invalid amount + Assets:Cash $1,00.00 + Equity:Opening Balances + +test bal -> 1 +__ERROR__ +While parsing file "$sourcepath/test/regress/1A546C4D.test", line 2: +While parsing posting: + Assets:Cash $1,00.00 + ^^^^^^^^ +Error: Incorrect use of thousand-mark comma +end test + diff --git a/test/regress/6188B0EC.test b/test/regress/6188B0EC.test new file mode 100644 index 00000000..475988e6 --- /dev/null +++ b/test/regress/6188B0EC.test @@ -0,0 +1,10 @@ +; Test for: No error message if the parser cannot find an included file + +!include 6188B0EC-does-not-exist.dat + +test bal -> 1 +__ERROR__ +While parsing file "$sourcepath/test/regress/6188B0EC.test", line 3: +Error: File to include was not found: "$sourcepath/test/regress/6188B0EC-does-not-exist.dat" +end test + diff --git a/test/regress/640D3205.test b/test/regress/640D3205.test new file mode 100644 index 00000000..f43b850f --- /dev/null +++ b/test/regress/640D3205.test @@ -0,0 +1,20 @@ +; Test for: "print" command filters out the "balance assertions" + +2008/12/31 * Interest + Assets:Brokerage $800.00 + Income:Somewhere + +2008/12/31 * Interest + Assets:Brokerage $200.00 = $1,000.00 + Income:Somewhere + +test print +2008/12/31 * Interest + Assets:Brokerage $800.00 + Income:Somewhere + +2008/12/31 * Interest + Assets:Brokerage $200.00 = $1000.00 + Income:Somewhere +end test + diff --git a/test/regress/89233B6D-a.dat b/test/regress/89233B6D-a.dat new file mode 100644 index 00000000..01d00e9a --- /dev/null +++ b/test/regress/89233B6D-a.dat @@ -0,0 +1,4 @@ +1994/01/02 * Salary + Asssets:Bank:Checking 200.00 + Income:Salary -200.00 + diff --git a/test/regress/89233B6D-b.dat b/test/regress/89233B6D-b.dat new file mode 100644 index 00000000..f54dc66f --- /dev/null +++ b/test/regress/89233B6D-b.dat @@ -0,0 +1,4 @@ +1994/01/02 * Rent + Expenses:Rent 100.00 + Asssets:Bank:Checking -100.00 + diff --git a/test/regress/89233B6D.test b/test/regress/89233B6D.test new file mode 100644 index 00000000..e6bd38f5 --- /dev/null +++ b/test/regress/89233B6D.test @@ -0,0 +1,13 @@ +!apply account A +!include 89233B6D-a.dat +!end + +!apply account B +!include 89233B6D-b.dat +!end + +test reg "^A:" +94-Jan-02 Salary A:Assset:Bank:Checking 200 200 + A:Income:Salary -200 0 +end test + diff --git a/test/regress/F32DBEF1.test b/test/regress/F32DBEF1.test new file mode 100644 index 00000000..1465b8c3 --- /dev/null +++ b/test/regress/F32DBEF1.test @@ -0,0 +1,19 @@ +2012-01-01 * Opening Balances + Assets:Cash 100.00 EUR + Equity:Opening balances -100.00 EUR + +2012-01-02 * Buy AAA + Assets:Investments 1 AAA @ 10.00 EUR + Assets:Cash -10.00 EUR + +2012-01-03 * Sell AAA + Assets:Investments -1 AAA @ 10.00 EUR + Assets:Cash 10.00 EUR + + +test equity +2012/01/03 Opening Balances + Assets:Cash 100.00 EUR + Equity:Opening balances +end test + diff --git a/test/regress/FDFBA165.test b/test/regress/FDFBA165.test new file mode 100644 index 00000000..55074bed --- /dev/null +++ b/test/regress/FDFBA165.test @@ -0,0 +1,23 @@ +; Test for: automated transactions didn't show up in the balance report + += Income:Clients: + (Liabilities:Taxes:VAT) ((1,00 / 1,19) * 0,19) + +2009/07/27 * Invoice + Assets:Bank:Checking €1.190,00 + Income:Clients:ACME_Inc + +test --decimal-comma bal + €1.190,00 Assets:Bank:Checking + €-1.190,00 Income:Clients:ACME_Inc + €-190,00 Liabilities:Taxes:VAT +-------------------- + €-190,00 +end test + +test --decimal-comma reg +09-Jul-27 Invoice Assets:Bank:Checking €1.190,00 €1.190,00 + Incom:Clients:ACME_Inc €-1.190,00 0 + (Liabilitie:Taxes:VAT) €-190,00 €-190,00 +end test + -- cgit v1.2.3 From 10e312cb78144f4d32cde8a5588880be36c927dc Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Thu, 22 Mar 2012 19:41:06 +0000 Subject: Add more regression tests --- test/regress/10D19C11.test | 37 ++++++++++++++++++++++++++++++ test/regress/178501DC.test | 21 +++++++++++++++++ test/regress/3AAB00ED.test | 23 +++++++++++++++++++ test/regress/53BCED29.test | 29 +++++++++++++++++++++++ test/regress/82763D86.test | 35 ++++++++++++++++++++++++++++ test/regress/AFAFB804.test | 57 ++++++++++++++++++++++++++++++++++++++++++++++ test/regress/C927CFFE.test | 43 ++++++++++++++++++++++++++++++++++ test/regress/D9C8EB08.test | 16 +++++++++++++ test/regress/F524E251.test | 37 ++++++++++++++++++++++++++++++ 9 files changed, 298 insertions(+) create mode 100644 test/regress/10D19C11.test create mode 100644 test/regress/178501DC.test create mode 100644 test/regress/3AAB00ED.test create mode 100644 test/regress/53BCED29.test create mode 100644 test/regress/82763D86.test create mode 100644 test/regress/AFAFB804.test create mode 100644 test/regress/C927CFFE.test create mode 100644 test/regress/D9C8EB08.test create mode 100644 test/regress/F524E251.test (limited to 'test') diff --git a/test/regress/10D19C11.test b/test/regress/10D19C11.test new file mode 100644 index 00000000..be0469ad --- /dev/null +++ b/test/regress/10D19C11.test @@ -0,0 +1,37 @@ +; Test for: ./ledger -f doc/sample.dat -E bal liab' shows the Assets account + += /^Expenses:Books/ + (Liabilities:Taxes) -0.10 + +~ Monthly + Assets:Bank:Checking $500.00 + Income:Salary + +2004/05/01 * Checking balance + Assets:Bank:Checking $1,000.00 + Equity:Opening Balances + +2004/05/01 * Investment balance + Assets:Brokerage 50 AAPL @ $30.00 + Equity:Opening Balances + +2004/05/14 * Pay day + Assets:Bank:Checking $500.00 + Income:Salary + +2004/05/27 Book Store + Expenses:Books $20.00 + Liabilities:MasterCard + +2004/05/27 (100) Credit card company + Liabilities:MasterCard $20.00 + Assets:Bank:Checking + +test -E bal liabilities + $-2.00 Liabilities + 0 MasterCard + $-2.00 Taxes +-------------------- + $-2.00 +end test + diff --git a/test/regress/178501DC.test b/test/regress/178501DC.test new file mode 100644 index 00000000..b5319ac8 --- /dev/null +++ b/test/regress/178501DC.test @@ -0,0 +1,21 @@ +; Test for: The bal report does not honor -r (ledger bal simon +; would show all accounts, rather than just simon and the related +; account). + +2011/10/26 trader joe's + simon $-50 + alice $-50 + expenses:food:groceries + +test bal -r simon + $-50 alice + $100 expenses:food:groceries +-------------------- + $50 +end test + +test reg -r simon +11-Oct-26 trader joe's alice $-50 $-50 + expense:food:groceries $100 $50 +end test + diff --git a/test/regress/3AAB00ED.test b/test/regress/3AAB00ED.test new file mode 100644 index 00000000..217917b3 --- /dev/null +++ b/test/regress/3AAB00ED.test @@ -0,0 +1,23 @@ +; Test for: --sort d not working with -p + +2009-01-01 Opening Balances + Assets:Checking 100.00 EUR + Equity:Opening Balances + +2009-03-01 Test + Expenses:Phone 10.00 EUR + Assets:Checking + +2009-02-01 Test + Expenses:Phone 10.00 EUR + Assets:Checking + +test --sort d -p "until 2010" reg +09-Jan-01 Opening Balances Assets:Checking 100.00 EUR 100.00 EUR + Equit:Opening Balances -100.00 EUR 0 +09-Feb-01 Test Expenses:Phone 10.00 EUR 10.00 EUR + Assets:Checking -10.00 EUR 0 +09-Mar-01 Test Expenses:Phone 10.00 EUR 10.00 EUR + Assets:Checking -10.00 EUR 0 +end test + diff --git a/test/regress/53BCED29.test b/test/regress/53BCED29.test new file mode 100644 index 00000000..77fd39f3 --- /dev/null +++ b/test/regress/53BCED29.test @@ -0,0 +1,29 @@ +D $1,000.00 + +; payroll taxes += /^Payroll/ + Liabilities:Taxes:CFICA 0.062 + Liabilities:Taxes:CMED 0.0145 + $account:EFICA -0.062 + $account:EMED -0.0145 + +; Hourly rates for each employee, as commodity prices. +P 2010/01/01 EONE $15.00 + +; Payroll transactions +2010/05/18 Payroll from May 2nd to May 15th for Employee1 + Assets:Checking 20 EONE + Payroll:Employee1 + +test bal -V + $300.00 Assets:Checking + $-22.95 Liabilities:Taxes + $-18.60 CFICA + $-4.35 CMED + $-277.05 Payroll:Employee1 + $18.60 EFICA + $4.35 EMED +-------------------- + 0 +end test + diff --git a/test/regress/82763D86.test b/test/regress/82763D86.test new file mode 100644 index 00000000..e580077d --- /dev/null +++ b/test/regress/82763D86.test @@ -0,0 +1,35 @@ +; Test for: 'ledger -f doc/sample.dat reg -s -n liab' elides too much +; It collapses the account down to "", even though there was +; only one account! + += /^Expenses:Books/ + (Liabilities:Taxes) -0.10 + +~ Monthly + Assets:Bank:Checking $500.00 + Income:Salary + +2004/05/01 * Checking balance + Assets:Bank:Checking $1,000.00 + Equity:Opening Balances + +2004/05/01 * Investment balance + Assets:Brokerage 50 AAPL @ $30.00 + Equity:Opening Balances + +2004/05/14 * Pay day + Assets:Bank:Checking $500.00 + Income:Salary + +2004/05/27 Book Store + Expenses:Books $20.00 + Liabilities:MasterCard + +2004/05/27 (100) Credit card company + Liabilities:MasterCard $20.00 + Assets:Bank:Checking + +test -s reg liabilities +04-May-27 - 04-May-27 (Liabilities:Taxes) $-2.00 $-2.00 +end test + diff --git a/test/regress/AFAFB804.test b/test/regress/AFAFB804.test new file mode 100644 index 00000000..472540fb --- /dev/null +++ b/test/regress/AFAFB804.test @@ -0,0 +1,57 @@ +; Test for: ledger should allow sorting by multiple criteria, like: +; -S date,payee + +2010-02-09 * Z + A $10 + B + +2010-02-09 * Y + B $10 + C + +2010-02-09 * X + C $10 + D + +2010-02-10 * Z + A $15 + B + +2010-02-10 * Y + B $15 + C + +2010-02-10 * X + C $15 + D + +test reg -S date,payee +10-Feb-09 X C $10 $10 + D $-10 0 +10-Feb-09 Y B $10 $10 + C $-10 0 +10-Feb-09 Z A $10 $10 + B $-10 0 +10-Feb-10 X C $15 $15 + D $-15 0 +10-Feb-10 Y B $15 $15 + C $-15 0 +10-Feb-10 Z A $15 $15 + B $-15 0 +end test + +test reg -S payee,date +10-Feb-09 X C $10 $10 + D $-10 0 +10-Feb-10 X C $15 $15 + D $-15 0 +10-Feb-09 Y B $10 $10 + C $-10 0 +10-Feb-10 Y B $15 $15 + C $-15 0 +10-Feb-09 Z A $10 $10 + B $-10 0 +10-Feb-10 Z A $15 $15 + B $-15 0 +end test + diff --git a/test/regress/C927CFFE.test b/test/regress/C927CFFE.test new file mode 100644 index 00000000..d455b480 --- /dev/null +++ b/test/regress/C927CFFE.test @@ -0,0 +1,43 @@ + +2010/02/09 * Test 1 + A $10 + B + +2010/02/10 * Test 2 + B $10 + C + +2010/02/11 * Test 3 + C $10 + D + +test reg +test -l "date>=[2010/02/10]" reg +10-Feb-10 Test 2 B $10 $10 + C $-10 0 +10-Feb-11 Test 3 C $10 $10 + D $-10 0 +end test + +test -l "date<=[2010/02/10]" reg +10-Feb-09 Test 1 A $10 $10 + B $-10 0 +10-Feb-10 Test 2 B $10 $10 + C $-10 0 +end test + +test -l "date==[2010/02/10]" reg +10-Feb-10 Test 2 B $10 $10 + C $-10 0 +end test + +test -l "date>[2010/02/10]" reg +10-Feb-11 Test 3 C $10 $10 + D $-10 0 +end test + +test -l "date<[2010/02/10]" reg +10-Feb-09 Test 1 A $10 $10 + B $-10 0 +end test + diff --git a/test/regress/D9C8EB08.test b/test/regress/D9C8EB08.test new file mode 100644 index 00000000..fa02431b --- /dev/null +++ b/test/regress/D9C8EB08.test @@ -0,0 +1,16 @@ +; Test for: Using ! erroneously in a data file causes a segfault + +! Assets:Cash + +2008/01/01 January + Expenses:Books $10.00 + Assets:Cash + +!end + +test bal -> 1 +__ERROR__ +While parsing file "$FILE", line 9: +Error: 'end' or 'end apply' found, but no enclosing 'apply' directive +end test + diff --git a/test/regress/F524E251.test b/test/regress/F524E251.test new file mode 100644 index 00000000..d2d2f049 --- /dev/null +++ b/test/regress/F524E251.test @@ -0,0 +1,37 @@ +; Test for: ledger -f doc/sample.dat -n reg' shows $0.00 on first post + += /^Expenses:Books/ + (Liabilities:Taxes) -0.10 + +~ Monthly + Assets:Bank:Checking $500.00 + Income:Salary + +2004/05/01 * Checking balance + Assets:Bank:Checking $1,000.00 + Equity:Opening Balances + +2004/05/01 * Investment balance + Assets:Brokerage 50 AAPL @ $30.00 + Equity:Opening Balances + +2004/05/14 * Pay day + Assets:Bank:Checking $500.00 + Income:Salary + +2004/05/27 Book Store + Expenses:Books $20.00 + Liabilities:MasterCard + +2004/05/27 (100) Credit card company + Liabilities:MasterCard $20.00 + Assets:Bank:Checking + +test -n reg +04-May-01 Investment balance $-1,500.00 + 50 AAPL $-1,500.00 + 50 AAPL +04-May-27 Book Store $-2.00 $-1,502.00 + 50 AAPL +end test + -- cgit v1.2.3 From 62943c6005683ad0d6952bc8d3c6a4ef6f95cfe8 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Thu, 22 Mar 2012 21:48:50 +0000 Subject: Add more test cases --- test/baseline/opt-file.test | 12 ++++++++++++ test/baseline/opt-file1.dat | 4 ++++ test/baseline/opt-file2.dat | 4 ++++ test/regress/15A80F68.test | 15 +++++++++++++++ test/regress/DDB54BB8.test | 18 ++++++++++++++++++ test/regress/E2E479BC.test | 17 +++++++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 test/baseline/opt-file.test create mode 100644 test/baseline/opt-file1.dat create mode 100644 test/baseline/opt-file2.dat create mode 100644 test/regress/15A80F68.test create mode 100644 test/regress/DDB54BB8.test create mode 100644 test/regress/E2E479BC.test (limited to 'test') diff --git a/test/baseline/opt-file.test b/test/baseline/opt-file.test new file mode 100644 index 00000000..e01d929d --- /dev/null +++ b/test/baseline/opt-file.test @@ -0,0 +1,12 @@ +test -f opt-file-does-not-exist.dat bal -> 1 +__ERROR__ +Error: Cannot read journal file "opt-file-does-not-exist.dat" +end test + +test -f test/baseline/opt-file1.dat -f test/baseline/opt-file2.dat bal + 10 A + -10 C +-------------------- + 0 +end test + diff --git a/test/baseline/opt-file1.dat b/test/baseline/opt-file1.dat new file mode 100644 index 00000000..394882cd --- /dev/null +++ b/test/baseline/opt-file1.dat @@ -0,0 +1,4 @@ +2012-03-22 * Test 1 + A 10.00 + B + diff --git a/test/baseline/opt-file2.dat b/test/baseline/opt-file2.dat new file mode 100644 index 00000000..569993f8 --- /dev/null +++ b/test/baseline/opt-file2.dat @@ -0,0 +1,4 @@ +2012-03-22 * Test 2 + B 10.00 + C + diff --git a/test/regress/15A80F68.test b/test/regress/15A80F68.test new file mode 100644 index 00000000..0b29b82d --- /dev/null +++ b/test/regress/15A80F68.test @@ -0,0 +1,15 @@ +; Test for: Confusing error message with ledger v3 with invalid input + +2008/03/03 + A (2 FOO @ 10.00 EUR) = 20.00 EUR + B + +test bal -> 1 +__ERROR__ +While parsing file "$FILE", line 4: +While parsing posting: + A (2 FOO @ 10.00 EUR) = 20.00 EUR + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: Invalid char '@' +end test + diff --git a/test/regress/DDB54BB8.test b/test/regress/DDB54BB8.test new file mode 100644 index 00000000..7d72043c --- /dev/null +++ b/test/regress/DDB54BB8.test @@ -0,0 +1,18 @@ +~ Monthly + Aufwand:Bargeld 0,30€ + Aktiva:Bank:Girokonto -0,40€ + +test bal -> 1 +__ERROR__ +While parsing file "$FILE", line 3: +Unbalanced remainder is: + -0,10€ +Amount to balance against: + 0,30€ +While parsing periodic transaction: +> ~ Monthly +> Aufwand:Bargeld 0,30€ +> Aktiva:Bank:Girokonto -0,40€ +Error: Transaction does not balance +end test + diff --git a/test/regress/E2E479BC.test b/test/regress/E2E479BC.test new file mode 100644 index 00000000..8216028a --- /dev/null +++ b/test/regress/E2E479BC.test @@ -0,0 +1,17 @@ +; Test for: ledger used to show multiple "Income:Unknown" in this +; case in the past, which it shouldn't. + +2009/01/01 Sample + Expenses:Alpha 10 A + Expenses:Beta 10 B + Expenses:Gamma 10 C + Income:Unknown + +test print +2009/01/01 Sample + Expenses:Alpha 10 A + Expenses:Beta 10 B + Expenses:Gamma 10 C + Income:Unknown +end test + -- cgit v1.2.3 From 6ff2257f30391f2da15c3ef8815180e2c3f03ccb Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Thu, 22 Mar 2012 21:53:53 +0000 Subject: Simply regression tests by using $FILE --- test/regress/1A546C4D.test | 2 +- test/regress/5D92A5EB.test | 2 +- test/regress/6188B0EC.test | 2 +- test/regress/8EAF77C0.test | 2 +- test/regress/D51BFF74.test | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/regress/1A546C4D.test b/test/regress/1A546C4D.test index e72fc553..97adc9de 100644 --- a/test/regress/1A546C4D.test +++ b/test/regress/1A546C4D.test @@ -4,7 +4,7 @@ test bal -> 1 __ERROR__ -While parsing file "$sourcepath/test/regress/1A546C4D.test", line 2: +While parsing file "$FILE", line 2: While parsing posting: Assets:Cash $1,00.00 ^^^^^^^^ diff --git a/test/regress/5D92A5EB.test b/test/regress/5D92A5EB.test index 1d3a06bb..1bdd7256 100644 --- a/test/regress/5D92A5EB.test +++ b/test/regress/5D92A5EB.test @@ -14,7 +14,7 @@ test -J reg checking -> 1 __ERROR__ -While parsing file "$sourcepath/test/regress/5D92A5EB.test", line 13: +While parsing file "$FILE", line 13: While parsing periodic transaction: > ~ Monthly from 2010/7/1 > Expenses:Auto:Gas $100.00 diff --git a/test/regress/6188B0EC.test b/test/regress/6188B0EC.test index 475988e6..b2aec910 100644 --- a/test/regress/6188B0EC.test +++ b/test/regress/6188B0EC.test @@ -4,7 +4,7 @@ test bal -> 1 __ERROR__ -While parsing file "$sourcepath/test/regress/6188B0EC.test", line 3: +While parsing file "$FILE", line 3: Error: File to include was not found: "$sourcepath/test/regress/6188B0EC-does-not-exist.dat" end test diff --git a/test/regress/8EAF77C0.test b/test/regress/8EAF77C0.test index fd73197e..f0a2829c 100644 --- a/test/regress/8EAF77C0.test +++ b/test/regress/8EAF77C0.test @@ -12,6 +12,6 @@ test reg -> 1 __ERROR__ -While parsing file "$sourcepath/test/regress/8EAF77C0.test", line 5: +While parsing file "$FILE", line 5: Error: Unexpected whitespace at beginning of line end test diff --git a/test/regress/D51BFF74.test b/test/regress/D51BFF74.test index dc472215..a13af897 100644 --- a/test/regress/D51BFF74.test +++ b/test/regress/D51BFF74.test @@ -16,7 +16,7 @@ test reg -> 1 __ERROR__ -While parsing file "$sourcepath/test/regress/D51BFF74.test", line 6: +While parsing file "$FILE", line 6: While parsing posting: Expenses:Food $- 20 ^^^^^ -- cgit v1.2.3 From 2bbcbbf78de55cacf3a764177626a3fb020ee1fd Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 22 Mar 2012 17:34:43 -0500 Subject: Renamed F32DBEF1.test to CEECC0B0.test --- test/regress/CEECC0B0.test | 19 +++++++++++++++++++ test/regress/F32DBEF1.test | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 test/regress/CEECC0B0.test delete mode 100644 test/regress/F32DBEF1.test (limited to 'test') diff --git a/test/regress/CEECC0B0.test b/test/regress/CEECC0B0.test new file mode 100644 index 00000000..1465b8c3 --- /dev/null +++ b/test/regress/CEECC0B0.test @@ -0,0 +1,19 @@ +2012-01-01 * Opening Balances + Assets:Cash 100.00 EUR + Equity:Opening balances -100.00 EUR + +2012-01-02 * Buy AAA + Assets:Investments 1 AAA @ 10.00 EUR + Assets:Cash -10.00 EUR + +2012-01-03 * Sell AAA + Assets:Investments -1 AAA @ 10.00 EUR + Assets:Cash 10.00 EUR + + +test equity +2012/01/03 Opening Balances + Assets:Cash 100.00 EUR + Equity:Opening balances +end test + diff --git a/test/regress/F32DBEF1.test b/test/regress/F32DBEF1.test deleted file mode 100644 index 1465b8c3..00000000 --- a/test/regress/F32DBEF1.test +++ /dev/null @@ -1,19 +0,0 @@ -2012-01-01 * Opening Balances - Assets:Cash 100.00 EUR - Equity:Opening balances -100.00 EUR - -2012-01-02 * Buy AAA - Assets:Investments 1 AAA @ 10.00 EUR - Assets:Cash -10.00 EUR - -2012-01-03 * Sell AAA - Assets:Investments -1 AAA @ 10.00 EUR - Assets:Cash 10.00 EUR - - -test equity -2012/01/03 Opening Balances - Assets:Cash 100.00 EUR - Equity:Opening balances -end test - -- 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 'test') 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 5cffbf16e94cffc1b62ef80f6fb4ef8c1356db41 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Fri, 23 Mar 2012 08:58:33 +0000 Subject: Add more baseline tests --- test/baseline/cmd-balance.test | 85 ++++++++++++++++++++++++++++++++++++++++++ test/baseline/cmd-cleared.test | 36 ++++++++++++++++++ test/baseline/cmd-csv.test | 53 ++++++++++++++++++++++++++ test/baseline/cmd-emacs.test | 18 +++++++++ test/baseline/cmd-pricedb.test | 39 +++++++++++++++++++ test/baseline/cmd-prices.test | 39 +++++++++++++++++++ 6 files changed, 270 insertions(+) create mode 100644 test/baseline/cmd-balance.test create mode 100644 test/baseline/cmd-cleared.test create mode 100644 test/baseline/cmd-csv.test create mode 100644 test/baseline/cmd-emacs.test create mode 100644 test/baseline/cmd-pricedb.test create mode 100644 test/baseline/cmd-prices.test (limited to 'test') diff --git a/test/baseline/cmd-balance.test b/test/baseline/cmd-balance.test new file mode 100644 index 00000000..064a046d --- /dev/null +++ b/test/baseline/cmd-balance.test @@ -0,0 +1,85 @@ + +2012-01-01 * Opening balances + Assets:A 10.00 + Equity:Opening balances -10.00 + +2012-01-02 * A to B + Assets:A -10.00 + Assets:B 10.00 + +2012-01-03 * B partly to C + Assets:B -5.00 + Assets:C 5.00 + +2012-01-04 * Borrow + Assets:A 10.00 + Liabilities:A -10.00 + +2012-01-05 * Return A + Assets:A -10.00 + Liabilities:A 10.00 + +test bal + 10 Assets + 5 B + 5 C + -10 Equity:Opening balances +-------------------- + 0 +end test + +test bal -n + 10 Assets + -10 Equity +-------------------- + 0 +end test + +test bal -n -E + 10 Assets + -10 Equity + 0 Liabilities +-------------------- + 0 +end test + +test bal -E + 10 Assets + 0 A + 5 B + 5 C + -10 Equity:Opening balances + 0 Liabilities:A +-------------------- + 0 +end test + +test bal --flat + 5 Assets:B + 5 Assets:C + -10 Equity:Opening balances +-------------------- + 0 +end test + +test bal --flat -E + 0 Assets:A + 5 Assets:B + 5 Assets:C + -10 Equity:Opening balances + 0 Liabilities:A +-------------------- + 0 +end test + +test bal -E --flat --no-total + 0 Assets:A + 5 Assets:B + 5 Assets:C + -10 Equity:Opening balances + 0 Liabilities:A +end test + +test bal -n --flat +end test + diff --git a/test/baseline/cmd-cleared.test b/test/baseline/cmd-cleared.test new file mode 100644 index 00000000..501d207f --- /dev/null +++ b/test/baseline/cmd-cleared.test @@ -0,0 +1,36 @@ +2012-02-23 * Test 1 + A 10.00 + B + +2012-02-24 Test 1 + C 15.00 + D + +; leave E/F uncleared +2012-02-25 Test 1 + E 20.00 + F + +; have a cleared posting last for C +2012-02-26 * Test 1 + C 30.00 + G + +; have an uncleared posting last for A +2012-02-27 Test 1 + A 40.00 + H + +test cleared + 50 10 12-Feb-23 A + -10 -10 12-Feb-23 B + 45 30 12-Feb-26 C + -15 0 D + 20 0 E + -20 0 F + -30 -30 12-Feb-26 G + -40 0 H +---------------- ---------------- --------- + 0 0 +end test + diff --git a/test/baseline/cmd-csv.test b/test/baseline/cmd-csv.test new file mode 100644 index 00000000..110e3d58 --- /dev/null +++ b/test/baseline/cmd-csv.test @@ -0,0 +1,53 @@ + +2012-01-01 * Opening balances + Assets:A 10.00 + Equity:Opening balances -10.00 + +2012-01-02 * Cleared posting + Assets:A -10.00 + Assets:B 10.00 + +2012-01-03 Uncleared posting + Assets:B -5.00 + Assets:C 5.00 + +2012-01-04=2012-01-05 * aux date + Assets:A 10.00 + Liabilities:A -10.00 + +2012-01-05 * (100) Code + Assets:A -10.00 + Liabilities:A 10.00 + +2012-01-06 * (100) Specify commodity + Assets:A $-10.00 + Liabilities:A $10.00 + +2012-01-07 * (100) Specify commodity + Assets:A -10.00 EUR + Liabilities:A 10.00 EUR + +2012-01-08 * (100) With note + ;This is an xact note + Assets:A -10.00 EUR + Liabilities:A 10.00 EUR + +test csv +"2012/01/01","","Opening balances","Assets:A","","10","*","" +"2012/01/01","","Opening balances","Equity:Opening balances","","-10","*","" +"2012/01/02","","Cleared posting","Assets:A","","-10","*","" +"2012/01/02","","Cleared posting","Assets:B","","10","*","" +"2012/01/03","","Uncleared posting","Assets:B","","-5","","" +"2012/01/03","","Uncleared posting","Assets:C","","5","","" +"2012/01/04","","aux date","Assets:A","","10","*","" +"2012/01/04","","aux date","Liabilities:A","","-10","*","" +"2012/01/05","100","Code","Assets:A","","-10","*","" +"2012/01/05","100","Code","Liabilities:A","","10","*","" +"2012/01/06","100","Specify commodity","Assets:A","$","-10","*","" +"2012/01/06","100","Specify commodity","Liabilities:A","$","10","*","" +"2012/01/07","100","Specify commodity","Assets:A","EUR","-10","*","" +"2012/01/07","100","Specify commodity","Liabilities:A","EUR","10","*","" +"2012/01/08","100","With note","Assets:A","EUR","-10","*","This is an xact note" +"2012/01/08","100","With note","Liabilities:A","EUR","10","*","This is an xact note" +end test + diff --git a/test/baseline/cmd-emacs.test b/test/baseline/cmd-emacs.test new file mode 100644 index 00000000..0d4addad --- /dev/null +++ b/test/baseline/cmd-emacs.test @@ -0,0 +1,18 @@ + +2012-03-23 * Test + A 10.00 + B + +2012-03-23 * (100) Code + A 10.00 EUR + B + +test emacs +(("$sourcepath/test/baseline/cmd-emacs.test" 2 (20331 48384 0) nil "Test" + (3 "A" "10" t) + (4 "B" "-10" t)) + ("$sourcepath/test/baseline/cmd-emacs.test" 6 (20331 48384 0) "100" "Code" + (7 "A" "10.00 EUR" t) + (8 "B" "-10.00 EUR" t))) +end test + diff --git a/test/baseline/cmd-pricedb.test b/test/baseline/cmd-pricedb.test new file mode 100644 index 00000000..4a220054 --- /dev/null +++ b/test/baseline/cmd-pricedb.test @@ -0,0 +1,39 @@ +2012-01-01 * Opening balance + A 10.00 GBP + B + +2012-01-02 * Test + A 10.00 GBP @@ 12.00 EUR + B + +2012-01-03 * Test + B 12.00 EUR @@ 15.80 USD + C + +2012-01-04 * Test + C 15.80 USD @ 0.63 GBP + D + +test pricedb +P 2012/01/02 00:00:00 GBP 1.20 EUR +P 2012/01/03 00:00:00 EUR 1.3166666667 USD +P 2012/01/04 00:00:00 USD 0.63 GBP +end test + +test pricedb EUR +P 2012/01/03 00:00:00 EUR 1.3166666667 USD +end test + +test pricedb GBP +P 2012/01/02 00:00:00 GBP 1.20 EUR +end test + +test pricedb USD +P 2012/01/04 00:00:00 USD 0.63 GBP +end test + +test pricedb U +P 2012/01/03 00:00:00 EUR 1.3166666667 USD +P 2012/01/04 00:00:00 USD 0.63 GBP +end test + diff --git a/test/baseline/cmd-prices.test b/test/baseline/cmd-prices.test new file mode 100644 index 00000000..ee2b4ba1 --- /dev/null +++ b/test/baseline/cmd-prices.test @@ -0,0 +1,39 @@ +2012-01-01 * Opening balance + A 10.00 GBP + B + +2012-01-02 * Test + A 10.00 GBP @@ 12.00 EUR + B + +2012-01-03 * Test + B 12.00 EUR @@ 15.80 USD + C + +2012-01-04 * Test + C 15.80 USD @ 0.63 GBP + D + +test prices +2012/01/02 GBP 1.20 EUR +2012/01/03 EUR 1.3166666667 USD +2012/01/04 USD 0.63 GBP +end test + +test prices EUR +2012/01/03 EUR 1.3166666667 USD +end test + +test prices USD +2012/01/04 USD 0.63 GBP +end test + +test prices GBP +2012/01/02 GBP 1.20 EUR +end test + +test prices U +2012/01/03 EUR 1.3166666667 USD +2012/01/04 USD 0.63 GBP +end test + -- cgit v1.2.3 From b5664ce2efdd698448f93dbe1248e7f8aeefe3fa Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 23 Mar 2012 04:02:01 -0500 Subject: Allow use of $FILE in stdout checks --- test/RegressTests.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/RegressTests.py b/test/RegressTests.py index a22e35bf..72667311 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -40,6 +40,7 @@ class RegressFile(object): def transform_line(self, line): line = re.sub('\$sourcepath', harness.sourcepath, line) + line = re.sub('\$FILE', self.filename, line) return line def read_test(self): @@ -130,9 +131,7 @@ class RegressFile(object): printed = False index = 0 if test['error'] is not None: - for line in unified_diff([re.sub('\$FILE', self.filename, line) - for line in test['error']], - harness.readlines(p.stderr)): + for line in unified_diff(test['error'], harness.readlines(p.stderr)): index += 1 if index < 3: continue -- cgit v1.2.3 From c4db812173d51113bdfcc98c186854c638b870af Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 23 Mar 2012 04:04:37 -0500 Subject: Fixed cmd-emacs.test --- test/baseline/cmd-emacs.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/baseline/cmd-emacs.test b/test/baseline/cmd-emacs.test index 0d4addad..beb4a6b5 100644 --- a/test/baseline/cmd-emacs.test +++ b/test/baseline/cmd-emacs.test @@ -7,11 +7,11 @@ A 10.00 EUR B -test emacs -(("$sourcepath/test/baseline/cmd-emacs.test" 2 (20331 48384 0) nil "Test" +test emacs --now=2012/03/23 +(("$FILE" 2 (20332 848 0) nil "Test" (3 "A" "10" t) (4 "B" "-10" t)) - ("$sourcepath/test/baseline/cmd-emacs.test" 6 (20331 48384 0) "100" "Code" + ("$FILE" 6 (20332 848 0) "100" "Code" (7 "A" "10.00 EUR" t) (8 "B" "-10.00 EUR" t))) end test -- cgit v1.2.3 From 902d09fd16166e97a66cd73e5f091a9593554b22 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Fri, 23 Mar 2012 09:32:39 +0000 Subject: Add test case for bug #379 --- test/baseline/opt-equity.test | 59 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/baseline/opt-equity.test b/test/baseline/opt-equity.test index 90f4743a..ba9c202c 100644 --- a/test/baseline/opt-equity.test +++ b/test/baseline/opt-equity.test @@ -1,9 +1,58 @@ -2007/02/02 RD VMMXX - Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 - Income:Dividends:Vanguard:VMMXX $-0.35 +D 1000.00 GBP + +2011-03-04 Buy shares + Assets:Broker 2 AAA @ 0.90 GBP + Assets:Bank + +2011-03-05 Buy shares + Assets:Broker 2 AAA @ 1.00 GBP + Assets:Bank + +test equity +2011/03/05 Opening Balances + Assets:Bank -3.80 GBP + Assets:Broker 4 AAA + Equity:Opening Balances 3.80 GBP + Equity:Opening Balances -4 AAA +end test test equity assets -2007/02/02 Opening Balances - Assets:Investments:Vanguard:VMMXX 0.350 VMMXX +2011/03/05 Opening Balances + Assets:Bank -3.80 GBP + Assets:Broker 4 AAA + Equity:Opening Balances 3.80 GBP + Equity:Opening Balances -4 AAA +end test + +test equity assets:bank +2011/03/05 Opening Balances + Assets:Bank -3.80 GBP + Equity:Opening Balances +end test + +test equity assets:broker +2011/03/05 Opening Balances + Assets:Broker 4 AAA Equity:Opening Balances end test + +test equity --lot-prices +2011/03/05 Opening Balances + 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} +end test + +test equity --lots +2011/03/05 Opening Balances + 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] +end test + -- cgit v1.2.3 From db20ad8f292695fcdab5836d9955bb9443d28cf0 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Fri, 23 Mar 2012 09:54:18 +0000 Subject: Add more baseline tests --- test/baseline/cmd-accounts.test | 38 ++++++++++++++++++++++++++++++++++ test/baseline/cmd-commodities.test | 30 +++++++++++++++++++++++++++ test/baseline/cmd-payees.test | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 test/baseline/cmd-accounts.test create mode 100644 test/baseline/cmd-commodities.test create mode 100644 test/baseline/cmd-payees.test (limited to 'test') diff --git a/test/baseline/cmd-accounts.test b/test/baseline/cmd-accounts.test new file mode 100644 index 00000000..be6365fd --- /dev/null +++ b/test/baseline/cmd-accounts.test @@ -0,0 +1,38 @@ +2011-01-01 * Opening balance + Assets:Bank 10.00 GBP + Equity:Opening balance + +2012-01-02 * List XXX before AAA to test sorting + Assets:XXX 5.00 GBP + Assets:Bank + +2012-01-03 * List AAA after XXX to test sorting + Assets:AAA 3.00 GBP + Assets:Bank + +2012-01-03 * Account name with UTF-8 + Assets:♚ 3.00 GBP + Assets:Testing123ÕßDone + +test accounts +Assets:Bank +Equity:Opening balance +Assets:XXX +Assets:AAA +Assets:♚ +Assets:Testing123ÕßDone +end test + +test accounts assets:a +Assets:AAA +end test + +test accounts b +Assets:Bank +Equity:Opening balance +end test + +test accounts ß +Assets:Testing123ÕßDone +end test + diff --git a/test/baseline/cmd-commodities.test b/test/baseline/cmd-commodities.test new file mode 100644 index 00000000..0ce6f7a0 --- /dev/null +++ b/test/baseline/cmd-commodities.test @@ -0,0 +1,30 @@ +2011-01-01 * Opening balance + Assets:Bank 10.00 GBP + Equity:Opening balance + +2011-03-04 * Buy AAA + Assets:Broker 2 AAA @ 0.90 GBP + Assets:Bank -1.80 GBP + +2011-03-05 * Buy AA2 + Assets:Broker 2 "AA2" @ 1.00 GBP + Assets:Bank + +2011-03-06 * Get Miles&More airmiles + Assets:Rewards 1000 "M&M" + Income:Rewards + +test commodities +GBP +AAA +"AA2" +"M&M" +end test + +test commodities Assets:Rewards +"M&M" +end test + +test commodities no:such:account +end test + diff --git a/test/baseline/cmd-payees.test b/test/baseline/cmd-payees.test new file mode 100644 index 00000000..af8a44bc --- /dev/null +++ b/test/baseline/cmd-payees.test @@ -0,0 +1,42 @@ +2011-03-01 * Z + A 10 + B + +2011-03-02 * A + C 10 + D + +2011-03-03 * 9 + B 10 + E + +2011-03-04 * B + B 10 + E + +2011-03-05 * 1 + B 10 + E + +test payees +1 +9 +A +B +Z +end test + +test payees a +Z +end test + +test payees no:such:account +end test + +test payees "^B$" +1 +9 +B +Z +end test + -- cgit v1.2.3 From 38813bc635e587f0979ef142511c5e7655fd88f1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 23 Mar 2012 15:55:50 -0500 Subject: Fixed output order in opt-equity.test --- test/baseline/opt-equity.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/baseline/opt-equity.test b/test/baseline/opt-equity.test index ba9c202c..511333cc 100644 --- a/test/baseline/opt-equity.test +++ b/test/baseline/opt-equity.test @@ -12,16 +12,16 @@ test equity 2011/03/05 Opening Balances Assets:Bank -3.80 GBP Assets:Broker 4 AAA - Equity:Opening Balances 3.80 GBP Equity:Opening Balances -4 AAA + Equity:Opening Balances 3.80 GBP end test test equity assets 2011/03/05 Opening Balances Assets:Bank -3.80 GBP Assets:Broker 4 AAA - Equity:Opening Balances 3.80 GBP Equity:Opening Balances -4 AAA + Equity:Opening Balances 3.80 GBP end test test equity assets:bank -- 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 'test') 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 3a61769098e4e90e6e55454ed0be861d69b0cd97 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Sun, 25 Mar 2012 11:38:26 +0100 Subject: Improve baseline/cmd-payees.test --- test/baseline/cmd-payees.test | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test') diff --git a/test/baseline/cmd-payees.test b/test/baseline/cmd-payees.test index af8a44bc..64d6bcf5 100644 --- a/test/baseline/cmd-payees.test +++ b/test/baseline/cmd-payees.test @@ -18,8 +18,14 @@ B 10 E +2011-03-06 * 2 + ; Payee: 3 + E 10 + F + test payees 1 +3 9 A B -- cgit v1.2.3 From 8962d9c904d5630b508ca2b10f2bf66e7f32a30b Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Sun, 25 Mar 2012 14:11:59 +0100 Subject: Add more baseline tests --- test/baseline/cmd-convert.test | 35 +++++++++++++++++++++++ test/baseline/cmd-convert1.dat | 3 ++ test/baseline/cmd-convert2.dat | 2 ++ test/baseline/cmd-convert3.dat | 2 ++ test/baseline/cmd-convert4.dat | 2 ++ test/baseline/cmd-echo.test | 12 ++++++++ test/baseline/cmd-entry.test | 43 ++++++++++++++++++++++++++++ test/baseline/cmd-org.test | 28 +++++++++++++++++++ test/baseline/cmd-pricemap.test | 36 ++++++++++++++++++++++++ test/baseline/cmd-select.test | 62 +++++++++++++++++++++++++++++++++++++++++ test/baseline/cmd-source.test | 26 +++++++++++++++++ test/baseline/cmd-stats.test | 31 +++++++++++++++++++++ test/baseline/cmd-xact.test | 43 ++++++++++++++++++++++++++++ 13 files changed, 325 insertions(+) create mode 100644 test/baseline/cmd-convert.test create mode 100644 test/baseline/cmd-convert1.dat create mode 100644 test/baseline/cmd-convert2.dat create mode 100644 test/baseline/cmd-convert3.dat create mode 100644 test/baseline/cmd-convert4.dat create mode 100644 test/baseline/cmd-echo.test create mode 100644 test/baseline/cmd-entry.test create mode 100644 test/baseline/cmd-org.test create mode 100644 test/baseline/cmd-pricemap.test create mode 100644 test/baseline/cmd-select.test create mode 100644 test/baseline/cmd-source.test create mode 100644 test/baseline/cmd-stats.test create mode 100644 test/baseline/cmd-xact.test (limited to 'test') diff --git a/test/baseline/cmd-convert.test b/test/baseline/cmd-convert.test new file mode 100644 index 00000000..181165df --- /dev/null +++ b/test/baseline/cmd-convert.test @@ -0,0 +1,35 @@ + +test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert1.dat +2011/12/12=2011/12/13 * (100) Test ;test + Expenses:Unknown $10 + Equity:Unknown $-10 = $20 + +2011/12/12=2011/12/12 * ; + Expenses:Unknown $10 + Equity:Unknown +end test + +test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert2.dat +2011/01/01 * test + Expenses:Unknown 20.00 EUR + Equity:Unknown +end test + +test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert3.dat -> 1 +__ERROR__ +While parsing file "test/baseline/cmd-convert3.dat", line 1: +While parsing CSV line: + 01/01/2011,, + +Error: No quantity specified for amount +end test + +test -f /dev/null convert test/baseline/cmd-convert4.dat -> 1 +__ERROR__ +While parsing file "test/baseline/cmd-convert4.dat", line 1: +While parsing CSV line: + bogus,$10, + +Error: Invalid date: bogus +end test + diff --git a/test/baseline/cmd-convert1.dat b/test/baseline/cmd-convert1.dat new file mode 100644 index 00000000..542a19e3 --- /dev/null +++ b/test/baseline/cmd-convert1.dat @@ -0,0 +1,3 @@ +date,posted,code,payee,amount,total,note, +12/12/2011,12/13/2011,100,Test,$10,$20,test, +12/12/2011,12/12/2011,,,$10,, diff --git a/test/baseline/cmd-convert2.dat b/test/baseline/cmd-convert2.dat new file mode 100644 index 00000000..190095c3 --- /dev/null +++ b/test/baseline/cmd-convert2.dat @@ -0,0 +1,2 @@ +date,amount,desc, +01/01/2011,20.00 EUR,test, diff --git a/test/baseline/cmd-convert3.dat b/test/baseline/cmd-convert3.dat new file mode 100644 index 00000000..7c31d986 --- /dev/null +++ b/test/baseline/cmd-convert3.dat @@ -0,0 +1,2 @@ +date,amount, +01/01/2011,, diff --git a/test/baseline/cmd-convert4.dat b/test/baseline/cmd-convert4.dat new file mode 100644 index 00000000..644f6806 --- /dev/null +++ b/test/baseline/cmd-convert4.dat @@ -0,0 +1,2 @@ +date,amount, +bogus,$10, diff --git a/test/baseline/cmd-echo.test b/test/baseline/cmd-echo.test new file mode 100644 index 00000000..af3d06ec --- /dev/null +++ b/test/baseline/cmd-echo.test @@ -0,0 +1,12 @@ +test echo foo +foo +end test + +test echo "foo bar" +foo bar +end test + +test echo "foo\nbar" +foo\nbar +end test + diff --git a/test/baseline/cmd-entry.test b/test/baseline/cmd-entry.test new file mode 100644 index 00000000..0ecfa5eb --- /dev/null +++ b/test/baseline/cmd-entry.test @@ -0,0 +1,43 @@ +2012-03-23 * Test 1 + A $10.00 + B + +2012-03-24 * Test 2 + ; Payee: Test 3 + C 20.00 EUR + D + +2012-03-25 * Test 4 + E 30.00 GBP + F + +test entry "Test 1" +2012/03/25 Test 1 + A $10.00 + B +end test + +test entry "Test 2" +2012/03/25 Test 2 + C 20.00 EUR + D +end test + +; I think this output is wrong, see bug #737 +test entry "Test 3" +2012/03/25 Test 4 + E 30.00 GBP + F +end test + +test entry "Test 4" +2012/03/25 Test 4 + E 30.00 GBP + F +end test + +test entry no:such:account -> 1 +__ERROR__ +Error: No accounts, and no past transaction matching 'no:such:account' +end test + diff --git a/test/baseline/cmd-org.test b/test/baseline/cmd-org.test new file mode 100644 index 00000000..12a380f5 --- /dev/null +++ b/test/baseline/cmd-org.test @@ -0,0 +1,28 @@ +2012-03-24 * Test 2 + ; Payee: Test 3 + C 20.00 EUR + D + +2012-03-25 * (99) Test 4 + E 30.00 GBP + F + +2012-03-26 * (test) Test 5 + G 1 AAA @ $10.00 + H + +test org +|Date|Code|Payee|X|Account|Amount|Total|Note| +|-| +|||<20>|||||<20>| +|12-Mar-24||Test 3|*|C|20.00 EUR|20.00 EUR| Payee: Test 3 +|||Test 3|*|D|-20.00 EUR|0.00 EUR| Payee: Test 3 +|12-Mar-25|99|Test 4|*|E|30.00 GBP|30.00 GBP| +||||*|F|-30.00 GBP|0.00 GBP| +|12-Mar-26|test|Test 5|*|G|1 AAA|0.00 GBP| +|||||||1 AAA| +||||*|H|$-10|0.00 GBP| +|||||||$-10| +|||||||1 AAA| +end test + diff --git a/test/baseline/cmd-pricemap.test b/test/baseline/cmd-pricemap.test new file mode 100644 index 00000000..6fbaa2fe --- /dev/null +++ b/test/baseline/cmd-pricemap.test @@ -0,0 +1,36 @@ +P 2012-03-25 EUR 0.83 GBP +P 2012-03-25 EUR 1.32 $ +P 2012-03-25 USD 0.75 EUR +P 2012-03-25 AAA $10.00 + +2012-03-23 * Test 1 + C 20.00 EUR @@ 16.71 GBP + D + +2012-03-24 * Test 2 + E 30.00 GBP + F + +2012-03-25 * Test 3 + G 1 AAA @ $10.00 + H + +test pricemap +graph G { +0[label=""]; +1[label="s"]; +2[label="%"]; +3[label="m"]; +4[label="h"]; +5[label="GBP"]; +6[label="EUR"]; +7[label="$"]; +8[label="USD"]; +9[label="AAA"]; +6--5 ; +6--7 ; +8--6 ; +9--7 ; +} +end test + diff --git a/test/baseline/cmd-select.test b/test/baseline/cmd-select.test new file mode 100644 index 00000000..c8ce7008 --- /dev/null +++ b/test/baseline/cmd-select.test @@ -0,0 +1,62 @@ +2012-02-28 * Test 1 + E 20.00 EUR + F + +2012-02-29 * Test 2 + Test 10.01 EUR + F + +2012-03-24 Test 3 + C 30.00 EUR + D + +2012-03-25 (test) Test 4 + ; Payee: Test 5 + E 40.00 GBP + F + +test select "date, account, amount" from posts +12-Feb-28 E  20.00 EUR +12-Feb-28 F  -20.00 EUR +12-Feb-29 Test  10.01 EUR +12-Feb-29 F  -10.01 EUR +12-Mar-24 C  30.00 EUR +12-Mar-24 D  -30.00 EUR +12-Mar-25 E  40.00 GBP +12-Mar-25 F  -40.00 GBP +end test + +test select "date, account, amount from posts where account =~ /^e/" +12-Feb-28 E  20.00 EUR +12-Mar-25 E  40.00 GBP +end test + +test select "date, account, amount from posts where account =~ /e/" +12-Feb-28 E  20.00 EUR +12-Feb-29 Test  10.01 EUR +12-Mar-25 E  40.00 GBP +end test + +; leave out "from posts" since it is the default +test select "date, account, amount where account =~ /e/" +12-Feb-28 E  20.00 EUR +12-Feb-29 Test  10.01 EUR +12-Mar-25 E  40.00 GBP +end test + +test select "date, payee, amount from posts where account =~ /e/ and commodity =~ /GBP/" +12-Mar-25 Test 5 40.00 GBP +end test + +test select "date, payee, amount * 2 from posts where account =~ /e/ and commodity =~ /GBP/" +12-Mar-25 Test 5 80.00 GBP +end test + +test select "date, code, amount from posts where account =~ /e/ and commodity =~ /GBP/" +12-Mar-25 test 40.00 GBP +end test + +test select "date, code * 2, amount from posts where account =~ /e/ and commodity =~ /GBP/" +12-Mar-25 testtest 40.00 GBP +end test + diff --git a/test/baseline/cmd-source.test b/test/baseline/cmd-source.test new file mode 100644 index 00000000..e78ae6fd --- /dev/null +++ b/test/baseline/cmd-source.test @@ -0,0 +1,26 @@ +2012-02-28 * Test + E 30.00 EUR + F + G + +2012-03-24 Test + C 30.00 EUR + D + C + +2012/03/xx + E 30.00 EUR + F + +test source -> 3 +__ERROR__ +While parsing file "$FILE", line 4: +Error: Only one posting with null amount allowed per transaction +While parsing file "$FILE", line 9: +Error: Only one posting with null amount allowed per transaction +While parsing file "$FILE", line 11: +While parsing transaction: +> 2012/03/xx +Error: Invalid date: 2012/03/xx +end test + diff --git a/test/baseline/cmd-stats.test b/test/baseline/cmd-stats.test new file mode 100644 index 00000000..ac8e1383 --- /dev/null +++ b/test/baseline/cmd-stats.test @@ -0,0 +1,31 @@ + +2012-02-28 * Test + E 30.00 EUR + F + +2012-02-29 * Test + E 30.00 EUR + F + +2012-03-24 Test + A 30.00 EUR + B + +test stats --now "2012-03-31" +Time period: 12-Feb-28 to 12-Mar-24 (25 days) + + Files these postings came from: + $sourcepath/test/baseline/cmd-stats.test + + Unique payees: 1 + Unique accounts: 4 + + Number of postings: 6 (0.24 per day) + Uncleared postings: 2 + + Days since last post: 7 + Posts in last 7 days: 2 + Posts in last 30 days: 2 + Posts seen this month: 2 +end test + diff --git a/test/baseline/cmd-xact.test b/test/baseline/cmd-xact.test new file mode 100644 index 00000000..f473c3dd --- /dev/null +++ b/test/baseline/cmd-xact.test @@ -0,0 +1,43 @@ +2012-03-23 * Test 1 + A $10.00 + B + +2012-03-24 * Test 2 + ; Payee: Test 3 + C 20.00 EUR + D + +2012-03-25 * Test 4 + E 30.00 GBP + F + +test xact "Test 1" +2012/03/25 Test 1 + A $10.00 + B +end test + +test xact "Test 2" +2012/03/25 Test 2 + C 20.00 EUR + D +end test + +; I think this output is wrong, see bug #737 +test xact "Test 3" +2012/03/25 Test 4 + E 30.00 GBP + F +end test + +test xact "Test 4" +2012/03/25 Test 4 + E 30.00 GBP + F +end test + +test xact no:such:account -> 1 +__ERROR__ +Error: No accounts, and no past transaction matching 'no:such:account' +end test + -- cgit v1.2.3 From d9baad45fac94190cf410629c216628e18cc5d75 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Sun, 25 Mar 2012 14:54:50 +0100 Subject: Add more baseline tests --- test/baseline/cmd-budget.test | 65 +++++++++++++++++++++++++++++++++++++++++ test/baseline/cmd-register.test | 40 +++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 test/baseline/cmd-budget.test create mode 100644 test/baseline/cmd-register.test (limited to 'test') diff --git a/test/baseline/cmd-budget.test b/test/baseline/cmd-budget.test new file mode 100644 index 00000000..91d5a901 --- /dev/null +++ b/test/baseline/cmd-budget.test @@ -0,0 +1,65 @@ +~ Monthly + Expenses:Phone 10.00 GBP + Expenses:Rent 550.00 GBP + Assets + +2012-01-10 * Phone expense on holidays + Expenses:Phone 12.00 EUR @@ 10.00 GBP + Assets:Cash -10.00 GBP + +2012-01-31 * Rent expense + Expenses:Rent 550.00 GBP + Assets:Cash -550.00 GBP + +2012-02-28 * Phone expense + Expenses:Phone 20.00 GBP + Assets:Cash -20.00 GBP + +2012-02-29 * Rent expense + Expenses:Rent 530.00 GBP + Assets:Cash -530.00 GBP + +2012-03-10 * Phone expense + Expenses:Phone 15.00 GBP + Assets:Cash -15.00 GBP + +2012-03-31 * Rent expense + Expenses:Rent 570.00 GBP + Assets:Cash -570.00 GBP + +test budget -X GBP -p "in january 2012" + -560.00 GBP -560.00 GBP 0 100% Assets + 560.00 GBP 560.00 GBP 0 100% Expenses + 10.00 GBP 10.00 GBP 0 100% Phone + 550.00 GBP 550.00 GBP 0 100% Rent +------------ ------------ ------------ ----- + 0 0 0 0 +end test + +test budget -X GBP -p "in feb 2012" + -550.00 GBP -560.00 GBP 10.00 GBP 98% Assets + 550.00 GBP 560.00 GBP -10.00 GBP 98% Expenses + 20.00 GBP 10.00 GBP 10.00 GBP 200% Phone + 530.00 GBP 550.00 GBP -20.00 GBP 96% Rent +------------ ------------ ------------ ----- + 0 0 0 0 +end test + +test budget -X GBP -p "in march 2012" + -585.00 GBP -560.00 GBP -25.00 GBP 104% Assets + 585.00 GBP 560.00 GBP 25.00 GBP 104% Expenses + 15.00 GBP 10.00 GBP 5.00 GBP 150% Phone + 570.00 GBP 550.00 GBP 20.00 GBP 104% Rent +------------ ------------ ------------ ----- + 0 0 0 0 +end test + +test budget -X GBP --now "2012-03-31" +-1695.00 GBP -1680.00 GBP -15.00 GBP 101% Assets + 1695.00 GBP 1680.00 GBP 15.00 GBP 101% Expenses + 45.00 GBP 30.00 GBP 15.00 GBP 150% Phone + 1650.00 GBP 1650.00 GBP 0 100% Rent +------------ ------------ ------------ ----- + 0 0 0 0 +end test + diff --git a/test/baseline/cmd-register.test b/test/baseline/cmd-register.test new file mode 100644 index 00000000..afb78fb0 --- /dev/null +++ b/test/baseline/cmd-register.test @@ -0,0 +1,40 @@ +2012-01-10 * Phone expense on holidays + Expenses:Phone 12.00 EUR @@ 10.00 GBP + Assets:Cash -10.00 GBP + +2012-01-31 * Rent expense + Expenses:Rent 550.00 GBP + Assets:Cash -550.00 GBP + +2012-02-01 * Buy AAA + Assets:Investment 1 AAA @ 10.00 GBP + Assets:Cash -10.00 GBP + +test reg +12-Jan-10 Phone expense on ho.. Expenses:Phone 12.00 EUR 12.00 EUR + Assets:Cash -10.00 GBP 12.00 EUR + -10.00 GBP +12-Jan-31 Rent expense Expenses:Rent 550.00 GBP 12.00 EUR + 540.00 GBP + Assets:Cash -550.00 GBP 12.00 EUR + -10.00 GBP +12-Feb-01 Buy AAA Assets:Investment 1 AAA 1 AAA + 12.00 EUR + -10.00 GBP + Assets:Cash -10.00 GBP 1 AAA + 12.00 EUR + -20.00 GBP +end test + +test r :inve +12-Feb-01 Buy AAA Assets:Investment 1 AAA 1 AAA +end test + +test reg :inve +12-Feb-01 Buy AAA Assets:Investment 1 AAA 1 AAA +end test + +test register :inve +12-Feb-01 Buy AAA Assets:Investment 1 AAA 1 AAA +end test + -- cgit v1.2.3 From 4d0daf77d7aef535bc206154344a1061b6064ec6 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Sun, 25 Mar 2012 17:39:49 +0100 Subject: Add more baseline tests --- test/baseline/cmd-source.test | 46 ++++++- test/baseline/opt-auto-match.dat | 4 + test/baseline/opt-auto-match.test | 30 +++++ test/baseline/opt-bold-if.test | 16 +++ test/baseline/opt-budget-format.test | 21 ++++ test/baseline/opt-count.test | 43 +++++++ test/baseline/opt-decimal-comma.test | 22 ++++ test/baseline/opt-forecast-years.test | 202 ++++++++++++++++++++++++++++++ test/baseline/opt-group-by.test | 114 +++++++++++++++++ test/baseline/opt-group-title-format.test | 48 +++++++ test/baseline/opt-inject.test | 10 ++ test/baseline/opt-lot-notes.test | 31 +++++ 12 files changed, 583 insertions(+), 4 deletions(-) create mode 100644 test/baseline/opt-auto-match.dat (limited to 'test') diff --git a/test/baseline/cmd-source.test b/test/baseline/cmd-source.test index e78ae6fd..95a10924 100644 --- a/test/baseline/cmd-source.test +++ b/test/baseline/cmd-source.test @@ -1,3 +1,5 @@ +~ xxx + 2012-02-28 * Test E 30.00 EUR F @@ -12,15 +14,51 @@ E 30.00 EUR F -test source -> 3 +2012-03-25 * Test + G AAA + H + +2012-03-26 * Test + I 1,00.00 EUR + J -100.00 EUR + +2012-03-27 * Test + K 100.00 EUR + L -200.00 EUR + +test source -> 7 __ERROR__ -While parsing file "$FILE", line 4: -Error: Only one posting with null amount allowed per transaction -While parsing file "$FILE", line 9: +While parsing file "$FILE", line 1: +While parsing periodic transaction: +> ~ xxx +Error: Unexpected date period token 'xxx' +While parsing file "$FILE", line 6: Error: Only one posting with null amount allowed per transaction While parsing file "$FILE", line 11: +Error: Only one posting with null amount allowed per transaction +While parsing file "$FILE", line 13: While parsing transaction: > 2012/03/xx Error: Invalid date: 2012/03/xx +While parsing file "$FILE", line 18: +While parsing posting: + G AAA + ^^^ +Error: No quantity specified for amount +While parsing file "$FILE", line 22: +While parsing posting: + I 1,00.00 EUR + ^^^^^^^^^^^ +Error: Incorrect use of thousand-mark comma +While parsing file "$FILE", line 27: +While balancing transaction from "$FILE", lines 25-27: +> 2012-03-27 * Test +> K 100.00 EUR +> L -200.00 EUR +Unbalanced remainder is: + -100.00 EUR +Amount to balance against: + 100.00 EUR +Error: Transaction does not balance end test diff --git a/test/baseline/opt-auto-match.dat b/test/baseline/opt-auto-match.dat new file mode 100644 index 00000000..bfbf71eb --- /dev/null +++ b/test/baseline/opt-auto-match.dat @@ -0,0 +1,4 @@ +date,amount,desc, +2012/03/01,10,Food, +2012/03/02,10,Phone, +2012/03/02,10,Dining, diff --git a/test/baseline/opt-auto-match.test b/test/baseline/opt-auto-match.test index e69de29b..7c3fb40a 100644 --- a/test/baseline/opt-auto-match.test +++ b/test/baseline/opt-auto-match.test @@ -0,0 +1,30 @@ +2012-01-01 * Opening balance + Assets:Cash 100.00 EUR + Equity:Opening Balance + +2012-01-02 * Food + Expenses:Food 25.00 EUR + Assets:Cash -25.00 EUR + +2012-01-03 * Phone + Expenses:Phone 10.00 EUR + Assets:Cash -10.00 EUR + +2012-01-04 * Dining + Expenses:Food 20.00 EUR + Liabilities:CC -20.00 EUR + +test --input-date-format "%Y-%m-%d" --auto-match convert test/baseline/opt-auto-match.dat +2012/03/01 * Food + Assets:Cash 10 + Equity:Unknown + +2012/03/02 * Phone + Assets:Cash 10 + Equity:Unknown + +2012/03/02 * Dining + Liabilities:CC 10 + Equity:Unknown +end test + diff --git a/test/baseline/opt-bold-if.test b/test/baseline/opt-bold-if.test index e69de29b..1f6f4c21 100644 --- a/test/baseline/opt-bold-if.test +++ b/test/baseline/opt-bold-if.test @@ -0,0 +1,16 @@ +2012-01-01 * Opening balance + Assets:Cash 100.00 EUR + Equity:Opening Balance + +2012-01-02 * Test + ; :test: + Expenses:Food 100.00 EUR + Assets:Cash -100.00 EUR + +test reg --bold-if 'has_tag("test")' +12-Jan-01 Opening balance Assets:Cash 100.00 EUR 100.00 EUR + Equity:Opening Balance -100.00 EUR 0 +12-Jan-02 Test  Expenses:Food   100.00 EUR  100.00 EUR +   Assets:Cash   -100.00 EUR  0 +end test + diff --git a/test/baseline/opt-budget-format.test b/test/baseline/opt-budget-format.test index e69de29b..d2b84f98 100644 --- a/test/baseline/opt-budget-format.test +++ b/test/baseline/opt-budget-format.test @@ -0,0 +1,21 @@ +~ Monthly + Expenses:Phone 10.00 GBP + Expenses:Rent 550.00 GBP + Assets + +2012-02-28 * Phone expense + Expenses:Phone 20.00 GBP + Assets:Cash -20.00 GBP + +2012-02-29 * Rent expense + Expenses:Rent 530.00 GBP + Assets:Cash -530.00 GBP + +test budget --now 2012-02-29 --budget-format "%(justify(scrub(display_total), 0))\n" +(-550.00 GBP, 560.00 GBP) +(550.00 GBP, -560.00 GBP) +(20.00 GBP, -10.00 GBP) +(530.00 GBP, -550.00 GBP) +(0, 0) +end test + diff --git a/test/baseline/opt-count.test b/test/baseline/opt-count.test index e69de29b..9c5495c8 100644 --- a/test/baseline/opt-count.test +++ b/test/baseline/opt-count.test @@ -0,0 +1,43 @@ +2012-02-28 Phone expense + Expenses:Phone 20.00 GBP + Assets:Cash -20.00 GBP + +2012-02-29 * Rent expense + Expenses:Rent 530.00 GBP + Assets:Cash -530.00 GBP + +2012-03-03 Phone expense + Expenses:Phone 12.00 EUR + Assets:Cash -12.00 EUR + +2012-03-04 * Bed and breakfast + ; Payee: Rent expense + ; :bnb: + Expenses:Rent 30.00 EUR + Assets:Cash -30.00 EUR + +test accounts --count +2 Expenses:Phone +4 Assets:Cash +2 Expenses:Rent +end test + +test commodities --count +4 GBP +4 EUR +end test + +test payees --count +4 Phone expense +4 Rent expense +end test + +test commodities :rent --count +1 GBP +1 EUR +end test + +test payees tag bnb --count +2 Rent expense +end test + diff --git a/test/baseline/opt-decimal-comma.test b/test/baseline/opt-decimal-comma.test index e69de29b..e056c914 100644 --- a/test/baseline/opt-decimal-comma.test +++ b/test/baseline/opt-decimal-comma.test @@ -0,0 +1,22 @@ +2012-01-01 * Opening balance + Assets:Cash 100,00 EUR + Equity:Opening Balance + +2012-01-02 * Test + Expenses:Food 10,00 EUR + Assets:Cash -10,00 EUR + +2012-01-03 * Test + Expenses:Food €10,00 + Assets:Cash €-10,00 + +test --decimal-comma bal + 90,00 EUR + €-10,00 Assets:Cash + -100,00 EUR Equity:Opening Balance + 10,00 EUR + €10,00 Expenses:Food +-------------------- + 0 +end test + diff --git a/test/baseline/opt-forecast-years.test b/test/baseline/opt-forecast-years.test index e69de29b..6b1053f5 100644 --- a/test/baseline/opt-forecast-years.test +++ b/test/baseline/opt-forecast-years.test @@ -0,0 +1,202 @@ +~ Monthly + Expenses:Rent 500.00 GBP + Assets + +test --now 2012-01-01 --forecast "T<200000.00 GBP" reg :rent +12-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 500.00 GBP +12-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 1000.00 GBP +12-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 1500.00 GBP +12-May-01 Forecast transaction Expenses:Rent 500.00 GBP 2000.00 GBP +12-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 2500.00 GBP +12-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 3000.00 GBP +12-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 3500.00 GBP +12-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 4000.00 GBP +12-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 4500.00 GBP +12-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 5000.00 GBP +12-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 5500.00 GBP +13-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 6000.00 GBP +13-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 6500.00 GBP +13-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 7000.00 GBP +13-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 7500.00 GBP +13-May-01 Forecast transaction Expenses:Rent 500.00 GBP 8000.00 GBP +13-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 8500.00 GBP +13-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 9000.00 GBP +13-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 9500.00 GBP +13-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 10000.00 GBP +13-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 10500.00 GBP +13-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 11000.00 GBP +13-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 11500.00 GBP +14-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 12000.00 GBP +14-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 12500.00 GBP +14-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 13000.00 GBP +14-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 13500.00 GBP +14-May-01 Forecast transaction Expenses:Rent 500.00 GBP 14000.00 GBP +14-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 14500.00 GBP +14-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 15000.00 GBP +14-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 15500.00 GBP +14-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 16000.00 GBP +14-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 16500.00 GBP +14-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 17000.00 GBP +14-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 17500.00 GBP +15-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 18000.00 GBP +15-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 18500.00 GBP +15-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 19000.00 GBP +15-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 19500.00 GBP +15-May-01 Forecast transaction Expenses:Rent 500.00 GBP 20000.00 GBP +15-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 20500.00 GBP +15-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 21000.00 GBP +15-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 21500.00 GBP +15-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 22000.00 GBP +15-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 22500.00 GBP +15-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 23000.00 GBP +15-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 23500.00 GBP +16-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 24000.00 GBP +16-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 24500.00 GBP +16-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 25000.00 GBP +16-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 25500.00 GBP +16-May-01 Forecast transaction Expenses:Rent 500.00 GBP 26000.00 GBP +16-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 26500.00 GBP +16-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 27000.00 GBP +16-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 27500.00 GBP +16-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 28000.00 GBP +16-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 28500.00 GBP +16-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 29000.00 GBP +16-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 29500.00 GBP +end test + +test --now 2012-01-01 --forecast-years 1 --forecast "T<200000.00 GBP" reg :rent +12-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 500.00 GBP +12-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 1000.00 GBP +12-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 1500.00 GBP +12-May-01 Forecast transaction Expenses:Rent 500.00 GBP 2000.00 GBP +12-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 2500.00 GBP +12-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 3000.00 GBP +12-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 3500.00 GBP +12-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 4000.00 GBP +12-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 4500.00 GBP +12-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 5000.00 GBP +12-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 5500.00 GBP +end test + +test --now 2012-01-01 --forecast-years 10 --forecast "T<200000.00 GBP" reg :rent +12-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 500.00 GBP +12-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 1000.00 GBP +12-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 1500.00 GBP +12-May-01 Forecast transaction Expenses:Rent 500.00 GBP 2000.00 GBP +12-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 2500.00 GBP +12-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 3000.00 GBP +12-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 3500.00 GBP +12-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 4000.00 GBP +12-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 4500.00 GBP +12-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 5000.00 GBP +12-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 5500.00 GBP +13-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 6000.00 GBP +13-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 6500.00 GBP +13-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 7000.00 GBP +13-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 7500.00 GBP +13-May-01 Forecast transaction Expenses:Rent 500.00 GBP 8000.00 GBP +13-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 8500.00 GBP +13-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 9000.00 GBP +13-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 9500.00 GBP +13-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 10000.00 GBP +13-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 10500.00 GBP +13-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 11000.00 GBP +13-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 11500.00 GBP +14-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 12000.00 GBP +14-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 12500.00 GBP +14-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 13000.00 GBP +14-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 13500.00 GBP +14-May-01 Forecast transaction Expenses:Rent 500.00 GBP 14000.00 GBP +14-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 14500.00 GBP +14-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 15000.00 GBP +14-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 15500.00 GBP +14-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 16000.00 GBP +14-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 16500.00 GBP +14-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 17000.00 GBP +14-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 17500.00 GBP +15-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 18000.00 GBP +15-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 18500.00 GBP +15-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 19000.00 GBP +15-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 19500.00 GBP +15-May-01 Forecast transaction Expenses:Rent 500.00 GBP 20000.00 GBP +15-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 20500.00 GBP +15-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 21000.00 GBP +15-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 21500.00 GBP +15-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 22000.00 GBP +15-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 22500.00 GBP +15-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 23000.00 GBP +15-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 23500.00 GBP +16-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 24000.00 GBP +16-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 24500.00 GBP +16-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 25000.00 GBP +16-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 25500.00 GBP +16-May-01 Forecast transaction Expenses:Rent 500.00 GBP 26000.00 GBP +16-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 26500.00 GBP +16-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 27000.00 GBP +16-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 27500.00 GBP +16-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 28000.00 GBP +16-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 28500.00 GBP +16-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 29000.00 GBP +16-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 29500.00 GBP +17-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 30000.00 GBP +17-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 30500.00 GBP +17-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 31000.00 GBP +17-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 31500.00 GBP +17-May-01 Forecast transaction Expenses:Rent 500.00 GBP 32000.00 GBP +17-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 32500.00 GBP +17-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 33000.00 GBP +17-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 33500.00 GBP +17-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 34000.00 GBP +17-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 34500.00 GBP +17-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 35000.00 GBP +17-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 35500.00 GBP +18-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 36000.00 GBP +18-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 36500.00 GBP +18-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 37000.00 GBP +18-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 37500.00 GBP +18-May-01 Forecast transaction Expenses:Rent 500.00 GBP 38000.00 GBP +18-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 38500.00 GBP +18-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 39000.00 GBP +18-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 39500.00 GBP +18-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 40000.00 GBP +18-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 40500.00 GBP +18-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 41000.00 GBP +18-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 41500.00 GBP +19-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 42000.00 GBP +19-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 42500.00 GBP +19-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 43000.00 GBP +19-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 43500.00 GBP +19-May-01 Forecast transaction Expenses:Rent 500.00 GBP 44000.00 GBP +19-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 44500.00 GBP +19-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 45000.00 GBP +19-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 45500.00 GBP +19-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 46000.00 GBP +19-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 46500.00 GBP +19-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 47000.00 GBP +19-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 47500.00 GBP +20-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 48000.00 GBP +20-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 48500.00 GBP +20-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 49000.00 GBP +20-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 49500.00 GBP +20-May-01 Forecast transaction Expenses:Rent 500.00 GBP 50000.00 GBP +20-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 50500.00 GBP +20-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 51000.00 GBP +20-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 51500.00 GBP +20-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 52000.00 GBP +20-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 52500.00 GBP +20-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 53000.00 GBP +20-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 53500.00 GBP +21-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 54000.00 GBP +21-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 54500.00 GBP +21-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 55000.00 GBP +21-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 55500.00 GBP +21-May-01 Forecast transaction Expenses:Rent 500.00 GBP 56000.00 GBP +21-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 56500.00 GBP +21-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 57000.00 GBP +21-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 57500.00 GBP +21-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 58000.00 GBP +21-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 58500.00 GBP +21-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 59000.00 GBP +21-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 59500.00 GBP +end test + diff --git a/test/baseline/opt-group-by.test b/test/baseline/opt-group-by.test index e69de29b..1f6c6adf 100644 --- a/test/baseline/opt-group-by.test +++ b/test/baseline/opt-group-by.test @@ -0,0 +1,114 @@ +2012-03-20 * Test GBP + A -10.00 GBP + B + +2012-03-20 * Test EUR + A -10.00 EUR + B + +2012-03-22 * Test GBP + A -10.00 GBP + B + +2012-03-22 * Test EUR + A -10.00 EUR + B + +2012-03-25 * Test GBP + A -10.00 GBP + B + +2012-03-25 * Test EUR + A -10.00 EUR + B + +test reg --group-by payee +Test EUR +12-Mar-20 Test EUR A -10.00 EUR -10.00 EUR + B 10.00 EUR 0 +12-Mar-22 Test EUR A -10.00 EUR -10.00 EUR + B 10.00 EUR 0 +12-Mar-25 Test EUR A -10.00 EUR -10.00 EUR + B 10.00 EUR 0 + +Test GBP +12-Mar-20 Test GBP A -10.00 GBP -10.00 GBP + B 10.00 GBP 0 +12-Mar-22 Test GBP A -10.00 GBP -10.00 GBP + B 10.00 GBP 0 +12-Mar-25 Test GBP A -10.00 GBP -10.00 GBP + B 10.00 GBP 0 +end test + +test reg --group-by commodity +EUR +12-Mar-20 Test EUR A -10.00 EUR -10.00 EUR + B 10.00 EUR 0 +12-Mar-22 Test EUR A -10.00 EUR -10.00 EUR + B 10.00 EUR 0 +12-Mar-25 Test EUR A -10.00 EUR -10.00 EUR + B 10.00 EUR 0 + +GBP +12-Mar-20 Test GBP A -10.00 GBP -10.00 GBP + B 10.00 GBP 0 +12-Mar-22 Test GBP A -10.00 GBP -10.00 GBP + B 10.00 GBP 0 +12-Mar-25 Test GBP A -10.00 GBP -10.00 GBP + B 10.00 GBP 0 +end test + +test bal --group-by commodity +EUR + -30.00 EUR A + 30.00 EUR B +-------------------- + 0 + +GBP + -30.00 GBP A + 30.00 GBP B +-------------------- + 0 +end test + +test bal --group-by payee +Test EUR + -30.00 EUR A + 30.00 EUR B +-------------------- + 0 + +Test GBP + -30.00 GBP A + 30.00 GBP B +-------------------- + 0 +end test + +test bal --group-by date +2012/03/20 + -10.00 EUR + -10.00 GBP A + 10.00 EUR + 10.00 GBP B +-------------------- + 0 + +2012/03/22 + -10.00 EUR + -10.00 GBP A + 10.00 EUR + 10.00 GBP B +-------------------- + 0 + +2012/03/25 + -10.00 EUR + -10.00 GBP A + 10.00 EUR + 10.00 GBP B +-------------------- + 0 +end test + diff --git a/test/baseline/opt-group-title-format.test b/test/baseline/opt-group-title-format.test index e69de29b..a2a1a984 100644 --- a/test/baseline/opt-group-title-format.test +++ b/test/baseline/opt-group-title-format.test @@ -0,0 +1,48 @@ +2012-03-20 * Test GBP + A -10.00 GBP + B + +2012-03-20 * Test EUR + A -10.00 EUR + B + +2012-03-22 * Test GBP + A -10.00 GBP + B + +2012-03-22 * Test EUR + A -10.00 EUR + B + +test bal --group-by payee --group-title-format "-%(value)-\n" +-Test EUR- + -20.00 EUR A + 20.00 EUR B +-------------------- + 0 + +-Test GBP- + -20.00 GBP A + 20.00 GBP B +-------------------- + 0 +end test + +test bal --group-by date --group-title-format "|%(value)|\n" +|2012/03/20| + -10.00 EUR + -10.00 GBP A + 10.00 EUR + 10.00 GBP B +-------------------- + 0 + +|2012/03/22| + -10.00 EUR + -10.00 GBP A + 10.00 EUR + 10.00 GBP B +-------------------- + 0 +end test + diff --git a/test/baseline/opt-inject.test b/test/baseline/opt-inject.test index e69de29b..685cf12c 100644 --- a/test/baseline/opt-inject.test +++ b/test/baseline/opt-inject.test @@ -0,0 +1,10 @@ +2012-03-20 * Test GBP + ; Expected:: -15.00 GBP + Expenses:Phone 20.00 GBP + Assets:Cash + +test --inject Expected reg Expenses:Phone +12-Mar-20 Test GBP Expected -15.00 GBP -15.00 GBP +12-Mar-20 Test GBP Expenses:Phone 20.00 GBP 5.00 GBP +end test + diff --git a/test/baseline/opt-lot-notes.test b/test/baseline/opt-lot-notes.test index e69de29b..3bfa9e45 100644 --- a/test/baseline/opt-lot-notes.test +++ b/test/baseline/opt-lot-notes.test @@ -0,0 +1,31 @@ +2012-01-01 * Opening balance + Assets:Cash 100.00 GBP + Equity:Opening Balance + +2012-01-02 * Voucher 1 + Assets:Voucher 10.00 GBP (aaaa) + Assets:Cash -10.00 GBP + +2012-01-03 * Voucher 1 + Assets:Voucher 10.00 GBP (bbbb) + Assets:Cash -10.00 GBP + +2012-01-04 * Voucher 1 + Assets:Voucher 10.00 GBP (cccc) + Assets:Cash -10.00 GBP + +test bal assets:voucher --lot-notes + 10.00 GBP (aaaa) + 10.00 GBP (bbbb) + 10.00 GBP (cccc) Assets:Voucher +end test + +test reg assets:voucher --lot-notes +12-Jan-02 Voucher 1 Assets:Voucher 10.00 GBP (aaaa) 10.00 GBP (aaaa) +12-Jan-03 Voucher 1 Assets:Voucher 10.00 GBP (bbbb) 10.00 GBP (aaaa) + 10.00 GBP (bbbb) +12-Jan-04 Voucher 1 Assets:Voucher 10.00 GBP (cccc) 10.00 GBP (aaaa) + 10.00 GBP (bbbb) + 10.00 GBP (cccc) +end test + -- cgit v1.2.3 From 86d58584441bf673266f234fc1c77e306f2efeb0 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Sun, 25 Mar 2012 20:26:18 +0100 Subject: Add more baseline tests. --- test/baseline/dir-account.test | 19 ++++++++++++++++++- test/baseline/dir-commodity.test | 21 +++++++++++++++++++++ test/baseline/dir-payee.test | 12 ++++++++++++ test/baseline/opt-check-payees.test | 37 +++++++++++++++++++++++++++++++++++++ test/baseline/opt-explicit.test | 34 ++++++++++++++++++++++++++++++++++ test/baseline/opt-pedantic.test | 17 +++++++++++++++++ 6 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 test/baseline/dir-commodity.test create mode 100644 test/baseline/dir-payee.test (limited to 'test') diff --git a/test/baseline/dir-account.test b/test/baseline/dir-account.test index 39831663..e8c3fc54 100644 --- a/test/baseline/dir-account.test +++ b/test/baseline/dir-account.test @@ -5,8 +5,9 @@ commodity $ format $1,000.00 account Assets:Cash - assert abs(amount) <= 20 + check abs(amount) <= 20 check commodity == '$' + default account Expenses:Food alias food @@ -20,9 +21,25 @@ account Expenses:Food food $20.00 Assets:Cash +2012-02-29 KFC + food $25.00 + Assets:Cash + +2012-02-29 KFC + food $25.00 + Assets:Cash + test reg 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 +12-Feb-29 KFC Expenses:Food $25.00 $25.00 + Assets:Cash $-25.00 0 +12-Feb-29 KFC Expenses:Food $25.00 $25.00 + Assets:Cash $-25.00 0 +__ERROR__ +Warning: "$FILE", line 26: Transaction check failed: (abs(amount) <= {20}) +Warning: "$FILE", line 30: Transaction check failed: (abs(amount) <= {20}) end test + diff --git a/test/baseline/dir-commodity.test b/test/baseline/dir-commodity.test new file mode 100644 index 00000000..fc925648 --- /dev/null +++ b/test/baseline/dir-commodity.test @@ -0,0 +1,21 @@ +account A +account B +commodity GBP + +2012-03-25 GBP + A 10.00 GBP + B + +2012-03-26 EUR + A 20.00 EUR + B + +test bal --pedantic -> 1 +__ERROR__ +While parsing file "$FILE", line 10: +While parsing posting: + A 20.00 EUR + ^^^^^^^^^ +Error: Unknown commodity 'EUR' +end test + diff --git a/test/baseline/dir-payee.test b/test/baseline/dir-payee.test new file mode 100644 index 00000000..b81bbc2b --- /dev/null +++ b/test/baseline/dir-payee.test @@ -0,0 +1,12 @@ +payee KFC + alias Kentucky Fried Chicken + +2012-03-25 * Kentucky Fried Chicken + A 10 + B + +test reg +12-Mar-25 KFC A 10 10 + B -10 0 +end test + diff --git a/test/baseline/opt-check-payees.test b/test/baseline/opt-check-payees.test index e69de29b..923729e7 100644 --- a/test/baseline/opt-check-payees.test +++ b/test/baseline/opt-check-payees.test @@ -0,0 +1,37 @@ +account Assets:Cash +account Expenses:Phone +account Expenses:Rent +account Expenses:Food +commodity EUR +commodity GBP +payee Phone +tag food + +2012-03-20 Phone + Expenses:Phone 20.00 GBP + Assets:Cash + +2012-03-21 Rent + Expenses:Rent 550.00 GBP + Assets:Cash + +2012-03-22 Food + ; :food: + Expenses:Food 20.00 EUR + Assets:Cash + +test bal --explicit --strict --check-payees + -20.00 EUR + -570.00 GBP Assets:Cash + 20.00 EUR + 570.00 GBP Expenses + 20.00 EUR Food + 20.00 GBP Phone + 550.00 GBP Rent +-------------------- + 0 +__ERROR__ +Warning: "$FILE", line 14: Unknown payee 'Rent' +Warning: "$FILE", line 18: Unknown payee 'Food' +end test + diff --git a/test/baseline/opt-explicit.test b/test/baseline/opt-explicit.test index e69de29b..defae179 100644 --- a/test/baseline/opt-explicit.test +++ b/test/baseline/opt-explicit.test @@ -0,0 +1,34 @@ +account Assets:Cash +account Expenses:Phone +account Expenses:Rent +commodity GBP + +2012-03-20 Phone + Expenses:Phone 20.00 GBP + Assets:Cash + +2012-03-21 Rent + Expenses:Rent 550.00 GBP + Assets:Cash + +2012-03-22 Food + ; :food: + Expenses:Food 20.00 EUR + Assets:Cash + +test bal --explicit --strict + -20.00 EUR + -570.00 GBP Assets:Cash + 20.00 EUR + 570.00 GBP Expenses + 20.00 EUR Food + 20.00 GBP Phone + 550.00 GBP Rent +-------------------- + 0 +__ERROR__ +Warning: "$FILE", line 16: Unknown account 'Expenses:Food' +Warning: "$FILE", line 16: Unknown commodity 'EUR' +Warning: "$FILE", line 17: Unknown metadata tag 'food' +end test + diff --git a/test/baseline/opt-pedantic.test b/test/baseline/opt-pedantic.test index e69de29b..fbb27b84 100644 --- a/test/baseline/opt-pedantic.test +++ b/test/baseline/opt-pedantic.test @@ -0,0 +1,17 @@ +2012-03-20 Test GBP + Expenses:Phone 20.00 GBP + Assets:Cash + +2012-03-21 * Test GBP + Expenses:Phone 20.00 GBP + Assets:Cash + +test bal --pedantic -> 1 +__ERROR__ +While parsing file "$FILE", line 2: +While parsing posting: + Expenses:Phone 20.00 GBP + +Error: Unknown account 'Expenses:Phone' +end test + -- cgit v1.2.3 From f3e320c98a39140989d52524a96ad205139ce06a Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Tue, 27 Mar 2012 20:15:42 +0100 Subject: Fix some tests not to be time-sensitive --- test/baseline/cmd-entry.test | 8 ++++---- test/baseline/cmd-xact.test | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/baseline/cmd-entry.test b/test/baseline/cmd-entry.test index 0ecfa5eb..0de39b9c 100644 --- a/test/baseline/cmd-entry.test +++ b/test/baseline/cmd-entry.test @@ -11,26 +11,26 @@ E 30.00 GBP F -test entry "Test 1" +test --now 2012/03/25 entry "Test 1" 2012/03/25 Test 1 A $10.00 B end test -test entry "Test 2" +test --now 2012/03/25 entry "Test 2" 2012/03/25 Test 2 C 20.00 EUR D end test ; I think this output is wrong, see bug #737 -test entry "Test 3" +test --now 2012/03/25 entry "Test 3" 2012/03/25 Test 4 E 30.00 GBP F end test -test entry "Test 4" +test --now 2012/03/25 entry "Test 4" 2012/03/25 Test 4 E 30.00 GBP F diff --git a/test/baseline/cmd-xact.test b/test/baseline/cmd-xact.test index f473c3dd..6f4ee014 100644 --- a/test/baseline/cmd-xact.test +++ b/test/baseline/cmd-xact.test @@ -11,26 +11,26 @@ E 30.00 GBP F -test xact "Test 1" +test --now 2012/03/25 xact "Test 1" 2012/03/25 Test 1 A $10.00 B end test -test xact "Test 2" +test --now 2012/03/25 xact "Test 2" 2012/03/25 Test 2 C 20.00 EUR D end test ; I think this output is wrong, see bug #737 -test xact "Test 3" +test --now 2012/03/25 xact "Test 3" 2012/03/25 Test 4 E 30.00 GBP F end test -test xact "Test 4" +test --now 2012/03/25 xact "Test 4" 2012/03/25 Test 4 E 30.00 GBP F -- cgit v1.2.3 From 362fad5925ba4e828b80bd5a90aff825296e6ad0 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Tue, 27 Mar 2012 20:16:17 +0100 Subject: Add more tests --- test/baseline/dir-commodity-alias.test | 23 +++++++++++++++++++++++ test/regress/C19E4E9B.test | 18 ++++++++++++++++++ test/regress/EA18D948.test | 14 ++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 test/baseline/dir-commodity-alias.test create mode 100644 test/regress/C19E4E9B.test create mode 100644 test/regress/EA18D948.test (limited to 'test') diff --git a/test/baseline/dir-commodity-alias.test b/test/baseline/dir-commodity-alias.test new file mode 100644 index 00000000..4de7f406 --- /dev/null +++ b/test/baseline/dir-commodity-alias.test @@ -0,0 +1,23 @@ +commodity $ + alias USD + +2012-03-12 * $ + A $10.00 + B + +2012-03-12 * USD + A 15.00 USD + B + +test bal + 25.00 $ A + -25.00 $ B +-------------------- + 0 +end test + +test reg a +12-Mar-12 $ A 10.00 $ 10.00 $ +12-Mar-12 USD A 15.00 $ 25.00 $ +end test + diff --git a/test/regress/C19E4E9B.test b/test/regress/C19E4E9B.test new file mode 100644 index 00000000..4837b4cd --- /dev/null +++ b/test/regress/C19E4E9B.test @@ -0,0 +1,18 @@ +2012-01-01=2012-01-02 * Buy AAA + A 1 AAA @ 1.00 EUR + B -1.00 EUR + +2012-02-01 * Buy AAA + A 1 AAA @ 2.00 EUR + B -2.00 EUR + +test reg --format "%S: %d %P %t %T\n" +$FILE: 2012/01/01 Buy AAA 1 AAA 1 AAA +$FILE: 2012/01/01 Buy AAA -1.00 EUR 1 AAA +-1.00 EUR +$FILE: 2012/02/01 Buy AAA 1 AAA 2 AAA +-1.00 EUR +$FILE: 2012/02/01 Buy AAA -2.00 EUR 2 AAA +-3.00 EUR +end test + diff --git a/test/regress/EA18D948.test b/test/regress/EA18D948.test new file mode 100644 index 00000000..a63d4c35 --- /dev/null +++ b/test/regress/EA18D948.test @@ -0,0 +1,14 @@ +2012-01-01 * Buy AAA + A 1 AAA @ 1.00 EUR + B -1.00 EUR + +2012-02-01 * Buy AAA + A 1 AAA @ 2.00 EUR + B -2.00 EUR + +test reg A -V -A +12-Jan-01 Buy AAA A 1.00 EUR 1.00 EUR +12-Feb-01 Commodities revalued 1.00 EUR 0 +12-Feb-01 Buy AAA A 2.00 EUR 2.00 EUR +end test + -- cgit v1.2.3 From ef7cab0830637828334fae0a9ad37d20f8e75176 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 29 Mar 2012 03:55:48 -0500 Subject: Removed the cmd-emacs baseline test The time issue causes too many false positives. --- test/baseline/cmd-emacs.test | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 test/baseline/cmd-emacs.test (limited to 'test') diff --git a/test/baseline/cmd-emacs.test b/test/baseline/cmd-emacs.test deleted file mode 100644 index beb4a6b5..00000000 --- a/test/baseline/cmd-emacs.test +++ /dev/null @@ -1,18 +0,0 @@ - -2012-03-23 * Test - A 10.00 - B - -2012-03-23 * (100) Code - A 10.00 EUR - B - -test emacs --now=2012/03/23 -(("$FILE" 2 (20332 848 0) nil "Test" - (3 "A" "10" t) - (4 "B" "-10" t)) - ("$FILE" 6 (20332 848 0) "100" "Code" - (7 "A" "10.00 EUR" t) - (8 "B" "-10.00 EUR" t))) -end test - -- cgit v1.2.3 From 3a159a134480debee2fd1665b87ab2d0994c3a3e Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Thu, 29 Mar 2012 22:56:56 +0100 Subject: Add a regression test --- test/regress/2CE7DADB.test | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/regress/2CE7DADB.test (limited to 'test') diff --git a/test/regress/2CE7DADB.test b/test/regress/2CE7DADB.test new file mode 100644 index 00000000..c6d94b96 --- /dev/null +++ b/test/regress/2CE7DADB.test @@ -0,0 +1,13 @@ +2012-01-01 * Buy AAA + A 1 AAA @ 1.00 EUR + B -1.00 EUR + +2012-02-01 * Buy AAA + A 1 AAA @ 2.00 EUR + B -2.00 EUR + +test --anon pricedb --format "%(date) %(amount)\n" +2012/01/01 1.00 A +2012/02/01 2.00 A +end test + -- 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 'test') 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 413a392aa99e8ff225d00d4e07d7370797ef4d88 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 30 Mar 2012 00:40:58 -0500 Subject: Added the first Python support regression test --- test/regress/4D9288AE.dat | 4 ++++ test/regress/4D9288AE.py | 4 ++++ test/regress/4D9288AE_py.test | 3 +++ 3 files changed, 11 insertions(+) create mode 100644 test/regress/4D9288AE.dat create mode 100644 test/regress/4D9288AE.py create mode 100644 test/regress/4D9288AE_py.test (limited to 'test') diff --git a/test/regress/4D9288AE.dat b/test/regress/4D9288AE.dat new file mode 100644 index 00000000..758feb19 --- /dev/null +++ b/test/regress/4D9288AE.dat @@ -0,0 +1,4 @@ +2012-03-17 Payee + Expenses:Food $20 + Assets:Cash + diff --git a/test/regress/4D9288AE.py b/test/regress/4D9288AE.py new file mode 100644 index 00000000..4f9c9ba9 --- /dev/null +++ b/test/regress/4D9288AE.py @@ -0,0 +1,4 @@ +import ledger + +for post in ledger.read_journal("test/regress/4D9288AE.dat").query("^expenses:"): + print post.cost diff --git a/test/regress/4D9288AE_py.test b/test/regress/4D9288AE_py.test new file mode 100644 index 00000000..ff2874ce --- /dev/null +++ b/test/regress/4D9288AE_py.test @@ -0,0 +1,3 @@ +test python test/regress/4D9288AE.py +None +end test -- cgit v1.2.3 From bf66abc258ee70de5bd5510301795168f46929f8 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 30 Mar 2012 00:43:48 -0500 Subject: Added another Python support regression test --- test/regress/78AB4B87.dat | 14 ++++++++++++++ test/regress/78AB4B87.py | 27 +++++++++++++++++++++++++++ test/regress/78AB4B87_py.test | 15 +++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 test/regress/78AB4B87.dat create mode 100644 test/regress/78AB4B87.py create mode 100644 test/regress/78AB4B87_py.test (limited to 'test') diff --git a/test/regress/78AB4B87.dat b/test/regress/78AB4B87.dat new file mode 100644 index 00000000..45b3028a --- /dev/null +++ b/test/regress/78AB4B87.dat @@ -0,0 +1,14 @@ +D 1000.00 EUR +P 2011-01-01 GBP 1.2 EUR + +2011-01-01 * Opening balance + Assets:Bank 10.00 GBP + Equity:Opening balance + +2012-01-02 * Test + Assets:Bank 5.00 GBP + Income:Whatever + +2012-01-03 * Test + Assets:Bank + Income:Whatever -5.00 EUR @ 0.8733 GBP diff --git a/test/regress/78AB4B87.py b/test/regress/78AB4B87.py new file mode 100644 index 00000000..fed95b54 --- /dev/null +++ b/test/regress/78AB4B87.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) + 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/78AB4B87_py.test b/test/regress/78AB4B87_py.test new file mode 100644 index 00000000..8f847145 --- /dev/null +++ b/test/regress/78AB4B87_py.test @@ -0,0 +1,15 @@ +test python test/regress/78AB4B87.py +-5.00 GBP +GBP +Total is presently: (0.00 EUR) +Converted to EUR: (-5.73 EUR) +Total is now: (-5.73 EUR) + +-5.00 EUR {0.8733 GBP} [2012/01/03] +EUR +Total is presently: (-5.73 EUR) +Converted to EUR: (-5.00 EUR) +Total is now: (-10.73 EUR) + +-10.73 EUR +end test -- 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 'test') 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 5c57eff64a0f6378c7be81369afb831769529a79 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 30 Mar 2012 04:49:19 -0500 Subject: Simplify feat-option_py.test --- test/baseline/feat-option_py.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/baseline/feat-option_py.test b/test/baseline/feat-option_py.test index a67b27c3..1b2a0c79 100644 --- a/test/baseline/feat-option_py.test +++ b/test/baseline/feat-option_py.test @@ -9,6 +9,6 @@ python --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) +In --pyfirst (from $FILE) +In --pysecond=Hey (from $FILE) end test -- cgit v1.2.3 From 1809e63fb0e6f7e64d7c2af73e41a24225809ea2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 30 Mar 2012 04:49:45 -0500 Subject: Added a test harness error diagnostic --- test/ConfirmTests.py | 2 ++ test/RegressTests.py | 2 ++ 2 files changed, 4 insertions(+) (limited to 'test') diff --git a/test/ConfirmTests.py b/test/ConfirmTests.py index 6fc04336..dffa74a6 100755 --- a/test/ConfirmTests.py +++ b/test/ConfirmTests.py @@ -13,6 +13,8 @@ harness = LedgerHarness(sys.argv) tests = sys.argv[3] if not os.path.isdir(tests) and not os.path.isfile(tests): + sys.stderr.write("'%s' is not a directory or file (cwd %s)" % + (tests, os.getcwd())) sys.exit(1) commands = [ diff --git a/test/RegressTests.py b/test/RegressTests.py index 72667311..a7d51ada 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -31,6 +31,8 @@ harness = LedgerHarness(args) tests = args[3] if not os.path.isdir(tests) and not os.path.isfile(tests): + sys.stderr.write("'%s' is not a directory or file (cwd %s)" % + (tests, os.getcwd())) sys.exit(1) class RegressFile(object): -- 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 'test') 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 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 'test') 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 a910549ca9f7d07d7610eb27d8b0709dc42802a6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 25 Apr 2012 03:08:45 -0500 Subject: Fixes so that tests can run under distcheck --- test/LedgerHarness.py | 5 ++--- test/RegressTests.py | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/LedgerHarness.py b/test/LedgerHarness.py index 3e797e85..b8900971 100755 --- a/test/LedgerHarness.py +++ b/test/LedgerHarness.py @@ -52,8 +52,6 @@ class LedgerHarness: 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() @@ -89,7 +87,8 @@ class LedgerHarness: command = valgrind + ' -q ' + command return Popen(command, shell=True, close_fds=True, env=env, - stdin=PIPE, stdout=PIPE, stderr=PIPE) + stdin=PIPE, stdout=PIPE, stderr=PIPE, + cwd=self.sourcepath) def read(self, fd): text = "" diff --git a/test/RegressTests.py b/test/RegressTests.py index 792c2d3e..7d67eb21 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -42,7 +42,7 @@ class RegressFile(object): def transform_line(self, line): line = re.sub('\$sourcepath', harness.sourcepath, line) - line = re.sub('\$FILE', self.filename, line) + line = re.sub('\$FILE', os.path.abspath(self.filename), line) return line def read_test(self): @@ -93,7 +93,7 @@ class RegressFile(object): def notify_user(self, msg, test): print msg print "--" - print test['command'], + print self.transform_line(test['command']), print "--" def run_test(self, test): @@ -101,7 +101,8 @@ class RegressFile(object): if test['command'].find("-f - ") != -1: use_stdin = True else: - test['command'] = (('$ledger -f "%s" ' % self.filename) + + test['command'] = (('$ledger -f "%s" ' % + os.path.abspath(self.filename)) + test['command']) p = harness.run(test['command'], -- cgit v1.2.3