diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | autoxact.cc | 12 | ||||
-rw-r--r-- | autoxact.h | 14 | ||||
-rw-r--r-- | balance.h | 2 | ||||
-rw-r--r-- | binary.cc | 48 | ||||
-rw-r--r-- | error.h | 9 | ||||
-rw-r--r-- | format.h | 26 | ||||
-rw-r--r-- | ledger.h | 6 | ||||
-rw-r--r-- | main.cc | 214 | ||||
-rw-r--r-- | textual.cc | 29 | ||||
-rw-r--r-- | valexpr.cc | 96 | ||||
-rw-r--r-- | valexpr.h | 29 | ||||
-rw-r--r-- | walk.cc | 2 | ||||
-rw-r--r-- | walk.h | 20 |
15 files changed, 273 insertions, 253 deletions
@@ -4,6 +4,7 @@ CODE = account.cc \ balance.cc \ binary.cc \ datetime.cc \ + debug.cc \ error.cc \ format.cc \ ledger.cc \ @@ -17,9 +18,9 @@ OBJS = $(patsubst %.cc,%.o,$(CODE)) CXX = g++ CFLAGS = -Wall -ansi -pedantic -#DFLAGS = -O3 -fomit-frame-pointer -DFLAGS = -g -DDEBUG=1 -#DFLAGS = -g -DDEBUG=1 -pg +#DFLAGS = -O3 -fomit-frame-pointer -DRELEASE_LEVEL=0 +DFLAGS = -g -DRELEASE_LEVEL=4 +#DFLAGS = -g -DRELEASE_LEVEL=2 -pg INCS = -I/sw/include \ -I/usr/include/gcc/darwin/3.3/c++ \ @@ -54,7 +54,7 @@ -W Report the trend, with older values affecting the trend less -X Report expected amount for the next transaction -- Amount expressions are now supported, where the totals reported can +- Value expressions are now supported, where the totals reported can be changed using -t and -T and an expression string composed of: a amount @@ -142,6 +142,16 @@ %?10d %?-.20p %-.22a %12.66t %12.80T %20T %-a +- Automated transactions now use a single value expression as a + predicate. This means the new syntax is: + + = VALUE-EXPR + TRANSACTIONS... + + Only one VALUE-EXPR is supported, compared to the multiple account + regexps supported before. By using a VALUE-EXPR as a predicate, + matching may now be much more comprehensive and selective. + * 1.7 (never released) - Pricing histories are now supported, so that ledger remembers diff --git a/autoxact.cc b/autoxact.cc index c49d1d38..7dac4512 100644 --- a/autoxact.cc +++ b/autoxact.cc @@ -4,11 +4,14 @@ namespace ledger { void automated_transaction_t::extend_entry(entry_t * entry) { - for (transactions_list::iterator i = entry->transactions.begin(); - i != entry->transactions.end(); + transactions_deque initial_xacts(entry->transactions.begin(), + entry->transactions.end()); + + for (transactions_deque::iterator i = initial_xacts.begin(); + i != initial_xacts.end(); i++) - if (matches(masks, *((*i)->account))) { - for (transactions_list::iterator t = transactions.begin(); + if (predicate(*i)) + for (transactions_deque::iterator t = transactions.begin(); t != transactions.end(); t++) { amount_t amt; @@ -22,7 +25,6 @@ void automated_transaction_t::extend_entry(entry_t * entry) (*t)->flags | TRANSACTION_AUTO); entry->add_transaction(xact); } - } } } // namespace ledger @@ -8,15 +8,17 @@ namespace ledger { +typedef std::deque<transaction_t *> transactions_deque; + class automated_transaction_t { public: - masks_list masks; - transactions_list transactions; + item_predicate<transaction_t> predicate; + transactions_deque transactions; - automated_transaction_t(masks_list& _masks, - transactions_list& _transactions) { - masks.insert(masks.begin(), _masks.begin(), _masks.end()); + automated_transaction_t(const std::string& _predicate, + transactions_deque& _transactions) + : predicate(_predicate) { transactions.insert(transactions.begin(), _transactions.begin(), _transactions.end()); // Take over ownership of the pointers @@ -24,7 +26,7 @@ public: } ~automated_transaction_t() { - for (transactions_list::iterator i = transactions.begin(); + for (transactions_deque::iterator i = transactions.begin(); i != transactions.end(); i++) delete *i; @@ -352,7 +352,7 @@ inline balance_t abs(const balance_t& bal) { return temp; } -#ifdef DEBUG +#ifdef DEBUG_ENABLED inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) { bal.write(out, 12); return out; @@ -29,7 +29,7 @@ void read_binary_amount(std::istream& in, amount_t& amt) { unsigned long id; -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -45,7 +45,7 @@ void read_binary_amount(std::istream& in, amount_t& amt) amt.read_quantity(in); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -60,7 +60,7 @@ transaction_t * read_binary_transaction(std::istream& in, entry_t * entry) unsigned long id; -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -85,7 +85,7 @@ transaction_t * read_binary_transaction(std::istream& in, entry_t * entry) xact->note = buf; } -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -100,7 +100,7 @@ entry_t * read_binary_entry(std::istream& in, journal_t * journal) { entry_t * entry = new entry_t; -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -134,7 +134,7 @@ entry_t * read_binary_entry(std::istream& in, journal_t * journal) entry->transactions.push_back(xact); } -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -152,7 +152,7 @@ commodity_t * read_binary_commodity(std::istream& in) commodity_t * commodity = new commodity_t; commodities.push_back(commodity); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -202,7 +202,7 @@ commodity_t * read_binary_commodity(std::istream& in) read_binary_amount(in, commodity->conversion); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -220,7 +220,7 @@ account_t * read_binary_account(std::istream& in, account_t * master = NULL) account_t * acct = new account_t(NULL); accounts.push_back(acct); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -272,7 +272,7 @@ account_t * read_binary_account(std::istream& in, account_t * master = NULL) acct->add_account(child); } -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -296,7 +296,7 @@ unsigned int read_binary_journal(std::istream& in, if (magic != binary_magic_number) return 0; -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -356,7 +356,7 @@ unsigned int read_binary_journal(std::istream& in, journal->entries.push_back(entry); } -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard; in.read((char *)&guard, sizeof(guard)); @@ -373,7 +373,7 @@ unsigned int read_binary_journal(std::istream& in, void write_binary_amount(std::ostream& out, const amount_t& amt) { -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1001; out.write((char *)&guard, sizeof(guard)); @@ -389,7 +389,7 @@ void write_binary_amount(std::ostream& out, const amount_t& amt) amt.write_quantity(out); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1002; out.write((char *)&guard, sizeof(guard)); @@ -399,7 +399,7 @@ void write_binary_amount(std::ostream& out, const amount_t& amt) void write_binary_transaction(std::ostream& out, transaction_t * xact) { -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1003; out.write((char *)&guard, sizeof(guard)); @@ -416,7 +416,7 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact) if (len) out.write(xact->note.c_str(), len); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1004; out.write((char *)&guard, sizeof(guard)); @@ -426,7 +426,7 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact) void write_binary_entry(std::ostream& out, entry_t * entry) { -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1005; out.write((char *)&guard, sizeof(guard)); @@ -454,7 +454,7 @@ void write_binary_entry(std::ostream& out, entry_t * entry) i++) write_binary_transaction(out, *i); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1006; out.write((char *)&guard, sizeof(guard)); @@ -464,7 +464,7 @@ void write_binary_entry(std::ostream& out, entry_t * entry) void write_binary_commodity(std::ostream& out, commodity_t * commodity) { -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1007; out.write((char *)&guard, sizeof(guard)); @@ -504,7 +504,7 @@ void write_binary_commodity(std::ostream& out, commodity_t * commodity) write_binary_amount(out, commodity->conversion); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1008; out.write((char *)&guard, sizeof(guard)); @@ -514,7 +514,7 @@ void write_binary_commodity(std::ostream& out, commodity_t * commodity) void write_binary_account(std::ostream& out, account_t * account) { -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1009; out.write((char *)&guard, sizeof(guard)); @@ -552,7 +552,7 @@ void write_binary_account(std::ostream& out, account_t * account) i++) write_binary_account(out, (*i).second); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1010; out.write((char *)&guard, sizeof(guard)); @@ -565,7 +565,7 @@ void write_binary_journal(std::ostream& out, journal_t * journal, { out.write((char *)&binary_magic_number, sizeof(binary_magic_number)); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1011; out.write((char *)&guard, sizeof(guard)); @@ -613,7 +613,7 @@ void write_binary_journal(std::ostream& out, journal_t * journal, i++) write_binary_entry(out, *i); -#ifdef DEBUG +#ifdef DEBUG_ENABLED { unsigned short guard = 0x1012; out.write((char *)&guard, sizeof(guard)); @@ -6,15 +6,6 @@ #include <exception> #include <string> -#ifdef DEBUG -#include <cassert> -#else -#ifdef assert -#undef assert -#endif -#define assert(x) -#endif - namespace ledger { class error : public std::exception @@ -104,14 +104,14 @@ class format_transaction mutable transaction_t * last_xact; public: - format_transaction(std::ostream& _output_stream, - const format_t& _first_line_format, - const format_t& _next_lines_format, - const node_t * display_predicate = NULL, + format_transaction(std::ostream& _output_stream, + const format_t& _first_line_format, + const format_t& _next_lines_format, + const std::string& display_predicate = NULL, #ifdef COLLAPSED_REGISTER - const bool _collapsed = false, + const bool _collapsed = false, #endif - const bool _inverted = false) + const bool _inverted = false) : output_stream(_output_stream), first_line_format(_first_line_format), next_lines_format(_next_lines_format), @@ -198,9 +198,9 @@ class format_account item_predicate<account_t> disp_pred_functor; public: - format_account(std::ostream& _output_stream, - const format_t& _format, - const node_t * display_predicate = NULL) + format_account(std::ostream& _output_stream, + const format_t& _format, + const std::string& display_predicate = NULL) : output_stream(_output_stream), format(_format), disp_pred_functor(display_predicate) {} @@ -237,10 +237,10 @@ class format_equity mutable balance_t total; public: - format_equity(std::ostream& _output_stream, - const format_t& _first_line_format, - const format_t& _next_lines_format, - const node_t * display_predicate = NULL) + format_equity(std::ostream& _output_stream, + const format_t& _first_line_format, + const format_t& _next_lines_format, + const std::string& display_predicate = NULL) : output_stream(_output_stream), first_line_format(_first_line_format), next_lines_format(_next_lines_format), @@ -19,6 +19,12 @@ #include "amount.h" #include "balance.h" +#ifdef RELEASE_LEVEL +#if RELEASE_LEVEL >= 2 +#include "debug.h" +#endif +#endif + namespace ledger { #define TRANSACTION_NORMAL 0x00 @@ -102,34 +102,6 @@ void download_price_quote(commodity_t * commodity, } // namespace ledger -static void assemble_regexp_predicate(std::string& predicate_string, - const std::list<std::string>& strings, - const bool exclude = false, - const bool payee = false) -{ - if (strings.size() == 0) - return; - - if (! predicate_string.empty()) - predicate_string += "&"; - if (exclude) - predicate_string += "!"; - if (payee) - predicate_string += "/"; - predicate_string += "/("; - bool first = true; - for (std::list<std::string>::const_iterator i = strings.begin(); - i != strings.end(); - i++) { - if (first) - first = false; - else - predicate_string += "|"; - predicate_string += *i; - } - predicate_string += ")/"; -} - static void show_version(std::ostream& out) { out @@ -186,13 +158,11 @@ int main(int argc, char * argv[]) using namespace ledger; std::auto_ptr<journal_t> journal(new journal_t); - std::list<std::string> files; - std::auto_ptr<node_t> predicate; - std::auto_ptr<node_t> display_predicate; - std::auto_ptr<node_t> sort_order; + std::list<std::string> files; + std::auto_ptr<node_t> sort_order; - std::string predicate_string; - std::string display_predicate_string; + std::string predicate; + std::string display_predicate; std::string format_string; std::string sort_string; std::string value_expr = "a"; @@ -207,8 +177,11 @@ int main(int argc, char * argv[]) bool show_commodities_revalued = false; bool show_commodities_revalued_only = false; -#ifdef DEBUG - bool debug = false; +#ifdef DEBUG_ENABLED + if (char * p = std::getenv("DEBUG_FILE")) { + debug_stream = new std::ofstream(p); + free_debug_stream = true; + } #endif // Initialize some variables based on environment variable settings @@ -251,19 +224,13 @@ int main(int argc, char * argv[]) int c, index; while (-1 != (c = getopt(argc, argv, - "+ABb:Ccd:DEe:F:f:Ghi:L:l:MnoOP:p:QRS:st:T:UVvWXZz"))) { + "+ABb:Ccd:DEe:F:f:Ghi:L:l:MnoOP:p:QRS:st:T:UVvWXZ"))) { switch (char(c)) { // Basic options case 'h': show_help(std::cout); break; -#ifdef DEBUG - case 'z': - debug = 1; - break; -#endif - case 'v': show_version(std::cout); return 0; @@ -278,48 +245,48 @@ int main(int argc, char * argv[]) break; case 'b': - if (! predicate_string.empty()) - predicate_string += "&"; - predicate_string += "(d>=["; - predicate_string += optarg; - predicate_string += "])"; + if (! predicate.empty()) + predicate += "&"; + predicate += "(d>=["; + predicate += optarg; + predicate += "])"; break; case 'e': - if (! predicate_string.empty()) - predicate_string += "&"; - predicate_string += "(d<["; - predicate_string += optarg; - predicate_string += "])"; + if (! predicate.empty()) + predicate += "&"; + predicate += "(d<["; + predicate += optarg; + predicate += "])"; break; case 'c': { - if (! predicate_string.empty()) - predicate_string += "&"; - predicate_string += "(d<"; + if (! predicate.empty()) + predicate += "&"; + predicate += "(d<"; std::ostringstream now; now << std::time(NULL); - predicate_string += now.str(); - predicate_string += ")"; + predicate += now.str(); + predicate += ")"; break; } case 'C': - if (! predicate_string.empty()) - predicate_string += "&"; - predicate_string += "X"; + if (! predicate.empty()) + predicate += "&"; + predicate += "X"; break; case 'U': - if (! predicate_string.empty()) - predicate_string += "&"; - predicate_string += "!X"; + if (! predicate.empty()) + predicate += "&"; + predicate += "!X"; break; case 'R': - if (! predicate_string.empty()) - predicate_string += "&"; - predicate_string += "R"; + if (! predicate.empty()) + predicate += "&"; + predicate += "R"; break; // Customizing output @@ -348,19 +315,19 @@ int main(int argc, char * argv[]) break; case 'l': - if (! predicate_string.empty()) - predicate_string += "&"; - predicate_string += "("; - predicate_string += optarg; - predicate_string += ")"; + if (! predicate.empty()) + predicate += "&"; + predicate += "("; + predicate += optarg; + predicate += ")"; break; case 'd': - if (! display_predicate_string.empty()) - display_predicate_string += "&"; - display_predicate_string += "("; - display_predicate_string += optarg; - display_predicate_string += ")"; + if (! display_predicate.empty()) + display_predicate += "&"; + display_predicate += "("; + display_predicate += optarg; + display_predicate += ")"; break; // Commodity reporting @@ -518,81 +485,56 @@ int main(int argc, char * argv[]) if (command == "e") { new_entry.reset(journal->derive_entry(argc - index, &argv[index])); } else { - std::list<std::string> account_include_regexps; - std::list<std::string> account_exclude_regexps; - std::list<std::string> payee_include_regexps; - std::list<std::string> payee_exclude_regexps; - // Treat the remaining command-line arguments as regular // expressions, used for refining report results. - for (; index < argc; index++) { + int start = index; + for (; index < argc; index++) if (std::strcmp(argv[index], "--") == 0) { index++; break; } - if (! show_expanded && command == "b") - show_expanded = true; - - if (argv[index][0] == '-') - account_exclude_regexps.push_back(argv[index] + 1); - else - account_include_regexps.push_back(argv[index]); + if (start < index) { + std::list<std::string> regexps(&argv[start], &argv[index]); + std::string pred = regexps_to_predicate(regexps.begin(), regexps.end()); + if (! pred.empty()) { + if (! predicate.empty()) + predicate += "&"; + predicate += pred; + } } - for (; index < argc; index++) { - if (! show_expanded && command == "b") - show_expanded = true; - - if (argv[index][0] == '-') - payee_exclude_regexps.push_back(argv[index] + 1); - else - payee_include_regexps.push_back(argv[index]); + if (index < argc) { + std::list<std::string> regexps(&argv[index], &argv[argc]); + std::string pred = regexps_to_predicate(regexps.begin(), regexps.end(), + false); + if (! pred.empty()) { + if (! predicate.empty()) + predicate += "&"; + predicate += pred; + } } - - assemble_regexp_predicate(predicate_string, account_include_regexps); - assemble_regexp_predicate(predicate_string, account_exclude_regexps, true); - assemble_regexp_predicate(predicate_string, payee_include_regexps, - false, true); - assemble_regexp_predicate(predicate_string, payee_exclude_regexps, - true, true); } // Compile the predicates - if (! predicate_string.empty()) { -#ifdef DEBUG - if (debug) - std::cerr << "predicate = " << predicate_string << std::endl; -#endif - predicate.reset(parse_expr(predicate_string)); - } - - if (display_predicate_string.empty()) { + if (display_predicate.empty()) { if (command == "b") { if (! show_empty) - display_predicate_string = "T"; + display_predicate = "T"; if (! show_expanded) { - if (! display_predicate_string.empty()) - display_predicate_string += "&"; - display_predicate_string += "!n"; + if (! display_predicate.empty()) + display_predicate += "&"; + display_predicate += "!n"; } } else if (command == "E") { - display_predicate_string = "a"; + display_predicate = "a"; } } - if (! display_predicate_string.empty()) { -#ifdef DEBUG - if (debug) - std::cerr << "disp-pred = " << display_predicate_string << std::endl; -#endif - display_predicate.reset(parse_expr(display_predicate_string)); - } - // Compile the sorting string if (! sort_string.empty()) @@ -647,8 +589,8 @@ int main(int argc, char * argv[]) if (command == "b") { format_t format(first_line_format); - format_account formatter(std::cout, format, display_predicate.get()); - walk_accounts(journal->master, formatter, predicate.get(), + format_account formatter(std::cout, format, display_predicate); + walk_accounts(journal->master, formatter, predicate, xact_display_flags, show_subtotals, sort_order.get()); if (format_account::disp_subaccounts_p(journal->master)) { @@ -660,9 +602,8 @@ int main(int argc, char * argv[]) else if (command == "E") { format_t format(first_line_format); format_t nformat(next_lines_format); - format_equity formatter(std::cout, format, nformat, - display_predicate.get()); - walk_accounts(journal->master, formatter, predicate.get(), + format_equity formatter(std::cout, format, nformat, display_predicate); + walk_accounts(journal->master, formatter, predicate, xact_display_flags, true, sort_order.get()); } else if (command == "e") { @@ -678,8 +619,7 @@ int main(int argc, char * argv[]) else { format_t format(first_line_format); format_t nformat(next_lines_format); - format_transaction formatter(std::cout, format, nformat, - display_predicate.get(), + format_transaction formatter(std::cout, format, nformat, display_predicate, #ifdef COLLAPSED_REGISTER ! show_subtotals, #endif @@ -689,15 +629,15 @@ int main(int argc, char * argv[]) changed_value_filter<format_transaction> filtered_formatter(formatter); walk_entries(journal->entries.begin(), journal->entries.end(), - filtered_formatter, predicate.get(), xact_display_flags); + filtered_formatter, predicate, xact_display_flags); } else { walk_entries(journal->entries.begin(), journal->entries.end(), - formatter, predicate.get(), xact_display_flags); + formatter, predicate, xact_display_flags); } } else { transactions_deque transactions_pool; walk_entries(journal->entries.begin(), journal->entries.end(), - collect_transactions(transactions_pool), predicate.get(), + collect_transactions(transactions_pool), predicate, xact_display_flags); std::stable_sort(transactions_pool.begin(), transactions_pool.end(), compare_items<transaction_t>(sort_order.get())); @@ -131,36 +131,23 @@ void parse_automated_transactions(std::istream& in, account_t * account, automated_transactions_t& auto_xacts) { static char line[MAX_LINE + 1]; + in.getline(line, MAX_LINE); + linenum++; - masks_list masks; - - while (! in.eof() && in.peek() == '=') { - in.getline(line, MAX_LINE); - linenum++; - - char * p = line + 1; - p = skip_ws(p); - - masks.push_back(mask_t(p)); - } - - transactions_list xacts; + transactions_deque xacts; - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) { + while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) if (transaction_t * xact = parse_transaction(in, account, NULL)) { if (! xact->amount) throw parse_error(path, linenum, - "All automated transactions must have a value"); + "All automated transactions must have values"); else xacts.push_back(xact); } - } - if (! masks.empty() && ! xacts.empty()) { - automated_transaction_t * auto_xact - = new automated_transaction_t(masks, xacts); - auto_xacts.add_automated_transaction(auto_xact); - } + if (! xacts.empty()) + auto_xacts. + add_automated_transaction(new automated_transaction_t(line + 1, xacts)); } bool finalize_entry(entry_t * entry) @@ -2,6 +2,8 @@ #include "error.h" #include "datetime.h" +#include <vector> + #include <pcre.h> namespace ledger { @@ -22,6 +24,8 @@ mask_t::mask_t(const std::string& pat) : exclude(false) } pattern = p; + DEBUG_PRINT("valexpr.mask.parse", "pattern = '" << pattern << "'"); + const char *error; int erroffset; regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS, @@ -337,6 +341,16 @@ void node_t::compute(balance_t& result, const details_t& details) const } } +inline char peek_next_nonws(std::istream& in) +{ + char c = in.peek(); + while (! in.eof() && std::isspace(c) && c != '\n') { + in.get(c); + c = in.peek(); + } + return c; +} + node_t * parse_term(std::istream& in); inline node_t * parse_term(const char * p) { @@ -348,7 +362,7 @@ node_t * parse_term(std::istream& in) { node_t * node = NULL; - char c = in.peek(); + char c = peek_next_nonws(in); if (std::isdigit(c) || c == '.' || c == '{') { std::string ident; @@ -425,14 +439,14 @@ node_t * parse_term(std::istream& in) case 'P': node = new node_t(node_t::F_VALUE); - if (in.peek() == '(') { + if (peek_next_nonws(in) == '(') { in.get(c); node->left = parse_expr(in); - if (in.peek() == ',') { + if (peek_next_nonws(in) == ',') { in.get(c); node->right = parse_expr(in); } - if (in.peek() == ')') + if (peek_next_nonws(in) == ')') in.get(c); else throw expr_error("Missing ')'"); @@ -446,7 +460,7 @@ node_t * parse_term(std::istream& in) std::string ident; bool payee_mask = false; - c = in.peek(); + c = peek_next_nonws(in); if (c == '/') { payee_mask = true; in.get(c); @@ -474,7 +488,7 @@ node_t * parse_term(std::istream& in) case '(': node = parse_expr(in); - if (in.peek() == ')') + if (peek_next_nonws(in) == ')') in.get(c); else throw expr_error("Missing ')'"); @@ -515,7 +529,7 @@ node_t * parse_mul_expr(std::istream& in) node = parse_term(in); if (node && ! in.eof()) { - char c = in.peek(); + char c = peek_next_nonws(in); while (c == '*' || c == '/') { in.get(c); switch (c) { @@ -535,7 +549,7 @@ node_t * parse_mul_expr(std::istream& in) break; } } - c = in.peek(); + c = peek_next_nonws(in); } } @@ -549,7 +563,7 @@ node_t * parse_add_expr(std::istream& in) node = parse_mul_expr(in); if (node && ! in.eof()) { - char c = in.peek(); + char c = peek_next_nonws(in); while (c == '+' || c == '-') { in.get(c); switch (c) { @@ -569,7 +583,7 @@ node_t * parse_add_expr(std::istream& in) break; } } - c = in.peek(); + c = peek_next_nonws(in); } } @@ -580,7 +594,7 @@ node_t * parse_logic_expr(std::istream& in) { node_t * node = NULL; - if (in.peek() == '!') { + if (peek_next_nonws(in) == '!') { char c; in.get(c); node = new node_t(node_t::O_NOT); @@ -591,7 +605,7 @@ node_t * parse_logic_expr(std::istream& in) node = parse_add_expr(in); if (node && ! in.eof()) { - char c = in.peek(); + char c = peek_next_nonws(in); if (c == '=' || c == '<' || c == '>') { in.get(c); switch (c) { @@ -606,7 +620,7 @@ node_t * parse_logic_expr(std::istream& in) case '<': { node_t * prev = node; node = new node_t(node_t::O_LT); - if (in.peek() == '=') { + if (peek_next_nonws(in) == '=') { in.get(c); node->type = node_t::O_LTE; } @@ -618,7 +632,7 @@ node_t * parse_logic_expr(std::istream& in) case '>': { node_t * prev = node; node = new node_t(node_t::O_GT); - if (in.peek() == '=') { + if (peek_next_nonws(in) == '=') { in.get(c); node->type = node_t::O_GTE; } @@ -647,7 +661,7 @@ node_t * parse_expr(std::istream& in) node = parse_logic_expr(in); if (node && ! in.eof()) { - char c = in.peek(); + char c = peek_next_nonws(in); while (c == '&' || c == '|' || c == '?') { in.get(c); switch (c) { @@ -674,7 +688,7 @@ node_t * parse_expr(std::istream& in) node_t * choices = new node_t(node_t::O_COL); node->right = choices; choices->left = parse_logic_expr(in); - c = in.peek(); + c = peek_next_nonws(in); if (c != ':') { std::ostringstream err; err << "Unexpected character '" << c << "'"; @@ -692,21 +706,57 @@ node_t * parse_expr(std::istream& in) throw expr_error(err.str()); } } - c = in.peek(); + c = peek_next_nonws(in); } } return node; } -} // namespace ledger +std::string regexps_to_predicate(std::list<std::string>::const_iterator begin, + std::list<std::string>::const_iterator end, + const bool account_regexp) +{ + std::vector<std::string> regexps(2); + std::string pred; + + // Treat the remaining command-line arguments as regular + // expressions, used for refining report results. + + for (std::list<std::string>::const_iterator i = begin; + i != end; + i++) + if ((*i)[0] == '-') { + if (! regexps[1].empty()) + regexps[1] += "|"; + regexps[1] += (*i).substr(1); + } else { + if (! regexps[0].empty()) + regexps[0] += "|"; + regexps[0] += *i; + } + for (std::vector<std::string>::const_iterator i = regexps.begin(); + i != regexps.end(); + i++) + if (! (*i).empty()) { + if (! pred.empty()) + pred += "&"; + if (i != regexps.begin()) + pred += "!"; + if (! account_regexp) + pred += "/"; + pred += "/(?:"; + pred += *i; + pred += ")/"; + } -#ifdef TEST + return pred; +} -namespace ledger { +#ifdef DEBUG_ENABLED -static void dump_tree(std::ostream& out, node_t * node) +void dump_tree(std::ostream& out, const node_t * node) { switch (node->type) { case node_t::CONSTANT_A: @@ -834,8 +884,12 @@ static void dump_tree(std::ostream& out, node_t * node) } } +#endif // DEBUG_ENABLED + } // namespace ledger +#ifdef TEST + int main(int argc, char *argv[]) { ledger::dump_tree(std::cout, ledger::parse_expr(argv[1])); @@ -134,13 +134,36 @@ inline node_t * find_node(node_t * node, node_t::kind_t type) { return result; } +#ifdef DEBUG_ENABLED +void dump_tree(std::ostream& out, const node_t * node); +#endif + template <typename T> class item_predicate { const node_t * predicate; public: - item_predicate(const node_t * _predicate) : predicate(_predicate) {} + item_predicate(const std::string& _predicate) + : predicate(_predicate.empty() ? NULL : parse_expr(_predicate)) { +#ifdef DEBUG_ENABLED + DEBUG_CLASS("valexpr.predicate.parse"); + + DEBUG_PRINT_("parsing: '" << _predicate << "'"); + if (DEBUG_() && ledger::debug_stream) { + *ledger::debug_stream << "dump: "; + dump_tree(*ledger::debug_stream, predicate); + *ledger::debug_stream << std::endl; + } +#endif + } + item_predicate(const node_t * _predicate) + : predicate(_predicate) {} + + ~item_predicate() { + if (predicate) + delete predicate; + } bool operator()(const T * item) const { if (predicate) { @@ -153,6 +176,10 @@ class item_predicate } }; +std::string regexps_to_predicate(std::list<std::string>::const_iterator begin, + std::list<std::string>::const_iterator end, + const bool account_regexp = true); + } // namespace report #endif // _REPORT_H @@ -11,7 +11,7 @@ class sum_in_account }; void calc__accounts(account_t * account, - item_predicate<transaction_t>& pred_functor, + const item_predicate<transaction_t>& pred_functor, unsigned int flags) { sum_in_account functor; @@ -82,9 +82,9 @@ void handle_transaction(transaction_t * xact, template <typename Function> void walk_entries(entries_list::iterator begin, entries_list::iterator end, - const Function& functor, - const node_t * predicate, - unsigned int flags) + const Function& functor, + const std::string& predicate, + unsigned int flags) { item_predicate<transaction_t> pred_functor(predicate); @@ -200,7 +200,7 @@ void for_each_account(account_t * account, const Function& functor) } void calc__accounts(account_t * account, - item_predicate<transaction_t>& pred_functor, + const item_predicate<transaction_t>& pred_functor, unsigned int flags); inline void sum__accounts(account_t * account) @@ -215,12 +215,12 @@ inline void sum__accounts(account_t * account) } template <typename Function> -void walk_accounts(account_t * account, - const Function& functor, - const node_t * predicate, - unsigned int flags, - const bool calc_subtotals, - const node_t * sort_order = NULL) +void walk_accounts(account_t * account, + const Function& functor, + const std::string& predicate, + unsigned int flags, + const bool calc_subtotals, + const node_t * sort_order = NULL) { item_predicate<transaction_t> pred_functor(predicate); |