diff options
author | John Wiegley <johnw@newartisans.com> | 2006-03-19 21:11:49 +0000 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-04-13 02:41:31 -0400 |
commit | 32bdfe20d98f1c8e6c0aabe9b26f0507326d2baa (patch) | |
tree | 50a4c5490f842ff4241ed6c574d3debd0ed6e1b6 | |
parent | ab748ed13ed87369e2e4ed5a617b5de12750fda6 (diff) | |
download | fork-ledger-32bdfe20d98f1c8e6c0aabe9b26f0507326d2baa.tar.gz fork-ledger-32bdfe20d98f1c8e6c0aabe9b26f0507326d2baa.tar.bz2 fork-ledger-32bdfe20d98f1c8e6c0aabe9b26f0507326d2baa.zip |
Tons of corrections and fixes to value expressions and lot figures.
-rw-r--r-- | amount.cc | 42 | ||||
-rw-r--r-- | balance.h | 50 | ||||
-rw-r--r-- | binary.cc | 130 | ||||
-rw-r--r-- | config.cc | 68 | ||||
-rw-r--r-- | config.h | 2 | ||||
-rw-r--r-- | derive.cc | 5 | ||||
-rw-r--r-- | format.cc | 65 | ||||
-rw-r--r-- | format.h | 6 | ||||
-rw-r--r-- | journal.cc | 27 | ||||
-rw-r--r-- | journal.h | 44 | ||||
-rw-r--r-- | main.cc | 8 | ||||
-rw-r--r-- | textual.cc | 116 | ||||
-rw-r--r-- | valexpr.cc | 726 | ||||
-rw-r--r-- | valexpr.h | 183 | ||||
-rw-r--r-- | walk.cc | 36 | ||||
-rw-r--r-- | walk.h | 4 | ||||
-rw-r--r-- | xml.cc | 4 |
17 files changed, 862 insertions, 654 deletions
@@ -932,9 +932,41 @@ void parse_quantity(std::istream& in, std::string& value) char c = peek_next_nonws(in); READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '-' || c == '.' || c == ','); + + int len = std::strlen(buf); + while (len > 0 && ! std::isdigit(buf[len - 1])) { + buf[--len] = '\0'; + in.unget(); + } + value = buf; } +// Invalid commodity characters: +// SPACE, TAB, NEWLINE, RETURN +// 0-9 . , ; - + * / ^ ? : & | ! = +// < > { } [ ] ( ) @ + +int invalid_chars[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +/* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, +/* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, +/* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +/* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, +/* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, +/* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + void parse_commodity(std::istream& in, std::string& symbol) { char buf[256]; @@ -947,8 +979,7 @@ void parse_commodity(std::istream& in, std::string& symbol) else throw new amount_error("Quoted commodity symbol lacks closing quote"); } else { - READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) && - c != '-' && c != '.'); + READ_INTO(in, buf, 255, c, ! invalid_chars[(unsigned char)c]); } symbol = buf; } @@ -1041,7 +1072,7 @@ void amount_t::parse(std::istream& in, unsigned char flags) } char n; - if (std::isdigit(c) || c == '.') { + if (std::isdigit(c)) { parse_quantity(in, quant); if (! in.eof() && ((n = in.peek()) != '\n')) { @@ -1065,7 +1096,7 @@ void amount_t::parse(std::istream& in, unsigned char flags) parse_quantity(in, quant); - if (! in.eof() && ((n = in.peek()) != '\n')) + if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n')) parse_annotations(in, price, date, tag); } } @@ -1651,6 +1682,9 @@ namespace { const std::time_t date, const std::string& tag) { + if (price < 0) + throw new amount_error("A commodity's price may not be negative"); + std::ostringstream name; comm.write(name); @@ -216,7 +216,15 @@ class balance_t return true; } bool operator<(const amount_t& amt) const { - return amount(amt.commodity()) < amt; + if (amt.commodity()) + return amount(amt.commodity()) < amt; + + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + if ((*i).second < amt) + return true; + return false; } template <typename T> bool operator<(T val) const { @@ -244,7 +252,15 @@ class balance_t return true; } bool operator<=(const amount_t& amt) const { - return amount(amt.commodity()) <= amt; + if (amt.commodity()) + return amount(amt.commodity()) <= amt; + + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + if ((*i).second <= amt) + return true; + return false; } template <typename T> bool operator<=(T val) const { @@ -275,7 +291,15 @@ class balance_t return true; } bool operator>(const amount_t& amt) const { - return amount(amt.commodity()) > amt; + if (amt.commodity()) + return amount(amt.commodity()) > amt; + + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + if ((*i).second > amt) + return true; + return false; } template <typename T> bool operator>(T val) const { @@ -303,7 +327,15 @@ class balance_t return true; } bool operator>=(const amount_t& amt) const { - return amount(amt.commodity()) >= amt; + if (amt.commodity()) + return amount(amt.commodity()) >= amt; + + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + if ((*i).second >= amt) + return true; + return false; } template <typename T> bool operator>=(T val) const { @@ -327,7 +359,15 @@ class balance_t return i == amounts.end() && j == bal.amounts.end(); } bool operator==(const amount_t& amt) const { - return amounts.size() == 1 && (*amounts.begin()).second == amt; + if (amt.commodity()) + return amounts.size() == 1 && (*amounts.begin()).second == amt; + + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + if ((*i).second == amt) + return true; + return false; } template <typename T> bool operator==(T val) const { @@ -245,6 +245,31 @@ inline void read_binary_amount(char *& data, amount_t& amt) amt.read_quantity(data); } +inline void read_binary_value(char *& data, value_t& val) +{ + val.type = static_cast<value_t::type_t>(read_binary_long<int>(data)); + + switch (val.type) { + case value_t::BOOLEAN: + *((bool *) val.data) = read_binary_number<char>(data) == 1; + break; + case value_t::INTEGER: + read_binary_long(data, *((long *) val.data)); + break; + case value_t::DATETIME: + read_binary_number(data, ((datetime_t *) val.data)->when); + break; + case value_t::AMOUNT: + read_binary_amount(data, *((amount_t *) val.data)); + break; + + case value_t::BALANCE: + case value_t::BALANCE_PAIR: + assert(0); + break; + } +} + inline void read_binary_mask(char *& data, mask_t *& mask) { bool exclude; @@ -274,18 +299,13 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr) } switch (expr->kind) { - case value_expr_t::CONSTANT_T: - read_binary_number(data, expr->constant_t); + case value_expr_t::O_ARG: + case value_expr_t::INDEX: + read_binary_long(data, expr->arg_index); break; - case value_expr_t::CONSTANT_I: - read_binary_long(data, expr->constant_i); - break; - case value_expr_t::CONSTANT_A: - expr->constant_a = new amount_t(); - read_binary_amount(data, *(expr->constant_a)); - break; - case value_expr_t::CONSTANT_V: - assert(0); + case value_expr_t::CONSTANT: + expr->value = new value_t; + read_binary_value(data, *expr->value); break; case value_expr_t::F_CODE_MASK: @@ -314,16 +334,26 @@ inline void read_binary_transaction(char *& data, transaction_t * xact) read_binary_long(data, xact->_date_eff); xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1]; - if (read_binary_number<char>(data) == 1) { - read_binary_value_expr(data, xact->amount_expr); - if (xact->amount_expr) xact->amount_expr->acquire(); - } else { + char flag = read_binary_number<char>(data); + if (flag == 0) { + read_binary_amount(data, xact->amount); + } + else if (flag == 1) { read_binary_amount(data, xact->amount); + read_binary_string(data, xact->amount_expr.expr); + } + else { + value_expr_t * ptr = NULL; + read_binary_value_expr(data, ptr); + assert(ptr); + xact->amount_expr.reset(ptr); + read_binary_string(data, xact->amount_expr.expr); } if (*data++ == 1) { xact->cost = new amount_t; read_binary_amount(data, *xact->cost); + read_binary_string(data, xact->cost_expr); } else { xact->cost = NULL; } @@ -358,7 +388,7 @@ inline void read_binary_entry_base(char *& data, entry_base_t * entry, for (unsigned long i = 0, count = read_binary_long<unsigned long>(data); i < count; i++) { - DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); + new(xact_pool) transaction_t; read_binary_transaction(data, xact_pool); if (ignore_calculated && xact_pool->flags & TRANSACTION_CALCULATED) finalize = true; @@ -601,6 +631,9 @@ unsigned int read_binary_journal(std::istream& in, char * item_pool = new char[pool_size]; + journal->item_pool = item_pool; + journal->item_pool_end = item_pool + pool_size; + entry_t * entry_pool = (entry_t *) item_pool; transaction_t * xact_pool = (transaction_t *) (item_pool + sizeof(entry_t) * count); @@ -718,9 +751,6 @@ unsigned int read_binary_journal(std::istream& in, // Clean up and return the number of entries read - journal->item_pool = item_pool; - journal->item_pool_end = item_pool + pool_size; - delete[] accounts; delete[] commodities; delete[] data_pool; @@ -822,6 +852,30 @@ void write_binary_amount(std::ostream& out, const amount_t& amt) amt.write_quantity(out); } +void write_binary_value(std::ostream& out, const value_t& val) +{ + write_binary_long(out, (int)val.type); + + switch (val.type) { + case value_t::BOOLEAN: + write_binary_number<char>(out, *((bool *) val.data) ? 1 : 0); + break; + case value_t::INTEGER: + write_binary_long(out, *((long *) val.data)); + break; + case value_t::DATETIME: + write_binary_number(out, ((datetime_t *) val.data)->when); + break; + case value_t::AMOUNT: + write_binary_amount(out, *((amount_t *) val.data)); + break; + + case value_t::BALANCE: + case value_t::BALANCE_PAIR: + throw new error("Cannot write a balance to the binary cache"); + } +} + void write_binary_mask(std::ostream& out, mask_t * mask) { write_binary_number(out, mask->exclude); @@ -842,17 +896,12 @@ void write_binary_value_expr(std::ostream& out, const value_expr_t * expr) write_binary_value_expr(out, expr->left); switch (expr->kind) { - case value_expr_t::CONSTANT_T: - write_binary_number(out, expr->constant_t); - break; - case value_expr_t::CONSTANT_I: - write_binary_long(out, expr->constant_i); + case value_expr_t::O_ARG: + case value_expr_t::INDEX: + write_binary_long(out, expr->arg_index); break; - case value_expr_t::CONSTANT_A: - write_binary_amount(out, *(expr->constant_a)); - break; - case value_expr_t::CONSTANT_V: - assert(0); + case value_expr_t::CONSTANT: + write_binary_value(out, *expr->value); break; case value_expr_t::F_CODE_MASK: @@ -884,21 +933,30 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact, write_binary_long(out, xact->_date_eff); write_binary_long(out, xact->account->ident); - if (xact->amount_expr) { + if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) { + write_binary_number<char>(out, 0); + write_binary_amount(out, amount_t()); + } + else if (xact->amount_expr) { + write_binary_number<char>(out, 2); + write_binary_value_expr(out, xact->amount_expr.get()); + write_binary_string(out, xact->amount_expr.expr); + } + else if (! xact->amount_expr.expr.empty()) { write_binary_number<char>(out, 1); - write_binary_value_expr(out, xact->amount_expr); - } else { + write_binary_amount(out, xact->amount); + write_binary_string(out, xact->amount_expr.expr); + } + else { write_binary_number<char>(out, 0); - if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) - write_binary_amount(out, amount_t()); - else - write_binary_amount(out, xact->amount); + write_binary_amount(out, xact->amount); } if (xact->cost && (! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) { write_binary_number<char>(out, 1); write_binary_amount(out, *xact->cost); + write_binary_string(out, xact->cost_expr); } else { write_binary_number<char>(out, 0); } @@ -31,8 +31,8 @@ namespace { void xact_amount(value_t& result, const details_t& details, value_expr_t *) { if (transaction_has_xdata(*details.xact) && - transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOSITE) - result = transaction_xdata_(*details.xact).composite_amount; + transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOUND) + result = transaction_xdata_(*details.xact).value; else result = details.xact->amount; } @@ -115,8 +115,8 @@ std::string resolve_path(const std::string& path) void config_t::reset() { - ledger::amount_expr.reset(new value_expr("a")); - ledger::total_expr.reset(new value_expr("O")); + ledger::amount_expr = "@a"; + ledger::total_expr = "@O"; pricing_leeway = 24 * 3600; budget_flags = BUDGET_NO_BUDGET; @@ -126,8 +126,8 @@ void config_t::reset() wide_register_format = ("%D %-.35P %-.38A %22.108t %!22.132T\n%/" "%48|%-.38A %22.108t %!22.132T\n"); csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n"; - plot_amount_format = "%D %(S(t))\n"; - plot_total_format = "%D %(S(T))\n"; + plot_amount_format = "%D %(@S(@t))\n"; + plot_total_format = "%D %(@S(@T))\n"; print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"; write_hdr_format = "%d %Y%C%P\n"; write_xact_format = " %-34W %12o%n\n"; @@ -315,6 +315,9 @@ void config_t::process_options(const std::string& command, } } + if (command != "b" && command != "r") + amount_t::keep_base = true; + // Process remaining command-line arguments if (command != "e") { @@ -360,9 +363,9 @@ void config_t::process_options(const std::string& command, // Setup the values of %t and %T, used in format strings if (! amount_expr.empty()) - ledger::amount_expr.reset(new value_expr(amount_expr)); + ledger::amount_expr = amount_expr; if (! total_expr.empty()) - ledger::total_expr.reset(new value_expr(total_expr)); + ledger::total_expr = total_expr; // If downloading is to be supported, configure the updater @@ -1097,6 +1100,13 @@ OPT_BEGIN(period_sort, ":") { config->report_period_sort = optarg; } OPT_END(period_sort); +OPT_BEGIN(daily, "") { + if (config->report_period.empty()) + config->report_period = "daily"; + else + config->report_period = std::string("daily ") + config->report_period; +} OPT_END(daily); + OPT_BEGIN(weekly, "W") { if (config->report_period.empty()) config->report_period = "weekly"; @@ -1179,11 +1189,11 @@ OPT_BEGIN(display, "d:") { } OPT_END(display); OPT_BEGIN(amount, "t:") { - ledger::amount_expr.reset(new value_expr(optarg)); + ledger::amount_expr = optarg; } OPT_END(amount); OPT_BEGIN(total, "T:") { - ledger::total_expr.reset(new value_expr(optarg)); + ledger::total_expr = optarg; } OPT_END(total); OPT_BEGIN(amount_data, "j") { @@ -1225,25 +1235,25 @@ OPT_BEGIN(download, "Q") { } OPT_END(download); OPT_BEGIN(quantity, "O") { - ledger::amount_expr.reset(new value_expr("a")); - ledger::total_expr.reset(new value_expr("O")); + ledger::amount_expr = "@a"; + ledger::total_expr = "@O"; } OPT_END(quantity); OPT_BEGIN(basis, "B") { - ledger::amount_expr.reset(new value_expr("b")); - ledger::total_expr.reset(new value_expr("B")); + ledger::amount_expr = "@b"; + ledger::total_expr = "@B"; } OPT_END(basis); OPT_BEGIN(price, "I") { - ledger::amount_expr.reset(new value_expr("i")); - ledger::total_expr.reset(new value_expr("I")); + ledger::amount_expr = "@i"; + ledger::total_expr = "@I"; } OPT_END(price); OPT_BEGIN(market, "V") { config->show_revalued = true; - ledger::amount_expr.reset(new value_expr("v")); - ledger::total_expr.reset(new value_expr("V")); + ledger::amount_expr = "@v"; + ledger::total_expr = "@V"; } OPT_END(market); namespace { @@ -1279,34 +1289,29 @@ OPT_BEGIN(set_price, ":") { } OPT_END(set_price); OPT_BEGIN(performance, "g") { - ledger::amount_expr.reset(new value_expr("P(a,m)-b")); - ledger::total_expr.reset(new value_expr("P(O,m)-B")); + ledger::amount_expr = "@P(@a,@m)-@b"; + ledger::total_expr = "@P(@O,@m)-@B"; } OPT_END(performance); OPT_BEGIN(gain, "G") { config->show_revalued = config->show_revalued_only = true; - ledger::amount_expr.reset(new value_expr("a")); - ledger::total_expr.reset(new value_expr("G")); + ledger::amount_expr = "@a"; + ledger::total_expr = "@G"; } OPT_END(gain); OPT_BEGIN(average, "A") { - ledger::total_expr.reset - (new value_expr(expand_value_expr("A(#)", ledger::total_expr->expr))); + ledger::total_expr = expand_value_expr("@A(#)", ledger::total_expr.expr); } OPT_END(average); OPT_BEGIN(deviation, "D") { - ledger::total_expr.reset(new value_expr("O")); - ledger::total_expr.reset - (new value_expr(expand_value_expr("t-A(#)", ledger::total_expr->expr))); + ledger::total_expr = expand_value_expr("@t-@A(#)", ledger::total_expr.expr); } OPT_END(deviation); OPT_BEGIN(percentage, "%") { - ledger::total_expr.reset(new value_expr("O")); - ledger::total_expr.reset - (new value_expr(expand_value_expr("^#&{100.0%}*(#/^#)", - ledger::total_expr->expr))); + ledger::total_expr = expand_value_expr("^#&{100.0%}*(#/^#)", + ledger::total_expr.expr); } OPT_END(percentage); ////////////////////////////////////////////////////////////////////// @@ -1332,6 +1337,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = { { "comm-as-payee", 'x', false, opt_comm_as_payee, false }, { "csv-register-format", '\0', true, opt_csv_register_format, false }, { "current", 'c', false, opt_current, false }, + { "daily", '\0', false, opt_daily, false }, { "date-format", 'y', true, opt_date_format, false }, { "debug", '\0', true, opt_debug, false }, { "descend", '\0', true, opt_descend, false }, @@ -107,7 +107,7 @@ class config_t std::list<item_handler<transaction_t> *>& ptrs); }; -#define CONFIG_OPTIONS_SIZE 90 +#define CONFIG_OPTIONS_SIZE 91 extern option_t config_options[CONFIG_OPTIONS_SIZE]; void option_help(std::ostream& out); @@ -167,8 +167,9 @@ entry_t * derive_new_entry(journal_t& journal, } done: - if (! run_hooks(journal.entry_finalize_hooks, *added) || - ! added->finalize()) + if (! run_hooks(journal.entry_finalize_hooks, *added, false) || + ! added->finalize() || + ! run_hooks(journal.entry_finalize_hooks, *added, true)) throw new error("Failed to finalize derived entry (check commodities)"); return added.release(); @@ -172,7 +172,7 @@ element_t * format_t::parse_elements(const std::string& fmt) current->type = element_t::VALUE_EXPR; assert(! current->val_expr); - current->val_expr = new value_expr(std::string(b, p)); + current->val_expr = std::string(b, p); break; } @@ -275,7 +275,7 @@ static bool entry_state(const entry_t * entry, transaction_t::state_t * state) } namespace { - void mark_red(std::ostream& out, const element_t * elem) { + inline void mark_red(std::ostream& out, const element_t * elem) { out.setf(std::ios::left); out.width(0); out << "\e[31m"; @@ -289,7 +289,7 @@ namespace { out.width(elem->min_width); } - void mark_plain(std::ostream& out) { + inline void mark_plain(std::ostream& out) { out << "\e[0m"; } } @@ -317,10 +317,10 @@ void format_t::format(std::ostream& out_str, const details_t& details) const case element_t::AMOUNT: case element_t::TOTAL: case element_t::VALUE_EXPR: { - value_expr * calc = NULL; + value_expr calc; switch (elem->type) { - case element_t::AMOUNT: calc = amount_expr.get(); break; - case element_t::TOTAL: calc = total_expr.get(); break; + case element_t::AMOUNT: calc = amount_expr; break; + case element_t::TOTAL: calc = total_expr; break; case element_t::VALUE_EXPR: calc = elem->val_expr; break; default: assert(0); @@ -348,6 +348,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const } } + bool highlighted = false; + switch (value.type) { case value_t::BOOLEAN: out << (*((bool *) value.data) ? "true" : "false"); @@ -356,11 +358,15 @@ void format_t::format(std::ostream& out_str, const details_t& details) const case value_t::INTEGER: if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_invert) { - if (*((long *) value.data) > 0) + if (*((long *) value.data) > 0) { mark_red(out, elem); + highlighted = true; + } } else { - if (*((long *) value.data) < 0) + if (*((long *) value.data) < 0) { mark_red(out, elem); + highlighted = true; + } } } out << *((long *) value.data); @@ -373,11 +379,15 @@ void format_t::format(std::ostream& out_str, const details_t& details) const case value_t::AMOUNT: if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_invert) { - if (*((amount_t *) value.data) > 0) + if (*((amount_t *) value.data) > 0) { mark_red(out, elem); + highlighted = true; + } } else { - if (*((amount_t *) value.data) < 0) + if (*((amount_t *) value.data) < 0) { mark_red(out, elem); + highlighted = true; + } } } out << *((amount_t *) value.data); @@ -393,11 +403,15 @@ void format_t::format(std::ostream& out_str, const details_t& details) const if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_invert) { - if (*bal > 0) + if (*bal > 0) { mark_red(out, elem); + highlighted = true; + } } else { - if (*bal < 0) + if (*bal < 0) { mark_red(out, elem); + highlighted = true; + } } } bal->write(out, elem->min_width, @@ -411,7 +425,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const break; } - if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) + if (highlighted) mark_plain(out); break; } @@ -419,13 +433,20 @@ void format_t::format(std::ostream& out_str, const details_t& details) const case element_t::OPT_AMOUNT: if (details.xact) { std::string disp; - bool use_disp = false; + bool use_disp = false; if (details.xact->cost && details.xact->amount) { std::ostringstream stream; - stream << details.xact->amount << " @ " - << amount_t(*details.xact->cost / - details.xact->amount).unround(); + if (! details.xact->amount_expr.expr.empty()) + stream << details.xact->amount_expr.expr; + else + stream << details.xact->amount.strip_annotations(); + + if (! details.xact->cost_expr.empty()) + stream << details.xact->cost_expr; + else + stream << " @ " << amount_t(*details.xact->cost / + details.xact->amount).unround(); disp = stream.str(); use_disp = true; } @@ -450,10 +471,14 @@ void format_t::format(std::ostream& out_str, const details_t& details) const first->amount == - last->amount); } - if (! use_disp) - out << details.xact->amount; - else + if (! use_disp) { + if (! details.xact->amount_expr.expr.empty()) + out << details.xact->amount_expr.expr; + else + out << details.xact->amount.strip_annotations(); + } else { out << disp; + } } break; @@ -53,19 +53,17 @@ struct element_t std::string chars; unsigned char min_width; unsigned char max_width; - value_expr * val_expr; + value_expr val_expr; struct element_t * next; element_t() : type(STRING), flags(false), - min_width(0), max_width(0), - val_expr(NULL), next(NULL) { + min_width(0), max_width(0), next(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor element_t"); } ~element_t() { DEBUG_PRINT("ledger.memory.dtors", "dtor element_t"); - if (val_expr) delete val_expr; if (next) delete next; // recursive, but not too deep } }; @@ -17,7 +17,6 @@ transaction_t::~transaction_t() { DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t"); if (cost) delete cost; - if (amount_expr) amount_expr->release(); } std::time_t transaction_t::actual_date() const @@ -74,7 +73,7 @@ bool transaction_t::valid() const return false; } - if (flags & ~0x001f) { + if (flags & ~0x003f) { DEBUG_PRINT("ledger.validate", "transaction_t: flags are bad"); return false; } @@ -316,35 +315,41 @@ auto_entry_t::~auto_entry_t() delete predicate; } -void auto_entry_t::extend_entry(entry_base_t& entry) +void auto_entry_t::extend_entry(entry_base_t& entry, bool post) { transactions_list initial_xacts(entry.transactions.begin(), entry.transactions.end()); for (transactions_list::iterator i = initial_xacts.begin(); i != initial_xacts.end(); - i++) - if ((*predicate)(**i)) + i++) { + if ((*predicate)(**i)) { for (transactions_list::iterator t = transactions.begin(); t != transactions.end(); t++) { amount_t amt; - if (! (*t)->amount.commodity()) + if (! (*t)->amount.commodity()) { + if (! post) + continue; amt = (*i)->amount * (*t)->amount; - else + } else { + if (post) + continue; amt = (*t)->amount; + } account_t * account = (*t)->account; std::string fullname = account->fullname(); assert(! fullname.empty()); - - if (fullname == "$account") + if (fullname == "$account" || fullname == "@account") account = (*i)->account; transaction_t * xact = new transaction_t(account, amt, (*t)->flags | TRANSACTION_AUTO); entry.add_transaction(xact); } + } + } } account_t::~account_t() @@ -514,7 +519,9 @@ bool journal_t::add_entry(entry_t * entry) { entry->journal = this; - if (! run_hooks(entry_finalize_hooks, *entry) || ! entry->finalize()) { + if (! run_hooks(entry_finalize_hooks, *entry, false) || + ! entry->finalize() || + ! run_hooks(entry_finalize_hooks, *entry, true)) { entry->journal = NULL; return false; } @@ -10,6 +10,7 @@ #include "amount.h" #include "datetime.h" #include "value.h" +#include "valexpr.h" #include "error.h" #include "debug.h" #include "util.h" @@ -26,7 +27,6 @@ namespace ledger { class entry_t; class account_t; -class value_expr_t; class transaction_t { @@ -38,8 +38,9 @@ class transaction_t std::time_t _date_eff; account_t * account; amount_t amount; - value_expr_t * amount_expr; + value_expr amount_expr; amount_t * cost; + std::string cost_expr; state_t state; unsigned short flags; std::string note; @@ -53,9 +54,8 @@ class transaction_t transaction_t(account_t * _account = NULL) : entry(NULL), _date(0), _date_eff(0), account(_account), - amount_expr(NULL), cost(NULL), state(UNCLEARED), - flags(TRANSACTION_NORMAL), beg_pos(0), beg_line(0), - end_pos(0), end_line(0), data(NULL) { + cost(NULL), state(UNCLEARED), flags(TRANSACTION_NORMAL), + beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); } transaction_t(account_t * _account, @@ -63,15 +63,14 @@ class transaction_t unsigned int _flags = TRANSACTION_NORMAL, const std::string& _note = "") : entry(NULL), _date(0), _date_eff(0), account(_account), - amount(_amount), amount_expr(NULL), cost(NULL), - state(UNCLEARED), flags(_flags), note(_note), - beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) { + amount(_amount), cost(NULL), state(UNCLEARED), flags(_flags), + note(_note), beg_pos(0), beg_line(0), end_pos(0), end_line(0), + data(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); } transaction_t(const transaction_t& xact) : entry(xact.entry), _date(0), _date_eff(0), account(xact.account), - amount(xact.amount), amount_expr(NULL), - cost(xact.cost ? new amount_t(*xact.cost) : NULL), + amount(xact.amount), cost(xact.cost ? new amount_t(*xact.cost) : NULL), state(xact.state), flags(xact.flags), note(xact.note), beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); @@ -199,7 +198,7 @@ class entry_t : public entry_base_t struct entry_finalizer_t { virtual ~entry_finalizer_t() {} - virtual bool operator()(entry_t& entry) = 0; + virtual bool operator()(entry_t& entry, bool post) = 0; }; class entry_context : public error_context { @@ -238,7 +237,7 @@ public: virtual ~auto_entry_t(); - virtual void extend_entry(entry_base_t& entry); + virtual void extend_entry(entry_base_t& entry, bool post); virtual bool valid() const { return true; } @@ -248,7 +247,7 @@ class journal_t; struct auto_entry_finalizer_t : public entry_finalizer_t { journal_t * journal; auto_entry_finalizer_t(journal_t * _journal) : journal(_journal) {} - virtual bool operator()(entry_t& entry); + virtual bool operator()(entry_t& entry, bool post); }; @@ -342,12 +341,12 @@ std::ostream& operator<<(std::ostream& out, const account_t& account); struct func_finalizer_t : public entry_finalizer_t { - typedef bool (*func_t)(entry_t& entry); + typedef bool (*func_t)(entry_t& entry, bool post); func_t func; func_finalizer_t(func_t _func) : func(_func) {} func_finalizer_t(const func_finalizer_t& other) : func(other.func) {} - virtual bool operator()(entry_t& entry) { - return func(entry); + virtual bool operator()(entry_t& entry, bool post) { + return func(entry, post); } }; @@ -365,11 +364,11 @@ void remove_hook(std::list<T>& list, T obj) { } template <typename T, typename Data> -bool run_hooks(std::list<T>& list, Data& item) { +bool run_hooks(std::list<T>& list, Data& item, bool post) { for (typename std::list<T>::const_iterator i = list.begin(); i != list.end(); i++) - if (! (*(*i))(item)) + if (! (*(*i))(item, post)) return false; return true; } @@ -446,15 +445,16 @@ class journal_t bool valid() const; }; -inline void extend_entry_base(journal_t * journal, entry_base_t& entry) { +inline void extend_entry_base(journal_t * journal, entry_base_t& entry, + bool post) { for (auto_entries_list::iterator i = journal->auto_entries.begin(); i != journal->auto_entries.end(); i++) - (*i)->extend_entry(entry); + (*i)->extend_entry(entry, post); } -inline bool auto_entry_finalizer_t::operator()(entry_t& entry) { - extend_entry_base(journal, entry); +inline bool auto_entry_finalizer_t::operator()(entry_t& entry, bool post) { + extend_entry_base(journal, entry, post); return true; } @@ -100,9 +100,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[]) command = "p"; else if (command == "output") command = "w"; - else if (command == "emacs") - command = "x"; - else if (command == "lisp") + else if (command == "emacs" || command == "lisp") command = "x"; else if (command == "xml") command = "X"; @@ -119,7 +117,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[]) command = "r"; } else if (command == "parse") { - value_auto_ptr expr(ledger::parse_value_expr(*arg)); + value_expr expr(ledger::parse_value_expr(*arg)); if (config.verbose_mode) { std::cout << "Value expression tree:" << std::endl; ledger::dump_value_expr(std::cout, expr.get()); @@ -243,7 +241,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[]) // Are we handling the parse or expr commands? Do so now. if (command == "expr") { - value_auto_ptr expr(ledger::parse_value_expr(*arg)); + value_expr expr(ledger::parse_value_expr(*arg)); if (config.verbose_mode) { std::cout << "Value expression tree:" << std::endl; ledger::dump_value_expr(std::cout, expr.get()); @@ -64,6 +64,35 @@ inline char * next_element(char * buf, bool variable = false) return NULL; } +value_expr parse_amount_expr(std::istream& in, amount_t& amount, + transaction_t * xact) +{ + value_expr expr(parse_value_expr(in, NULL, PARSE_VALEXPR_RELAXED | + PARSE_VALEXPR_PARTIAL)->acquire()); + + DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << + "Parsed an amount expression"); + +#ifdef DEBUG_ENABLED + DEBUG_IF("ledger.textual.parse") { + if (_debug_stream) { + ledger::dump_value_expr(*_debug_stream, expr); + *_debug_stream << std::endl; + } + } +#endif + + if (! compute_amount(expr, amount, xact)) + throw new parse_error("Amount expression failed to compute"); + + if (expr->kind == value_expr_t::CONSTANT) + expr = NULL; + + DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << + "The transaction amount is " << xact->amount); + return expr; +} + transaction_t * parse_transaction(char * line, account_t * account, entry_t * entry = NULL) { @@ -146,53 +175,19 @@ transaction_t * parse_transaction(char * line, account_t * account, goto finished; if (p == ';') goto parse_note; - if (p == '(') { - try { - xact->amount_expr = parse_value_expr(in)->acquire(); - } - catch (error * err) { - err_desc = "While parsing transaction amount's value expression:"; - throw err; - } - DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << - "Parsed an amount expression"); -#ifdef DEBUG_ENABLED - DEBUG_IF("ledger.textual.parse") { - if (_debug_stream) { - ledger::dump_value_expr(*_debug_stream, xact->amount_expr); - *_debug_stream << std::endl; - } - } -#endif - if (! compute_amount(xact->amount_expr, xact->amount, xact.get())) - throw new parse_error("Value expression for amount failed to compute"); - DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << - "The computed amount is " << xact->amount); - } else { - xact->amount.parse(in, AMOUNT_PARSE_NO_REDUCE); - DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << - "Parsed amount " << xact->amount); - // Parse any inline math - p = peek_next_nonws(in); - while (in.good() && ! in.eof() && (p == '-' || p == '+')) { - DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << - "Parsed inline math operator " << p); - in.get(p); - amount_t temp; - temp.parse(in, AMOUNT_PARSE_NO_REDUCE); - switch (p) { - case '-': - xact->amount -= temp; - break; - case '+': - xact->amount += temp; - break; - } - DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << - "Calculated amount is " << xact->amount); - p = peek_next_nonws(in); - } + try { + unsigned long beg = (long)in.tellg(); + + xact->amount_expr = + parse_amount_expr(in, xact->amount, xact.get()); + + unsigned long end = (long)in.tellg(); + xact->amount_expr.expr = std::string(line, beg, end - beg); + } + catch (error * err) { + err_desc = "While parsing transaction amount:"; + throw err; } } @@ -215,13 +210,28 @@ transaction_t * parse_transaction(char * line, account_t * account, if (in.good() && ! in.eof()) { xact->cost = new amount_t; - p = peek_next_nonws(in); - if (p == '(') - throw new parse_error("A transaction's cost may not be a value expression"); + try { + unsigned long beg = (long)in.tellg(); - xact->cost->parse(in, AMOUNT_PARSE_NO_MIGRATE); - DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << - "Parsed cost " << *xact->cost); + if (parse_amount_expr(in, *xact->cost, xact.get())) + throw new parse_error("A transaction's cost must evalute to a constant value"); + + unsigned long end = (long)in.tellg(); + + if (per_unit) + xact->cost_expr = (std::string("@") + + std::string(line, beg, end - beg)); + else + xact->cost_expr = (std::string("@@") + + std::string(line, beg, end - beg)); + } + catch (error * err) { + err_desc = "While parsing transaction cost:"; + throw err; + } + + if (*xact->cost < 0) + throw new parse_error("A transaction's cost may not be a negative value"); amount_t per_unit_cost(*xact->cost); if (per_unit) @@ -730,7 +740,7 @@ unsigned int textual_parser_t::parse(std::istream& in, if (parse_transactions(in, account_stack.front(), *pe, "period", end_pos)) { if (pe->finalize()) { - extend_entry_base(journal, *pe); + extend_entry_base(journal, *pe, true); journal->period_entries.push_back(pe); pe->src_idx = src_idx; pe->beg_pos = beg_pos; @@ -7,8 +7,8 @@ namespace ledger { -std::auto_ptr<value_expr> amount_expr; -std::auto_ptr<value_expr> total_expr; +value_expr amount_expr; +value_expr total_expr; std::auto_ptr<scope_t> global_scope; std::time_t terminus; @@ -63,18 +63,9 @@ value_expr_t::~value_expr_t() delete mask; break; - case CONSTANT_A: - assert(constant_a); - delete constant_a; - break; - - case CONSTANT_T: - assert(constant_t); - delete constant_t; - break; - - case CONSTANT_I: - case CONSTANT_V: + case CONSTANT: + assert(value); + delete value; break; default: @@ -103,15 +94,15 @@ namespace { if (! expr) return NULL; - value_auto_ptr temp; + value_expr temp; if (expr->kind != value_expr_t::O_COM) { if (expr->kind < value_expr_t::TERMINALS) { temp.reset(expr); } else { - temp.reset(new value_expr_t(value_expr_t::CONSTANT_V)); - temp->constant_v = new value_t(); - expr->compute(*(temp->constant_v), details, context); + temp.reset(new value_expr_t(value_expr_t::CONSTANT)); + temp->value = new value_t; + expr->compute(*(temp->value), details, context); } } else { temp.reset(new value_expr_t(value_expr_t::O_COM)); @@ -146,17 +137,16 @@ void value_expr_t::compute(value_t& result, const details_t& details, { try { switch (kind) { - case CONSTANT_I: - result = constant_i; - break; - case CONSTANT_T: - result = *constant_t; - break; - case CONSTANT_A: - result = *constant_a; + case ZERO: + result = 0L; break; - case CONSTANT_V: - result = *constant_v; + + case ARG_INDEX: + throw new compute_error("Cannot directly compute an arg_index"); + + case CONSTANT: + assert(value); + result = *value; break; case F_NOW: @@ -166,8 +156,8 @@ void value_expr_t::compute(value_t& result, const details_t& details, case AMOUNT: if (details.xact) { if (transaction_has_xdata(*details.xact) && - transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOSITE) - result = transaction_xdata_(*details.xact).composite_amount; + transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOUND) + result = transaction_xdata_(*details.xact).value; else result = details.xact->amount; } @@ -184,8 +174,8 @@ void value_expr_t::compute(value_t& result, const details_t& details, bool set = false; if (transaction_has_xdata(*details.xact)) { transaction_xdata_t& xdata(transaction_xdata_(*details.xact)); - if (xdata.dflags & TRANSACTION_COMPOSITE) { - result = xdata.composite_amount.price(); + if (xdata.dflags & TRANSACTION_COMPOUND) { + result = xdata.value.price(); set = true; } } @@ -205,8 +195,8 @@ void value_expr_t::compute(value_t& result, const details_t& details, bool set = false; if (transaction_has_xdata(*details.xact)) { transaction_xdata_t& xdata(transaction_xdata_(*details.xact)); - if (xdata.dflags & TRANSACTION_COMPOSITE) { - result = xdata.composite_amount.cost(); + if (xdata.dflags & TRANSACTION_COMPOUND) { + result = xdata.value.cost(); set = true; } } @@ -353,31 +343,31 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case F_PRICE: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); result = result.price(); break; } case F_DATE: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); result = result.date(); break; } case F_DATECMP: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); result = result.date(); if (! result) break; - index = 0; - expr = find_leaf(context, 1, index); + arg_index = 0; + expr = find_leaf(context, 1, arg_index); value_t moment; expr->compute(moment, details, context); if (moment.type == value_t::DATETIME) { @@ -394,8 +384,8 @@ void value_expr_t::compute(value_t& result, const details_t& details, case F_YEAR: case F_MONTH: case F_DAY: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); // jww (2006-03-05): Generate an error if result is not a DATETIME @@ -417,8 +407,8 @@ void value_expr_t::compute(value_t& result, const details_t& details, } case F_ARITH_MEAN: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); if (details.xact && transaction_has_xdata(*details.xact)) { expr->compute(result, details, context); result /= amount_t(long(transaction_xdata_(*details.xact).index + 1)); @@ -440,24 +430,24 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case F_ABS: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); result.abs(); break; } case F_ROUND: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); result.round(); break; } case F_COMMODITY: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); if (result.type != value_t::AMOUNT) throw new compute_error("Argument to commodity() must be a commoditized amount", @@ -469,17 +459,18 @@ void value_expr_t::compute(value_t& result, const details_t& details, } case F_SET_COMMODITY: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); value_t temp; expr->compute(temp, details, context); - index = 0; - expr = find_leaf(context, 1, index); + arg_index = 0; + expr = find_leaf(context, 1, arg_index); expr->compute(result, details, context); if (result.type != value_t::AMOUNT) - throw new compute_error("Second argument to set_commodity() must be a commoditized amount", - new valexpr_context(expr)); + throw new compute_error + ("Second argument to set_commodity() must be a commoditized amount", + new valexpr_context(expr)); amount_t one("1"); one.set_commodity(((amount_t *) result.data)->commodity()); result = one; @@ -489,8 +480,8 @@ void value_expr_t::compute(value_t& result, const details_t& details, } case F_QUANTITY: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); balance_t * bal = NULL; @@ -578,10 +569,10 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case O_ARG: { - int index = 0; + int arg_index = 0; assert(left); - assert(left->kind == CONSTANT_I); - value_expr_t * expr = find_leaf(context, left->constant_i, index); + assert(left->kind == ARG_INDEX); + value_expr_t * expr = find_leaf(context, left->arg_index, arg_index); if (expr) expr->compute(result, details, context); else @@ -602,7 +593,7 @@ void value_expr_t::compute(value_t& result, const details_t& details, case O_REF: { assert(left); if (right) { - value_auto_ptr args(reduce_leaves(right, details, context)); + value_expr args(reduce_leaves(right, details, context)); left->compute(result, details, args.get()); } else { left->compute(result, details, context); @@ -611,12 +602,12 @@ void value_expr_t::compute(value_t& result, const details_t& details, } case F_VALUE: { - int index = 0; - value_expr_t * expr = find_leaf(context, 0, index); + int arg_index = 0; + value_expr_t * expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - index = 0; - expr = find_leaf(context, 1, index); + arg_index = 0; + expr = find_leaf(context, 1, arg_index); value_t moment; expr->compute(moment, details, context); if (moment.type != value_t::DATETIME) @@ -750,30 +741,64 @@ static inline void unexpected(char c, char wanted = '\0') { } } -value_expr_t * parse_value_term(std::istream& in, scope_t * scope); +value_expr_t * parse_value_term(std::istream& in, scope_t * scope, + const short flags); -inline value_expr_t * parse_value_term(const char * p, scope_t * scope) { +inline value_expr_t * parse_value_term(const char * p, scope_t * scope, + const short flags) { std::istringstream stream(p); - return parse_value_term(stream, scope); + return parse_value_term(stream, scope, flags); } -value_expr_t * parse_value_term(std::istream& in, scope_t * scope) +value_expr_t * parse_value_term(std::istream& in, scope_t * scope, + const short flags) { - value_auto_ptr node; + value_expr node; char buf[256]; char c = peek_next_nonws(in); - if (std::isdigit(c) || c == '.') { - READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.'); - if (std::strchr(buf, '.')) { - node.reset(new value_expr_t(value_expr_t::CONSTANT_A)); - node->constant_a = new amount_t; - node->constant_a->parse(buf, AMOUNT_PARSE_NO_MIGRATE); - } else { - node.reset(new value_expr_t(value_expr_t::CONSTANT_I)); - node->constant_i = std::atol(buf); + if (flags & PARSE_VALEXPR_RELAXED) { + if (c == '@') { + in.get(c); + c = peek_next_nonws(in); } + else if (! (c == '(' || c == '[' || c == '{' || c == '/')) { + amount_t temp; + char prev_c = c; + unsigned long pos = 0; + // When in relaxed parsing mode, we do want to migrate commodity + // flags, so that any precision specified by the user updates + // the current maximum precision displayed. + try { + pos = (long)in.tellg(); + temp.parse(in); + } + catch (amount_error * err) { + // If the amount had no commodity, it must be an unambiguous + // variable reference + if (std::strcmp(err->what(), "No quantity specified for amount") == 0) { + in.clear(); + in.seekg(pos, std::ios::beg); + c = prev_c; + goto parse_ident; + } else { + throw err; + } + } + node.reset(new value_expr_t(value_expr_t::CONSTANT)); + node->value = new value_t(temp); + goto parsed; + } + } + + parse_ident: + if (std::isdigit(c) || c == '.') { + READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.'); + amount_t temp; + temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE); + node.reset(new value_expr_t(value_expr_t::CONSTANT)); + node->value = new value_t(temp); goto parsed; } else if (std::isalnum(c) || c == '_') { @@ -818,7 +843,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) if (definition) { std::auto_ptr<scope_t> params(new scope_t(scope)); - int index = 0; + int arg_index = 0; if (have_args) { bool done = false; @@ -838,8 +863,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) // Define the parameter so that on lookup the parser will find // an O_ARG value. node.reset(new value_expr_t(value_expr_t::O_ARG)); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = index++; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = arg_index++; params->define(ident, node.release()); } @@ -851,20 +876,19 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) } // Define the value associated with the defined identifier - value_auto_ptr def(parse_boolean_expr(in, params.get())); + value_expr def(parse_boolean_expr(in, params.get(), flags)); if (! def.get()) throw new value_expr_error(std::string("Definition failed for '") + buf + "'"); node.reset(new value_expr_t(value_expr_t::O_DEF)); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = index; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = arg_index; node->set_right(def.release()); scope->define(buf, node.release()); // Returning a dummy value in place of the definition - node.reset(new value_expr_t(value_expr_t::CONSTANT_I)); - node->constant_i = 0; + node.reset(new value_expr_t(value_expr_t::ZERO)); } else { assert(scope); value_expr_t * def = scope->lookup(buf); @@ -873,7 +897,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) (buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' || buf[0] == 'w' || buf[0] == 'W' || buf[0] == 'e')) goto find_term; - throw new value_expr_error(std::string("Unknown identifier '") + buf + "'"); + throw new value_expr_error(std::string("Unknown identifier '") + + buf + "'"); } else if (def->kind == value_expr_t::O_DEF) { node.reset(new value_expr_t(value_expr_t::O_REF)); @@ -883,7 +908,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) if (have_args) { in.clear(); in.seekg(beg, std::ios::beg); - value_auto_ptr args(parse_value_expr(in, scope, true)); + value_expr args + (parse_value_expr(in, scope, flags | PARSE_VALEXPR_PARTIAL)); if (peek_next_nonws(in) != ')') { in.get(c); @@ -897,11 +923,10 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) } } - if (count != def->left->constant_i) { + if (count != def->left->arg_index) { std::ostringstream errmsg; errmsg << "Wrong number of arguments to '" << buf - << "': saw " << count - << ", wanted " << def->left->constant_i; + << "': saw " << count << ", wanted " << def->left->arg_index; throw new value_expr_error(errmsg.str()); } } @@ -918,7 +943,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) // Functions case '^': node.reset(new value_expr_t(value_expr_t::F_PARENT)); - node->set_left(parse_value_term(in, scope)); + node->set_left(parse_value_term(in, scope, flags)); break; // Other @@ -979,34 +1004,21 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) } case '{': { - int paren_depth = 0; - int i = 0; - while (i < 255 && ! in.eof()) { - in.get(c); - if (c == '{') { - paren_depth++; - } - else if (c == '}') { - if (paren_depth == 0) - break; - paren_depth--; - } - buf[i++] = c; - } - buf[i] = '\0'; - + amount_t temp; + temp.parse(in, AMOUNT_PARSE_NO_MIGRATE); + in.get(c); if (c != '}') unexpected(c, '}'); - node.reset(new value_expr_t(value_expr_t::CONSTANT_A)); - node->constant_a = new amount_t; - node->constant_a->parse(buf, AMOUNT_PARSE_NO_MIGRATE); + node.reset(new value_expr_t(value_expr_t::CONSTANT)); + node->value = new value_t(temp); break; } case '(': { std::auto_ptr<scope_t> locals(new scope_t(scope)); - node.reset(parse_value_expr(in, locals.get(), true)); + node.reset(parse_value_expr(in, locals.get(), + flags | PARSE_VALEXPR_PARTIAL)); in.get(c); if (c != ')') unexpected(c, ')'); @@ -1019,10 +1031,9 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) unexpected(c, ']'); in.get(c); - node.reset(new value_expr_t(value_expr_t::CONSTANT_T)); - + node.reset(new value_expr_t(value_expr_t::CONSTANT)); interval_t timespan(buf); - node->constant_t = new datetime_t(timespan.first()); + node->value = new value_t(datetime_t(timespan.first())); break; } @@ -1035,19 +1046,20 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope) return node.release(); } -value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope) +value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope, + const short flags) { - value_auto_ptr node; + value_expr node; if (peek_next_nonws(in) == '%') { char c; in.get(c); node.reset(new value_expr_t(value_expr_t::O_PERC)); - node->set_left(parse_value_term(in, scope)); + node->set_left(parse_value_term(in, scope, flags)); return node.release(); } - node.reset(parse_value_term(in, scope)); + node.reset(parse_value_term(in, scope, flags)); if (node.get() && ! in.eof()) { char c = peek_next_nonws(in); @@ -1055,18 +1067,18 @@ value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope) in.get(c); switch (c) { case '*': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_MUL)); node->set_left(prev.release()); - node->set_right(parse_value_term(in, scope)); + node->set_right(parse_value_term(in, scope, flags)); break; } case '/': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_DIV)); node->set_left(prev.release()); - node->set_right(parse_value_term(in, scope)); + node->set_right(parse_value_term(in, scope, flags)); break; } } @@ -1077,20 +1089,17 @@ value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope) return node.release(); } -value_expr_t * parse_add_expr(std::istream& in, scope_t * scope) +value_expr_t * parse_add_expr(std::istream& in, scope_t * scope, + const short flags) { - value_auto_ptr node; + value_expr node; if (peek_next_nonws(in) == '-') { char c; in.get(c); - value_auto_ptr expr(parse_mul_expr(in, scope)); - if (expr->kind == value_expr_t::CONSTANT_I) { - expr->constant_i = - expr->constant_i; - return expr.release(); - } - else if (expr->kind == value_expr_t::CONSTANT_A) { - expr->constant_a->negate(); + value_expr expr(parse_mul_expr(in, scope, flags)); + if (expr->kind == value_expr_t::CONSTANT) { + expr->value->negate(); return expr.release(); } node.reset(new value_expr_t(value_expr_t::O_NEG)); @@ -1098,7 +1107,7 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope) return node.release(); } - node.reset(parse_mul_expr(in, scope)); + node.reset(parse_mul_expr(in, scope, flags)); if (node.get() && ! in.eof()) { char c = peek_next_nonws(in); @@ -1106,18 +1115,18 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope) in.get(c); switch (c) { case '+': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_ADD)); node->set_left(prev.release()); - node->set_right(parse_mul_expr(in, scope)); + node->set_right(parse_mul_expr(in, scope, flags)); break; } case '-': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_SUB)); node->set_left(prev.release()); - node->set_right(parse_mul_expr(in, scope)); + node->set_right(parse_mul_expr(in, scope, flags)); break; } } @@ -1128,19 +1137,20 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope) return node.release(); } -value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope) +value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope, + const short flags) { - value_auto_ptr node; + value_expr node; if (peek_next_nonws(in) == '!') { char c; in.get(c); node.reset(new value_expr_t(value_expr_t::O_NOT)); - node->set_left(parse_add_expr(in, scope)); + node->set_left(parse_add_expr(in, scope, flags)); return node.release(); } - node.reset(parse_add_expr(in, scope)); + node.reset(parse_add_expr(in, scope, flags)); if (node.get() && ! in.eof()) { char c = peek_next_nonws(in); @@ -1154,35 +1164,35 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope) in.get(c); else unexpected(c, '='); - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(negate ? value_expr_t::O_NEQ : value_expr_t::O_EQ)); node->set_left(prev.release()); - node->set_right(parse_add_expr(in, scope)); + node->set_right(parse_add_expr(in, scope, flags)); break; } case '<': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_LT)); if (peek_next_nonws(in) == '=') { in.get(c); node->kind = value_expr_t::O_LTE; } node->set_left(prev.release()); - node->set_right(parse_add_expr(in, scope)); + node->set_right(parse_add_expr(in, scope, flags)); break; } case '>': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_GT)); if (peek_next_nonws(in) == '=') { in.get(c); node->kind = value_expr_t::O_GTE; } node->set_left(prev.release()); - node->set_right(parse_add_expr(in, scope)); + node->set_right(parse_add_expr(in, scope, flags)); break; } @@ -1197,9 +1207,10 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope) return node.release(); } -value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope) +value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope, + const short flags) { - value_auto_ptr node(parse_logic_expr(in, scope)); + value_expr node(parse_logic_expr(in, scope, flags)); if (node.get() && ! in.eof()) { char c = peek_next_nonws(in); @@ -1207,32 +1218,32 @@ value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope) in.get(c); switch (c) { case '&': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_AND)); node->set_left(prev.release()); - node->set_right(parse_logic_expr(in, scope)); + node->set_right(parse_logic_expr(in, scope, flags)); break; } case '|': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_OR)); node->set_left(prev.release()); - node->set_right(parse_logic_expr(in, scope)); + node->set_right(parse_logic_expr(in, scope, flags)); break; } case '?': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_QUES)); node->set_left(prev.release()); node->set_right(new value_expr_t(value_expr_t::O_COL)); - node->right->set_left(parse_logic_expr(in, scope)); + node->right->set_left(parse_logic_expr(in, scope, flags)); c = peek_next_nonws(in); if (c != ':') unexpected(c, ':'); in.get(c); - node->right->set_right(parse_logic_expr(in, scope)); + node->right->set_right(parse_logic_expr(in, scope, flags)); break; } @@ -1331,43 +1342,43 @@ void init_value_expr() // Functions node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_ABS)); globals->define("U", node); globals->define("abs", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_ROUND)); globals->define("round", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_QUANTITY)); globals->define("S", node); globals->define("quant", node); globals->define("quantity", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_COMMODITY)); globals->define("comm", node); globals->define("commodity", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 2; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 2; node->set_right(new value_expr_t(value_expr_t::F_SET_COMMODITY)); globals->define("setcomm", node); globals->define("set_commodity", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_ARITH_MEAN)); globals->define("A", node); globals->define("avg", node); @@ -1375,87 +1386,87 @@ void init_value_expr() globals->define("average", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 2; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 2; node->set_right(new value_expr_t(value_expr_t::F_VALUE)); globals->define("P", node); - parse_value_definition("value=P(t,m)", globals); - parse_value_definition("total_value=P(T,m)", globals); - parse_value_definition("valueof(x)=P(x,m)", globals); - parse_value_definition("datedvalueof(x,y)=P(x,y)", globals); + parse_value_definition("@value=@P(@t,@m)", globals); + parse_value_definition("@total_value=@P(@T,@m)", globals); + parse_value_definition("@valueof(x)=@P(@x,@m)", globals); + parse_value_definition("@datedvalueof(x,y)=@P(@x,@y)", globals); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_PRICE)); globals->define("priceof", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_DATE)); globals->define("dateof", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 2; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 2; node->set_right(new value_expr_t(value_expr_t::F_DATECMP)); globals->define("datecmp", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_YEAR)); globals->define("yearof", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_MONTH)); globals->define("monthof", node); node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::CONSTANT_I)); - node->left->constant_i = 1; + node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); + node->left->arg_index = 1; node->set_right(new value_expr_t(value_expr_t::F_DAY)); globals->define("dayof", node); - parse_value_definition("year=yearof(d)", globals); - parse_value_definition("month=monthof(d)", globals); - parse_value_definition("day=dayof(d)", globals); + parse_value_definition("@year=@yearof(@d)", globals); + parse_value_definition("@month=@monthof(@d)", globals); + parse_value_definition("@day=@dayof(@d)", globals); // Macros - node = parse_value_expr("P(a,d)"); + node = parse_value_expr("@P(@a,@d)"); globals->define("v", node); globals->define("market", node); - node = parse_value_expr("P(O,d)"); + node = parse_value_expr("@P(@O,@d)"); globals->define("V", node); globals->define("total_market", node); - node = parse_value_expr("v-b"); + node = parse_value_expr("@v-@b"); globals->define("g", node); globals->define("gain", node); - node = parse_value_expr("V-B"); + node = parse_value_expr("@V-@B"); globals->define("G", node); globals->define("total_gain", node); - parse_value_definition("min(x,y)=x<y?x:y", globals); - parse_value_definition("max(x,y)=x>y?x:y", globals); + parse_value_definition("@min(x,y)=@x<@y?@x:@y", globals); + parse_value_definition("@max(x,y)=@x>@y?@x:@y", globals); } value_expr_t * parse_value_expr(std::istream& in, scope_t * scope, - const bool partial) + const short flags) { if (! global_scope.get()) init_value_expr(); std::auto_ptr<scope_t> this_scope(new scope_t(scope ? scope : global_scope.get())); - value_auto_ptr node; - node.reset(parse_boolean_expr(in, this_scope.get())); + value_expr node; + node.reset(parse_boolean_expr(in, this_scope.get(), flags)); if (node.get() && ! in.eof()) { char c = peek_next_nonws(in); @@ -1463,10 +1474,10 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope, in.get(c); switch (c) { case ',': { - value_auto_ptr prev(node.release()); + value_expr prev(node.release()); node.reset(new value_expr_t(value_expr_t::O_COM)); node->set_left(prev.release()); - node->set_right(parse_logic_expr(in, this_scope.get())); + node->set_right(parse_logic_expr(in, this_scope.get(), flags)); break; } @@ -1486,7 +1497,7 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope, throw new value_expr_error(std::string("Failed to parse value expression")); else unexpected(c); - } else if (! partial) { + } else if (! (flags & PARSE_VALEXPR_PARTIAL)) { in.get(c); if (! in.eof()) unexpected(c); @@ -1499,7 +1510,7 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope, valexpr_context::valexpr_context(const ledger::value_expr_t * _expr, const std::string& desc) throw() - : expr(NULL), error_node(_expr), error_context(desc) + : expr(_expr), error_node(_expr), error_context(desc) { error_node->acquire(); } @@ -1513,7 +1524,7 @@ valexpr_context::~valexpr_context() throw() void valexpr_context::describe(std::ostream& out) const throw() { if (! expr) { - out << "Valexpr_context expr not set!" << std::endl; + out << "valexpr_context expr not set!" << std::endl; return; } @@ -1524,7 +1535,8 @@ void valexpr_context::describe(std::ostream& out) const throw() unsigned long start = (long)out.tellp() - 1; unsigned long begin; unsigned long end; - bool found = ledger::write_value_expr(out, expr, error_node, &begin, &end); + bool found = ledger::write_value_expr(out, expr, true, + error_node, &begin, &end); out << std::endl; if (found) { out << " "; @@ -1540,132 +1552,122 @@ void valexpr_context::describe(std::ostream& out) const throw() bool write_value_expr(std::ostream& out, const value_expr_t * node, + const bool relaxed, const value_expr_t * node_to_find, unsigned long * start_pos, unsigned long * end_pos) { + int arg_index = 0; bool found = false; + value_expr_t * expr; if (start_pos && node == node_to_find) { *start_pos = (long)out.tellp() - 1; found = true; } + std::string symbol; + switch (node->kind) { - case value_expr_t::CONSTANT_I: - out << node->constant_i; - break; - case value_expr_t::CONSTANT_T: - out << "[" << *(node->constant_t) << ']'; - break; - case value_expr_t::CONSTANT_A: - out << "{" << *(node->constant_a) << '}'; - break; - case value_expr_t::CONSTANT_V: - out << "{" << *(node->constant_v) << '}'; - break; - - case value_expr_t::AMOUNT: out << "amount"; break; - case value_expr_t::PRICE: out << "price"; break; - case value_expr_t::COST: out << "cost"; break; - case value_expr_t::DATE: out << "date"; break; - case value_expr_t::ACT_DATE: out << "actual_date"; break; - case value_expr_t::EFF_DATE: out << "effective_date"; break; - case value_expr_t::CLEARED: out << "cleared"; break; - case value_expr_t::PENDING: out << "pending"; break; - case value_expr_t::REAL: out << "real"; break; - case value_expr_t::ACTUAL: out << "actual"; break; - case value_expr_t::INDEX: out << "index"; break; - case value_expr_t::COUNT: out << "count"; break; - case value_expr_t::DEPTH: out << "depth"; break; - case value_expr_t::TOTAL: out << "total"; break; - case value_expr_t::PRICE_TOTAL: out << "total_price"; break; - case value_expr_t::COST_TOTAL: out << "total_cost"; break; - case value_expr_t::F_NOW: out << "now"; break; + case value_expr_t::ZERO: + out << '0'; + break; + case value_expr_t::ARG_INDEX: + break; + + case value_expr_t::CONSTANT: + switch (node->value->type) { + case value_t::BOOLEAN: + assert(0); + break; + case value_t::DATETIME: + out << '[' << *(node->value) << ']'; + break; + case value_t::INTEGER: + case value_t::AMOUNT: + if (! relaxed) + out << '{'; + out << *(node->value); + if (! relaxed) + out << '}'; + break; + case value_t::BALANCE: + case value_t::BALANCE_PAIR: + assert(0); + break; + } + break; + + case value_expr_t::AMOUNT: + symbol = "amount"; break; + case value_expr_t::PRICE: + symbol = "price"; break; + case value_expr_t::COST: + symbol = "cost"; break; + case value_expr_t::DATE: + symbol = "date"; break; + case value_expr_t::ACT_DATE: + symbol = "actual_date"; break; + case value_expr_t::EFF_DATE: + symbol = "effective_date"; break; + case value_expr_t::CLEARED: + symbol = "cleared"; break; + case value_expr_t::PENDING: + symbol = "pending"; break; + case value_expr_t::REAL: + symbol = "real"; break; + case value_expr_t::ACTUAL: + symbol = "actual"; break; + case value_expr_t::INDEX: + symbol = "index"; break; + case value_expr_t::COUNT: + symbol = "count"; break; + case value_expr_t::DEPTH: + symbol = "depth"; break; + case value_expr_t::TOTAL: + symbol = "total"; break; + case value_expr_t::PRICE_TOTAL: + symbol = "total_price"; break; + case value_expr_t::COST_TOTAL: + symbol = "total_cost"; break; + case value_expr_t::F_NOW: + symbol = "now"; break; case value_expr_t::VALUE_EXPR: - if (write_value_expr(out, amount_expr->parsed, + if (write_value_expr(out, amount_expr.get(), relaxed, node_to_find, start_pos, end_pos)) found = true; break; case value_expr_t::TOTAL_EXPR: - if (write_value_expr(out, total_expr->parsed, + if (write_value_expr(out, total_expr.get(), relaxed, node_to_find, start_pos, end_pos)) found = true; break; case value_expr_t::F_ARITH_MEAN: - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_ABS: out << "abs"; break; - out << "abs("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_QUANTITY: out << "quantity"; break; - out << "quantity("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_COMMODITY: out << "commodity"; break; - out << "commodity("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_SET_COMMODITY: out << "set_commodity"; break; - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_VALUE: out << "valueof"; break; - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_PRICE: out << "priceof"; break; - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_DATE: out << "dateof"; break; - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_DATECMP: out << "datecmp"; break; - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_YEAR: out << "yearof"; break; - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_MONTH: out << "monthof"; break; - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::F_DAY: out << "dayof"; break; - out << "average("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; + symbol = "average"; break; + case value_expr_t::F_ABS: + symbol = "abs"; break; + case value_expr_t::F_QUANTITY: + symbol = "quantity"; break; + case value_expr_t::F_COMMODITY: + symbol = "commodity"; break; + case value_expr_t::F_SET_COMMODITY: + symbol = "set_commodity"; break; + case value_expr_t::F_VALUE: + symbol = "valueof"; break; + case value_expr_t::F_PRICE: + symbol = "priceof"; break; + case value_expr_t::F_DATE: + symbol = "dateof"; break; + case value_expr_t::F_DATECMP: + symbol = "datecmp"; break; + case value_expr_t::F_YEAR: + symbol = "yearof"; break; + case value_expr_t::F_MONTH: + symbol = "monthof"; break; + case value_expr_t::F_DAY: + symbol = "dayof"; break; case value_expr_t::F_CODE_MASK: out << "c/" << node->mask->pattern << "/"; @@ -1688,160 +1690,169 @@ bool write_value_expr(std::ostream& out, case value_expr_t::O_NOT: out << "!"; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; break; case value_expr_t::O_NEG: out << "-"; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; break; case value_expr_t::O_PERC: out << "%"; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; break; case value_expr_t::O_ARG: - out << "arg" << node->constant_i; + out << "@arg" << node->arg_index; break; case value_expr_t::O_DEF: out << "O_DEF"; break; + case value_expr_t::O_REF: + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) + found = true; + if (node->right) { + out << "("; + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) + found = true; + out << ")"; + } break; case value_expr_t::O_COM: - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ", "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; break; case value_expr_t::O_QUES: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " ? "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_COL: - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " : "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; break; - case value_expr_t::O_AND: out << "O_AND"; break; + case value_expr_t::O_AND: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " & "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; - case value_expr_t::O_OR: out << "O_OR"; break; + case value_expr_t::O_OR: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " | "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_NEQ: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " != "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_EQ: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " == "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_LT: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " < "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_LTE: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " <= "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_GT: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " > "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_GTE: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " >= "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_ADD: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " + "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_SUB: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " - "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_MUL: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " * "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; case value_expr_t::O_DIV: out << "("; - if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) found = true; out << " / "; - if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos)) + if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) found = true; out << ")"; break; @@ -1852,6 +1863,12 @@ bool write_value_expr(std::ostream& out, break; } + if (! symbol.empty()) { + if (commodity_t::find(symbol)) + out << '@'; + out << symbol; + } + if (end_pos && node == node_to_find) *end_pos = (long)out.tellp() - 1; @@ -1869,17 +1886,14 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node, out << " "; switch (node->kind) { - case value_expr_t::CONSTANT_I: - out << "CONSTANT_I - " << node->constant_i; - break; - case value_expr_t::CONSTANT_T: - out << "CONSTANT_T - [" << *(node->constant_t) << ']'; + case value_expr_t::ZERO: + out << "ZERO"; break; - case value_expr_t::CONSTANT_A: - out << "CONSTANT_A - {" << *(node->constant_a) << '}'; + case value_expr_t::ARG_INDEX: + out << "ARG_INDEX - " << node->arg_index; break; - case value_expr_t::CONSTANT_V: - out << "CONSTANT_V - {" << *(node->constant_v) << '}'; + case value_expr_t::CONSTANT: + out << "CONSTANT - " << *(node->value); break; case value_expr_t::AMOUNT: out << "AMOUNT"; break; @@ -1,7 +1,6 @@ #ifndef _VALEXPR_H #define _VALEXPR_H -#include "journal.h" #include "value.h" #include "error.h" #include "mask.h" @@ -10,6 +9,10 @@ namespace ledger { +class entry_t; +class transaction_t; +class account_t; + struct details_t { const entry_t * entry; @@ -37,10 +40,9 @@ struct value_expr_t { enum kind_t { // Constants - CONSTANT_I, - CONSTANT_T, - CONSTANT_A, - CONSTANT_V, + CONSTANT, + ARG_INDEX, + ZERO, CONSTANTS, @@ -125,11 +127,9 @@ struct value_expr_t value_expr_t * left; union { - datetime_t * constant_t; - long constant_i; - amount_t * constant_a; - value_t * constant_v; + value_t * value; mask_t * mask; + unsigned int arg_index; // used by ARG_INDEX and O_ARG value_expr_t * right; }; @@ -137,9 +137,6 @@ struct value_expr_t : kind(_kind), refc(0), left(NULL), right(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t " << this); } - value_expr_t(const value_expr_t&) { - DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t (copy) " << this); - } ~value_expr_t(); void release() const { @@ -180,12 +177,18 @@ struct value_expr_t void compute(value_t& result, const details_t& details = details_t(), value_expr_t * context = NULL) const; + value_t compute(const details_t& details = details_t(), value_expr_t * context = NULL) const { value_t temp; compute(temp, details, context); return temp; } + + private: + value_expr_t(const value_expr_t&) { + DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t (copy) " << this); + } }; class valexpr_context : public error_context { @@ -273,37 +276,21 @@ bool compute_amount(value_expr_t * expr, amount_t& amt, const transaction_t * xact, value_expr_t * context = NULL); -struct scope_t; -value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope); - -inline value_expr_t * parse_boolean_expr(const std::string& str, - scope_t * scope = NULL) { - std::istringstream stream(str); - try { - return parse_boolean_expr(stream, scope); - } - catch (error * err) { - err->context.push_back - (new error_context("While parsing value expression: " + str)); - throw err; - } -} - -inline value_expr_t * parse_boolean_expr(const char * p, - scope_t * scope = NULL) { - return parse_boolean_expr(std::string(p), scope); -} +#define PARSE_VALEXPR_NORMAL 0x00 +#define PARSE_VALEXPR_PARTIAL 0x01 +#define PARSE_VALEXPR_RELAXED 0x02 value_expr_t * parse_value_expr(std::istream& in, scope_t * scope = NULL, - const bool partial = false); + const short flags = PARSE_VALEXPR_RELAXED); -inline value_expr_t * parse_value_expr(const std::string& str, - scope_t * scope = NULL, - const bool partial = false) { +inline value_expr_t * +parse_value_expr(const std::string& str, + scope_t * scope = NULL, + const short flags = PARSE_VALEXPR_RELAXED) { std::istringstream stream(str); try { - return parse_value_expr(stream, scope, partial); + return parse_value_expr(stream, scope, flags); } catch (error * err) { err->context.push_back @@ -313,10 +300,11 @@ inline value_expr_t * parse_value_expr(const std::string& str, } } -inline value_expr_t * parse_value_expr(const char * p, - scope_t * scope = NULL, - const bool partial = false) { - return parse_value_expr(std::string(p), scope, partial); +inline value_expr_t * +parse_value_expr(const char * p, + scope_t * scope = NULL, + const short flags = PARSE_VALEXPR_RELAXED) { + return parse_value_expr(std::string(p), scope, flags); } void dump_value_expr(std::ostream& out, const value_expr_t * node, @@ -324,6 +312,7 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node, bool write_value_expr(std::ostream& out, const value_expr_t * node, + const bool relaxed = true, const value_expr_t * node_to_find = NULL, unsigned long * start_pos = NULL, unsigned long * end_pos = NULL); @@ -359,27 +348,70 @@ inline value_t guarded_compute(const value_expr_t * expr, } ////////////////////////////////////////////////////////////////////// -// -// This class is used so that during the "in between" stages of value -// expression parsing -- while no one yet holds a reference to the -// value_expr_t object -- we can be assured of deletion should an -// exception happen to whip by. -struct value_auto_ptr { +class value_expr +{ value_expr_t * ptr; - value_auto_ptr() : ptr(NULL) {} - explicit value_auto_ptr(value_expr_t * _ptr) - : ptr(_ptr ? _ptr->acquire() : NULL) {} - ~value_auto_ptr() { +public: + std::string expr; + + value_expr() : ptr(NULL) {} + + value_expr(const std::string& _expr) : expr(_expr) { + DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); + if (! _expr.empty()) + ptr = parse_value_expr(expr)->acquire(); + else + ptr = NULL; + } + value_expr(value_expr_t * _ptr) + : ptr(_ptr ? _ptr->acquire(): NULL) { + DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); + } + value_expr(const value_expr& other) + : ptr(other.ptr ? other.ptr->acquire() : NULL), + expr(other.expr) { + DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); + } + virtual ~value_expr() { + DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr"); if (ptr) ptr->release(); } + + value_expr& operator=(const std::string& _expr) { + expr = _expr; + reset(parse_value_expr(expr)); + return *this; + } + value_expr& operator=(value_expr_t * _expr) { + expr = ""; + reset(_expr); + return *this; + } + value_expr& operator=(const value_expr& _expr) { + expr = _expr.expr; + reset(_expr.get()); + return *this; + } + + operator bool() const throw() { + return ptr != NULL; + } + operator std::string() const throw() { + return expr; + } + operator value_expr_t *() const throw() { + return ptr; + } + value_expr_t& operator*() const throw() { return *ptr; } value_expr_t * operator->() const throw() { return ptr; } + value_expr_t * get() const throw() { return ptr; } value_expr_t * release() throw() { value_expr_t * tmp = ptr; @@ -390,41 +422,19 @@ struct value_auto_ptr { if (p != ptr) { if (ptr) ptr->release(); - ptr = p->acquire(); + ptr = p ? p->acquire() : NULL; } } -}; - -////////////////////////////////////////////////////////////////////// - -class value_expr -{ - value_expr_t * parsed; -public: - std::string expr; - - value_expr(const std::string& _expr) : expr(_expr) { - DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); - parsed = parse_value_expr(expr)->acquire(); - } - value_expr(value_expr_t * _parsed) : parsed(_parsed->acquire()) { - DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr"); - } - virtual ~value_expr() { - DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr"); - if (parsed) - parsed->release(); - } virtual void compute(value_t& result, const details_t& details = details_t(), value_expr_t * context = NULL) { - guarded_compute(parsed, result, details, context); + guarded_compute(ptr, result, details, context); } virtual value_t compute(const details_t& details = details_t(), value_expr_t * context = NULL) { value_t temp; - guarded_compute(parsed, temp, details, context); + guarded_compute(ptr, temp, details, context); return temp; } @@ -435,35 +445,40 @@ public: unsigned long * end_pos); }; -extern std::auto_ptr<value_expr> amount_expr; -extern std::auto_ptr<value_expr> total_expr; +extern value_expr amount_expr; +extern value_expr total_expr; inline void compute_amount(value_t& result, const details_t& details = details_t()) { - if (amount_expr.get()) + if (amount_expr) amount_expr->compute(result, details); } inline value_t compute_amount(const details_t& details = details_t()) { - if (amount_expr.get()) + if (amount_expr) return amount_expr->compute(details); } inline void compute_total(value_t& result, const details_t& details = details_t()) { - if (total_expr.get()) + if (total_expr) total_expr->compute(result, details); } inline value_t compute_total(const details_t& details = details_t()) { - if (total_expr.get()) + if (total_expr) return total_expr->compute(details); } +value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope, + const short flags); + inline void parse_value_definition(const std::string& str, scope_t * scope = NULL) { - value_auto_ptr expr - (parse_boolean_expr(str, scope ? scope : global_scope.get())); + std::istringstream def(str); + value_expr expr + (parse_boolean_expr(def, scope ? scope : global_scope.get(), + PARSE_VALEXPR_RELAXED)); } ////////////////////////////////////////////////////////////////////// @@ -39,8 +39,8 @@ transaction_xdata_t& transaction_xdata(const transaction_t& xact) void add_transaction_to(const transaction_t& xact, value_t& value) { if (transaction_has_xdata(xact) && - transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) { - value += transaction_xdata_(xact).composite_amount; + transaction_xdata_(xact).dflags & TRANSACTION_COMPOUND) { + value += transaction_xdata_(xact).value; } else if (xact.cost || ! value.realzero()) { value.add(xact.amount, xact.cost); @@ -162,8 +162,8 @@ void calc_transactions::operator()(transaction_t& xact) void invert_transactions::operator()(transaction_t& xact) { if (transaction_has_xdata(xact) && - transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) { - transaction_xdata_(xact).composite_amount.negate(); + transaction_xdata_(xact).dflags & TRANSACTION_COMPOUND) { + transaction_xdata_(xact).value.negate(); } else { xact.amount.negate(); if (xact.cost) @@ -209,29 +209,31 @@ void handle_value(const value_t& value, transaction_xdata_t& xdata(transaction_xdata(xact)); + if (date) + xdata.date = date; + if (flags) + xdata.dflags |= flags; + + value_t temp(value); + switch (value.type) { case value_t::BOOLEAN: - xact.amount = *((bool *) value.data); - break; + case value_t::DATETIME: case value_t::INTEGER: - xact.amount = *((long *) value.data); - break; + temp.cast(value_t::AMOUNT); + // fall through... + case value_t::AMOUNT: - xact.amount = *((amount_t *) value.data); + xact.amount = *((amount_t *) temp.data); break; case value_t::BALANCE: case value_t::BALANCE_PAIR: - xdata.composite_amount = value; - flags |= TRANSACTION_COMPOSITE; + xdata.value = temp; + flags |= TRANSACTION_COMPOUND; break; } - if (date) - xdata.date = date; - if (flags) - xdata.dflags |= flags; - handler(xact); } @@ -830,7 +832,7 @@ void walk_accounts(account_t& account, const std::string& sort_string) { if (! sort_string.empty()) { - value_auto_ptr sort_order; + value_expr sort_order; sort_order.reset(parse_value_expr(sort_string)); walk_accounts(account, handler, sort_order.get()); } else { @@ -80,14 +80,14 @@ bool compare_items<account_t>::operator()(const account_t * left, #define TRANSACTION_DISPLAYED 0x0008 #define TRANSACTION_NO_TOTAL 0x0010 #define TRANSACTION_SORT_CALC 0x0020 -#define TRANSACTION_COMPOSITE 0x0040 +#define TRANSACTION_COMPOUND 0x0040 #define TRANSACTION_MATCHES 0x0080 struct transaction_xdata_t { value_t total; value_t sort_value; - value_t composite_amount; + value_t value; unsigned int index; unsigned short dflags; std::time_t date; @@ -435,9 +435,9 @@ void format_xml_entries::format_last_entry() } output_stream << " <tr:amount>\n"; - if (transaction_xdata_(**i).dflags & TRANSACTION_COMPOSITE) + if (transaction_xdata_(**i).dflags & TRANSACTION_COMPOUND) xml_write_value(output_stream, - transaction_xdata_(**i).composite_amount, 10); + transaction_xdata_(**i).value, 10); else xml_write_value(output_stream, value_t((*i)->amount), 10); output_stream << " </tr:amount>\n"; |