new debug code; automated transactions now use value expression predicates

This commit is contained in:
John Wiegley 2004-08-10 17:54:47 -04:00
parent 799e97e420
commit 7a1d0d4614
15 changed files with 273 additions and 253 deletions

View file

@ -4,6 +4,7 @@ CODE = account.cc \
balance.cc \ balance.cc \
binary.cc \ binary.cc \
datetime.cc \ datetime.cc \
debug.cc \
error.cc \ error.cc \
format.cc \ format.cc \
ledger.cc \ ledger.cc \
@ -17,9 +18,9 @@ OBJS = $(patsubst %.cc,%.o,$(CODE))
CXX = g++ CXX = g++
CFLAGS = -Wall -ansi -pedantic CFLAGS = -Wall -ansi -pedantic
#DFLAGS = -O3 -fomit-frame-pointer #DFLAGS = -O3 -fomit-frame-pointer -DRELEASE_LEVEL=0
DFLAGS = -g -DDEBUG=1 DFLAGS = -g -DRELEASE_LEVEL=4
#DFLAGS = -g -DDEBUG=1 -pg #DFLAGS = -g -DRELEASE_LEVEL=2 -pg
INCS = -I/sw/include \ INCS = -I/sw/include \
-I/usr/include/gcc/darwin/3.3/c++ \ -I/usr/include/gcc/darwin/3.3/c++ \

12
NEWS
View file

@ -54,7 +54,7 @@
-W Report the trend, with older values affecting the trend less -W Report the trend, with older values affecting the trend less
-X Report expected amount for the next transaction -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: be changed using -t and -T and an expression string composed of:
a amount a amount
@ -142,6 +142,16 @@
%?10d %?-.20p %-.22a %12.66t %12.80T %?10d %?-.20p %-.22a %12.66t %12.80T
%20T %-a %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) * 1.7 (never released)
- Pricing histories are now supported, so that ledger remembers - Pricing histories are now supported, so that ledger remembers

View file

@ -4,11 +4,14 @@ namespace ledger {
void automated_transaction_t::extend_entry(entry_t * entry) void automated_transaction_t::extend_entry(entry_t * entry)
{ {
for (transactions_list::iterator i = entry->transactions.begin(); transactions_deque initial_xacts(entry->transactions.begin(),
i != entry->transactions.end(); entry->transactions.end());
for (transactions_deque::iterator i = initial_xacts.begin();
i != initial_xacts.end();
i++) i++)
if (matches(masks, *((*i)->account))) { if (predicate(*i))
for (transactions_list::iterator t = transactions.begin(); for (transactions_deque::iterator t = transactions.begin();
t != transactions.end(); t != transactions.end();
t++) { t++) {
amount_t amt; amount_t amt;
@ -22,7 +25,6 @@ void automated_transaction_t::extend_entry(entry_t * entry)
(*t)->flags | TRANSACTION_AUTO); (*t)->flags | TRANSACTION_AUTO);
entry->add_transaction(xact); entry->add_transaction(xact);
} }
}
} }
} // namespace ledger } // namespace ledger

View file

@ -8,15 +8,17 @@
namespace ledger { namespace ledger {
typedef std::deque<transaction_t *> transactions_deque;
class automated_transaction_t class automated_transaction_t
{ {
public: public:
masks_list masks; item_predicate<transaction_t> predicate;
transactions_list transactions; transactions_deque transactions;
automated_transaction_t(masks_list& _masks, automated_transaction_t(const std::string& _predicate,
transactions_list& _transactions) { transactions_deque& _transactions)
masks.insert(masks.begin(), _masks.begin(), _masks.end()); : predicate(_predicate) {
transactions.insert(transactions.begin(), transactions.insert(transactions.begin(),
_transactions.begin(), _transactions.end()); _transactions.begin(), _transactions.end());
// Take over ownership of the pointers // Take over ownership of the pointers
@ -24,7 +26,7 @@ public:
} }
~automated_transaction_t() { ~automated_transaction_t() {
for (transactions_list::iterator i = transactions.begin(); for (transactions_deque::iterator i = transactions.begin();
i != transactions.end(); i != transactions.end();
i++) i++)
delete *i; delete *i;

View file

@ -352,7 +352,7 @@ inline balance_t abs(const balance_t& bal) {
return temp; return temp;
} }
#ifdef DEBUG #ifdef DEBUG_ENABLED
inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) { inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) {
bal.write(out, 12); bal.write(out, 12);
return out; return out;

View file

@ -29,7 +29,7 @@ void read_binary_amount(std::istream& in, amount_t& amt)
{ {
unsigned long id; unsigned long id;
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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); amt.read_quantity(in);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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; unsigned long id;
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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; xact->note = buf;
} }
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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; entry_t * entry = new entry_t;
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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); entry->transactions.push_back(xact);
} }
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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; commodity_t * commodity = new commodity_t;
commodities.push_back(commodity); commodities.push_back(commodity);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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); read_binary_amount(in, commodity->conversion);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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); account_t * acct = new account_t(NULL);
accounts.push_back(acct); accounts.push_back(acct);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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); acct->add_account(child);
} }
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(guard)); in.read((char *)&guard, sizeof(guard));
@ -296,7 +296,7 @@ unsigned int read_binary_journal(std::istream& in,
if (magic != binary_magic_number) if (magic != binary_magic_number)
return 0; return 0;
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(guard)); in.read((char *)&guard, sizeof(guard));
@ -356,7 +356,7 @@ unsigned int read_binary_journal(std::istream& in,
journal->entries.push_back(entry); journal->entries.push_back(entry);
} }
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard; unsigned short guard;
in.read((char *)&guard, sizeof(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) void write_binary_amount(std::ostream& out, const amount_t& amt)
{ {
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1001; unsigned short guard = 0x1001;
out.write((char *)&guard, sizeof(guard)); 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); amt.write_quantity(out);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1002; unsigned short guard = 0x1002;
out.write((char *)&guard, sizeof(guard)); 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) void write_binary_transaction(std::ostream& out, transaction_t * xact)
{ {
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1003; unsigned short guard = 0x1003;
out.write((char *)&guard, sizeof(guard)); out.write((char *)&guard, sizeof(guard));
@ -416,7 +416,7 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact)
if (len) if (len)
out.write(xact->note.c_str(), len); out.write(xact->note.c_str(), len);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1004; unsigned short guard = 0x1004;
out.write((char *)&guard, sizeof(guard)); 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) void write_binary_entry(std::ostream& out, entry_t * entry)
{ {
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1005; unsigned short guard = 0x1005;
out.write((char *)&guard, sizeof(guard)); out.write((char *)&guard, sizeof(guard));
@ -454,7 +454,7 @@ void write_binary_entry(std::ostream& out, entry_t * entry)
i++) i++)
write_binary_transaction(out, *i); write_binary_transaction(out, *i);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1006; unsigned short guard = 0x1006;
out.write((char *)&guard, sizeof(guard)); 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) void write_binary_commodity(std::ostream& out, commodity_t * commodity)
{ {
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1007; unsigned short guard = 0x1007;
out.write((char *)&guard, sizeof(guard)); 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); write_binary_amount(out, commodity->conversion);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1008; unsigned short guard = 0x1008;
out.write((char *)&guard, sizeof(guard)); 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) void write_binary_account(std::ostream& out, account_t * account)
{ {
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1009; unsigned short guard = 0x1009;
out.write((char *)&guard, sizeof(guard)); out.write((char *)&guard, sizeof(guard));
@ -552,7 +552,7 @@ void write_binary_account(std::ostream& out, account_t * account)
i++) i++)
write_binary_account(out, (*i).second); write_binary_account(out, (*i).second);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1010; unsigned short guard = 0x1010;
out.write((char *)&guard, sizeof(guard)); 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)); out.write((char *)&binary_magic_number, sizeof(binary_magic_number));
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1011; unsigned short guard = 0x1011;
out.write((char *)&guard, sizeof(guard)); out.write((char *)&guard, sizeof(guard));
@ -613,7 +613,7 @@ void write_binary_journal(std::ostream& out, journal_t * journal,
i++) i++)
write_binary_entry(out, *i); write_binary_entry(out, *i);
#ifdef DEBUG #ifdef DEBUG_ENABLED
{ {
unsigned short guard = 0x1012; unsigned short guard = 0x1012;
out.write((char *)&guard, sizeof(guard)); out.write((char *)&guard, sizeof(guard));

View file

@ -6,15 +6,6 @@
#include <exception> #include <exception>
#include <string> #include <string>
#ifdef DEBUG
#include <cassert>
#else
#ifdef assert
#undef assert
#endif
#define assert(x)
#endif
namespace ledger { namespace ledger {
class error : public std::exception class error : public std::exception

View file

@ -107,7 +107,7 @@ class format_transaction
format_transaction(std::ostream& _output_stream, format_transaction(std::ostream& _output_stream,
const format_t& _first_line_format, const format_t& _first_line_format,
const format_t& _next_lines_format, const format_t& _next_lines_format,
const node_t * display_predicate = NULL, const std::string& display_predicate = NULL,
#ifdef COLLAPSED_REGISTER #ifdef COLLAPSED_REGISTER
const bool _collapsed = false, const bool _collapsed = false,
#endif #endif
@ -200,7 +200,7 @@ class format_account
public: public:
format_account(std::ostream& _output_stream, format_account(std::ostream& _output_stream,
const format_t& _format, const format_t& _format,
const node_t * display_predicate = NULL) const std::string& display_predicate = NULL)
: output_stream(_output_stream), format(_format), : output_stream(_output_stream), format(_format),
disp_pred_functor(display_predicate) {} disp_pred_functor(display_predicate) {}
@ -240,7 +240,7 @@ class format_equity
format_equity(std::ostream& _output_stream, format_equity(std::ostream& _output_stream,
const format_t& _first_line_format, const format_t& _first_line_format,
const format_t& _next_lines_format, const format_t& _next_lines_format,
const node_t * display_predicate = NULL) const std::string& display_predicate = NULL)
: output_stream(_output_stream), : output_stream(_output_stream),
first_line_format(_first_line_format), first_line_format(_first_line_format),
next_lines_format(_next_lines_format), next_lines_format(_next_lines_format),

View file

@ -19,6 +19,12 @@
#include "amount.h" #include "amount.h"
#include "balance.h" #include "balance.h"
#ifdef RELEASE_LEVEL
#if RELEASE_LEVEL >= 2
#include "debug.h"
#endif
#endif
namespace ledger { namespace ledger {
#define TRANSACTION_NORMAL 0x00 #define TRANSACTION_NORMAL 0x00

210
main.cc
View file

@ -102,34 +102,6 @@ void download_price_quote(commodity_t * commodity,
} // namespace ledger } // 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) static void show_version(std::ostream& out)
{ {
out out
@ -187,12 +159,10 @@ int main(int argc, char * argv[])
std::auto_ptr<journal_t> journal(new journal_t); std::auto_ptr<journal_t> journal(new journal_t);
std::list<std::string> files; 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::auto_ptr<node_t> sort_order;
std::string predicate_string; std::string predicate;
std::string display_predicate_string; std::string display_predicate;
std::string format_string; std::string format_string;
std::string sort_string; std::string sort_string;
std::string value_expr = "a"; std::string value_expr = "a";
@ -207,8 +177,11 @@ int main(int argc, char * argv[])
bool show_commodities_revalued = false; bool show_commodities_revalued = false;
bool show_commodities_revalued_only = false; bool show_commodities_revalued_only = false;
#ifdef DEBUG #ifdef DEBUG_ENABLED
bool debug = false; if (char * p = std::getenv("DEBUG_FILE")) {
debug_stream = new std::ofstream(p);
free_debug_stream = true;
}
#endif #endif
// Initialize some variables based on environment variable settings // Initialize some variables based on environment variable settings
@ -251,19 +224,13 @@ int main(int argc, char * argv[])
int c, index; int c, index;
while (-1 != while (-1 !=
(c = getopt(argc, argv, (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)) { switch (char(c)) {
// Basic options // Basic options
case 'h': case 'h':
show_help(std::cout); show_help(std::cout);
break; break;
#ifdef DEBUG
case 'z':
debug = 1;
break;
#endif
case 'v': case 'v':
show_version(std::cout); show_version(std::cout);
return 0; return 0;
@ -278,48 +245,48 @@ int main(int argc, char * argv[])
break; break;
case 'b': case 'b':
if (! predicate_string.empty()) if (! predicate.empty())
predicate_string += "&"; predicate += "&";
predicate_string += "(d>=["; predicate += "(d>=[";
predicate_string += optarg; predicate += optarg;
predicate_string += "])"; predicate += "])";
break; break;
case 'e': case 'e':
if (! predicate_string.empty()) if (! predicate.empty())
predicate_string += "&"; predicate += "&";
predicate_string += "(d<["; predicate += "(d<[";
predicate_string += optarg; predicate += optarg;
predicate_string += "])"; predicate += "])";
break; break;
case 'c': { case 'c': {
if (! predicate_string.empty()) if (! predicate.empty())
predicate_string += "&"; predicate += "&";
predicate_string += "(d<"; predicate += "(d<";
std::ostringstream now; std::ostringstream now;
now << std::time(NULL); now << std::time(NULL);
predicate_string += now.str(); predicate += now.str();
predicate_string += ")"; predicate += ")";
break; break;
} }
case 'C': case 'C':
if (! predicate_string.empty()) if (! predicate.empty())
predicate_string += "&"; predicate += "&";
predicate_string += "X"; predicate += "X";
break; break;
case 'U': case 'U':
if (! predicate_string.empty()) if (! predicate.empty())
predicate_string += "&"; predicate += "&";
predicate_string += "!X"; predicate += "!X";
break; break;
case 'R': case 'R':
if (! predicate_string.empty()) if (! predicate.empty())
predicate_string += "&"; predicate += "&";
predicate_string += "R"; predicate += "R";
break; break;
// Customizing output // Customizing output
@ -348,19 +315,19 @@ int main(int argc, char * argv[])
break; break;
case 'l': case 'l':
if (! predicate_string.empty()) if (! predicate.empty())
predicate_string += "&"; predicate += "&";
predicate_string += "("; predicate += "(";
predicate_string += optarg; predicate += optarg;
predicate_string += ")"; predicate += ")";
break; break;
case 'd': case 'd':
if (! display_predicate_string.empty()) if (! display_predicate.empty())
display_predicate_string += "&"; display_predicate += "&";
display_predicate_string += "("; display_predicate += "(";
display_predicate_string += optarg; display_predicate += optarg;
display_predicate_string += ")"; display_predicate += ")";
break; break;
// Commodity reporting // Commodity reporting
@ -518,81 +485,56 @@ int main(int argc, char * argv[])
if (command == "e") { if (command == "e") {
new_entry.reset(journal->derive_entry(argc - index, &argv[index])); new_entry.reset(journal->derive_entry(argc - index, &argv[index]));
} else { } 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 // Treat the remaining command-line arguments as regular
// expressions, used for refining report results. // expressions, used for refining report results.
for (; index < argc; index++) { int start = index;
for (; index < argc; index++)
if (std::strcmp(argv[index], "--") == 0) { if (std::strcmp(argv[index], "--") == 0) {
index++; index++;
break; break;
} }
if (! show_expanded && command == "b") if (start < index) {
show_expanded = true; std::list<std::string> regexps(&argv[start], &argv[index]);
std::string pred = regexps_to_predicate(regexps.begin(), regexps.end());
if (argv[index][0] == '-') if (! pred.empty()) {
account_exclude_regexps.push_back(argv[index] + 1); if (! predicate.empty())
else predicate += "&";
account_include_regexps.push_back(argv[index]); predicate += pred;
}
} }
for (; index < argc; index++) { if (index < argc) {
if (! show_expanded && command == "b") std::list<std::string> regexps(&argv[index], &argv[argc]);
show_expanded = true; std::string pred = regexps_to_predicate(regexps.begin(), regexps.end(),
false);
if (argv[index][0] == '-') if (! pred.empty()) {
payee_exclude_regexps.push_back(argv[index] + 1); if (! predicate.empty())
else predicate += "&";
payee_include_regexps.push_back(argv[index]); 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 // Compile the predicates
if (! predicate_string.empty()) { if (display_predicate.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 (command == "b") { if (command == "b") {
if (! show_empty) if (! show_empty)
display_predicate_string = "T"; display_predicate = "T";
if (! show_expanded) { if (! show_expanded) {
if (! display_predicate_string.empty()) if (! display_predicate.empty())
display_predicate_string += "&"; display_predicate += "&";
display_predicate_string += "!n"; display_predicate += "!n";
} }
} }
else if (command == "E") { 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 // Compile the sorting string
if (! sort_string.empty()) if (! sort_string.empty())
@ -647,8 +589,8 @@ int main(int argc, char * argv[])
if (command == "b") { if (command == "b") {
format_t format(first_line_format); format_t format(first_line_format);
format_account formatter(std::cout, format, display_predicate.get()); format_account formatter(std::cout, format, display_predicate);
walk_accounts(journal->master, formatter, predicate.get(), walk_accounts(journal->master, formatter, predicate,
xact_display_flags, show_subtotals, sort_order.get()); xact_display_flags, show_subtotals, sort_order.get());
if (format_account::disp_subaccounts_p(journal->master)) { if (format_account::disp_subaccounts_p(journal->master)) {
@ -660,9 +602,8 @@ int main(int argc, char * argv[])
else if (command == "E") { else if (command == "E") {
format_t format(first_line_format); format_t format(first_line_format);
format_t nformat(next_lines_format); format_t nformat(next_lines_format);
format_equity formatter(std::cout, format, nformat, format_equity formatter(std::cout, format, nformat, display_predicate);
display_predicate.get()); walk_accounts(journal->master, formatter, predicate,
walk_accounts(journal->master, formatter, predicate.get(),
xact_display_flags, true, sort_order.get()); xact_display_flags, true, sort_order.get());
} }
else if (command == "e") { else if (command == "e") {
@ -678,8 +619,7 @@ int main(int argc, char * argv[])
else { else {
format_t format(first_line_format); format_t format(first_line_format);
format_t nformat(next_lines_format); format_t nformat(next_lines_format);
format_transaction formatter(std::cout, format, nformat, format_transaction formatter(std::cout, format, nformat, display_predicate,
display_predicate.get(),
#ifdef COLLAPSED_REGISTER #ifdef COLLAPSED_REGISTER
! show_subtotals, ! show_subtotals,
#endif #endif
@ -689,15 +629,15 @@ int main(int argc, char * argv[])
changed_value_filter<format_transaction> changed_value_filter<format_transaction>
filtered_formatter(formatter); filtered_formatter(formatter);
walk_entries(journal->entries.begin(), journal->entries.end(), walk_entries(journal->entries.begin(), journal->entries.end(),
filtered_formatter, predicate.get(), xact_display_flags); filtered_formatter, predicate, xact_display_flags);
} else { } else {
walk_entries(journal->entries.begin(), journal->entries.end(), walk_entries(journal->entries.begin(), journal->entries.end(),
formatter, predicate.get(), xact_display_flags); formatter, predicate, xact_display_flags);
} }
} else { } else {
transactions_deque transactions_pool; transactions_deque transactions_pool;
walk_entries(journal->entries.begin(), journal->entries.end(), walk_entries(journal->entries.begin(), journal->entries.end(),
collect_transactions(transactions_pool), predicate.get(), collect_transactions(transactions_pool), predicate,
xact_display_flags); xact_display_flags);
std::stable_sort(transactions_pool.begin(), transactions_pool.end(), std::stable_sort(transactions_pool.begin(), transactions_pool.end(),
compare_items<transaction_t>(sort_order.get())); compare_items<transaction_t>(sort_order.get()));

View file

@ -131,36 +131,23 @@ void parse_automated_transactions(std::istream& in, account_t * account,
automated_transactions_t& auto_xacts) automated_transactions_t& auto_xacts)
{ {
static char line[MAX_LINE + 1]; static char line[MAX_LINE + 1];
masks_list masks;
while (! in.eof() && in.peek() == '=') {
in.getline(line, MAX_LINE); in.getline(line, MAX_LINE);
linenum++; linenum++;
char * p = line + 1; transactions_deque xacts;
p = skip_ws(p);
masks.push_back(mask_t(p)); while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t'))
}
transactions_list xacts;
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
if (transaction_t * xact = parse_transaction(in, account, NULL)) { if (transaction_t * xact = parse_transaction(in, account, NULL)) {
if (! xact->amount) if (! xact->amount)
throw parse_error(path, linenum, throw parse_error(path, linenum,
"All automated transactions must have a value"); "All automated transactions must have values");
else else
xacts.push_back(xact); xacts.push_back(xact);
} }
}
if (! masks.empty() && ! xacts.empty()) { if (! xacts.empty())
automated_transaction_t * auto_xact auto_xacts.
= new automated_transaction_t(masks, xacts); add_automated_transaction(new automated_transaction_t(line + 1, xacts));
auto_xacts.add_automated_transaction(auto_xact);
}
} }
bool finalize_entry(entry_t * entry) bool finalize_entry(entry_t * entry)

View file

@ -2,6 +2,8 @@
#include "error.h" #include "error.h"
#include "datetime.h" #include "datetime.h"
#include <vector>
#include <pcre.h> #include <pcre.h>
namespace ledger { namespace ledger {
@ -22,6 +24,8 @@ mask_t::mask_t(const std::string& pat) : exclude(false)
} }
pattern = p; pattern = p;
DEBUG_PRINT("valexpr.mask.parse", "pattern = '" << pattern << "'");
const char *error; const char *error;
int erroffset; int erroffset;
regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS, 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); node_t * parse_term(std::istream& in);
inline node_t * parse_term(const char * p) { inline node_t * parse_term(const char * p) {
@ -348,7 +362,7 @@ node_t * parse_term(std::istream& in)
{ {
node_t * node = NULL; node_t * node = NULL;
char c = in.peek(); char c = peek_next_nonws(in);
if (std::isdigit(c) || c == '.' || c == '{') { if (std::isdigit(c) || c == '.' || c == '{') {
std::string ident; std::string ident;
@ -425,14 +439,14 @@ node_t * parse_term(std::istream& in)
case 'P': case 'P':
node = new node_t(node_t::F_VALUE); node = new node_t(node_t::F_VALUE);
if (in.peek() == '(') { if (peek_next_nonws(in) == '(') {
in.get(c); in.get(c);
node->left = parse_expr(in); node->left = parse_expr(in);
if (in.peek() == ',') { if (peek_next_nonws(in) == ',') {
in.get(c); in.get(c);
node->right = parse_expr(in); node->right = parse_expr(in);
} }
if (in.peek() == ')') if (peek_next_nonws(in) == ')')
in.get(c); in.get(c);
else else
throw expr_error("Missing ')'"); throw expr_error("Missing ')'");
@ -446,7 +460,7 @@ node_t * parse_term(std::istream& in)
std::string ident; std::string ident;
bool payee_mask = false; bool payee_mask = false;
c = in.peek(); c = peek_next_nonws(in);
if (c == '/') { if (c == '/') {
payee_mask = true; payee_mask = true;
in.get(c); in.get(c);
@ -474,7 +488,7 @@ node_t * parse_term(std::istream& in)
case '(': case '(':
node = parse_expr(in); node = parse_expr(in);
if (in.peek() == ')') if (peek_next_nonws(in) == ')')
in.get(c); in.get(c);
else else
throw expr_error("Missing ')'"); throw expr_error("Missing ')'");
@ -515,7 +529,7 @@ node_t * parse_mul_expr(std::istream& in)
node = parse_term(in); node = parse_term(in);
if (node && ! in.eof()) { if (node && ! in.eof()) {
char c = in.peek(); char c = peek_next_nonws(in);
while (c == '*' || c == '/') { while (c == '*' || c == '/') {
in.get(c); in.get(c);
switch (c) { switch (c) {
@ -535,7 +549,7 @@ node_t * parse_mul_expr(std::istream& in)
break; 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); node = parse_mul_expr(in);
if (node && ! in.eof()) { if (node && ! in.eof()) {
char c = in.peek(); char c = peek_next_nonws(in);
while (c == '+' || c == '-') { while (c == '+' || c == '-') {
in.get(c); in.get(c);
switch (c) { switch (c) {
@ -569,7 +583,7 @@ node_t * parse_add_expr(std::istream& in)
break; 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; node_t * node = NULL;
if (in.peek() == '!') { if (peek_next_nonws(in) == '!') {
char c; char c;
in.get(c); in.get(c);
node = new node_t(node_t::O_NOT); 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); node = parse_add_expr(in);
if (node && ! in.eof()) { if (node && ! in.eof()) {
char c = in.peek(); char c = peek_next_nonws(in);
if (c == '=' || c == '<' || c == '>') { if (c == '=' || c == '<' || c == '>') {
in.get(c); in.get(c);
switch (c) { switch (c) {
@ -606,7 +620,7 @@ node_t * parse_logic_expr(std::istream& in)
case '<': { case '<': {
node_t * prev = node; node_t * prev = node;
node = new node_t(node_t::O_LT); node = new node_t(node_t::O_LT);
if (in.peek() == '=') { if (peek_next_nonws(in) == '=') {
in.get(c); in.get(c);
node->type = node_t::O_LTE; node->type = node_t::O_LTE;
} }
@ -618,7 +632,7 @@ node_t * parse_logic_expr(std::istream& in)
case '>': { case '>': {
node_t * prev = node; node_t * prev = node;
node = new node_t(node_t::O_GT); node = new node_t(node_t::O_GT);
if (in.peek() == '=') { if (peek_next_nonws(in) == '=') {
in.get(c); in.get(c);
node->type = node_t::O_GTE; node->type = node_t::O_GTE;
} }
@ -647,7 +661,7 @@ node_t * parse_expr(std::istream& in)
node = parse_logic_expr(in); node = parse_logic_expr(in);
if (node && ! in.eof()) { if (node && ! in.eof()) {
char c = in.peek(); char c = peek_next_nonws(in);
while (c == '&' || c == '|' || c == '?') { while (c == '&' || c == '|' || c == '?') {
in.get(c); in.get(c);
switch (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_t * choices = new node_t(node_t::O_COL);
node->right = choices; node->right = choices;
choices->left = parse_logic_expr(in); choices->left = parse_logic_expr(in);
c = in.peek(); c = peek_next_nonws(in);
if (c != ':') { if (c != ':') {
std::ostringstream err; std::ostringstream err;
err << "Unexpected character '" << c << "'"; err << "Unexpected character '" << c << "'";
@ -692,21 +706,57 @@ node_t * parse_expr(std::istream& in)
throw expr_error(err.str()); throw expr_error(err.str());
} }
} }
c = in.peek(); c = peek_next_nonws(in);
} }
} }
return node; 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.
#ifdef TEST 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;
}
namespace ledger { 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 += ")/";
}
static void dump_tree(std::ostream& out, node_t * node) return pred;
}
#ifdef DEBUG_ENABLED
void dump_tree(std::ostream& out, const node_t * node)
{ {
switch (node->type) { switch (node->type) {
case node_t::CONSTANT_A: case node_t::CONSTANT_A:
@ -834,8 +884,12 @@ static void dump_tree(std::ostream& out, node_t * node)
} }
} }
#endif // DEBUG_ENABLED
} // namespace ledger } // namespace ledger
#ifdef TEST
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
ledger::dump_tree(std::cout, ledger::parse_expr(argv[1])); ledger::dump_tree(std::cout, ledger::parse_expr(argv[1]));

View file

@ -134,13 +134,36 @@ inline node_t * find_node(node_t * node, node_t::kind_t type) {
return result; return result;
} }
#ifdef DEBUG_ENABLED
void dump_tree(std::ostream& out, const node_t * node);
#endif
template <typename T> template <typename T>
class item_predicate class item_predicate
{ {
const node_t * predicate; const node_t * predicate;
public: 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 { bool operator()(const T * item) const {
if (predicate) { 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 } // namespace report
#endif // _REPORT_H #endif // _REPORT_H

View file

@ -11,7 +11,7 @@ class sum_in_account
}; };
void calc__accounts(account_t * account, void calc__accounts(account_t * account,
item_predicate<transaction_t>& pred_functor, const item_predicate<transaction_t>& pred_functor,
unsigned int flags) unsigned int flags)
{ {
sum_in_account functor; sum_in_account functor;

6
walk.h
View file

@ -83,7 +83,7 @@ template <typename Function>
void walk_entries(entries_list::iterator begin, void walk_entries(entries_list::iterator begin,
entries_list::iterator end, entries_list::iterator end,
const Function& functor, const Function& functor,
const node_t * predicate, const std::string& predicate,
unsigned int flags) unsigned int flags)
{ {
item_predicate<transaction_t> pred_functor(predicate); 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, void calc__accounts(account_t * account,
item_predicate<transaction_t>& pred_functor, const item_predicate<transaction_t>& pred_functor,
unsigned int flags); unsigned int flags);
inline void sum__accounts(account_t * account) inline void sum__accounts(account_t * account)
@ -217,7 +217,7 @@ inline void sum__accounts(account_t * account)
template <typename Function> template <typename Function>
void walk_accounts(account_t * account, void walk_accounts(account_t * account,
const Function& functor, const Function& functor,
const node_t * predicate, const std::string& predicate,
unsigned int flags, unsigned int flags,
const bool calc_subtotals, const bool calc_subtotals,
const node_t * sort_order = NULL) const node_t * sort_order = NULL)