Tons of corrections and fixes to value expressions and lot figures.

This commit is contained in:
John Wiegley 2006-03-19 21:11:49 +00:00
parent ab748ed13e
commit 32bdfe20d9
17 changed files with 866 additions and 658 deletions

View file

@ -932,9 +932,41 @@ void parse_quantity(std::istream& in, std::string& value)
char c = peek_next_nonws(in); char c = peek_next_nonws(in);
READ_INTO(in, buf, 255, c, READ_INTO(in, buf, 255, c,
std::isdigit(c) || c == '-' || c == '.' || 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; 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) void parse_commodity(std::istream& in, std::string& symbol)
{ {
char buf[256]; char buf[256];
@ -947,8 +979,7 @@ void parse_commodity(std::istream& in, std::string& symbol)
else else
throw new amount_error("Quoted commodity symbol lacks closing quote"); throw new amount_error("Quoted commodity symbol lacks closing quote");
} else { } else {
READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) && READ_INTO(in, buf, 255, c, ! invalid_chars[(unsigned char)c]);
c != '-' && c != '.');
} }
symbol = buf; symbol = buf;
} }
@ -1041,7 +1072,7 @@ void amount_t::parse(std::istream& in, unsigned char flags)
} }
char n; char n;
if (std::isdigit(c) || c == '.') { if (std::isdigit(c)) {
parse_quantity(in, quant); parse_quantity(in, quant);
if (! in.eof() && ((n = in.peek()) != '\n')) { 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); 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); parse_annotations(in, price, date, tag);
} }
} }
@ -1651,6 +1682,9 @@ namespace {
const std::time_t date, const std::time_t date,
const std::string& tag) const std::string& tag)
{ {
if (price < 0)
throw new amount_error("A commodity's price may not be negative");
std::ostringstream name; std::ostringstream name;
comm.write(name); comm.write(name);

View file

@ -216,7 +216,15 @@ class balance_t
return true; return true;
} }
bool operator<(const amount_t& amt) const { bool operator<(const amount_t& amt) const {
if (amt.commodity())
return amount(amt.commodity()) < amt; 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> template <typename T>
bool operator<(T val) const { bool operator<(T val) const {
@ -244,7 +252,15 @@ class balance_t
return true; return true;
} }
bool operator<=(const amount_t& amt) const { bool operator<=(const amount_t& amt) const {
if (amt.commodity())
return amount(amt.commodity()) <= amt; 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> template <typename T>
bool operator<=(T val) const { bool operator<=(T val) const {
@ -275,7 +291,15 @@ class balance_t
return true; return true;
} }
bool operator>(const amount_t& amt) const { bool operator>(const amount_t& amt) const {
if (amt.commodity())
return amount(amt.commodity()) > amt; 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> template <typename T>
bool operator>(T val) const { bool operator>(T val) const {
@ -303,7 +327,15 @@ class balance_t
return true; return true;
} }
bool operator>=(const amount_t& amt) const { bool operator>=(const amount_t& amt) const {
if (amt.commodity())
return amount(amt.commodity()) >= amt; 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> template <typename T>
bool operator>=(T val) const { bool operator>=(T val) const {
@ -327,7 +359,15 @@ class balance_t
return i == amounts.end() && j == bal.amounts.end(); return i == amounts.end() && j == bal.amounts.end();
} }
bool operator==(const amount_t& amt) const { bool operator==(const amount_t& amt) const {
if (amt.commodity())
return amounts.size() == 1 && (*amounts.begin()).second == amt; 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> template <typename T>
bool operator==(T val) const { bool operator==(T val) const {

128
binary.cc
View file

@ -245,6 +245,31 @@ inline void read_binary_amount(char *& data, amount_t& amt)
amt.read_quantity(data); 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) inline void read_binary_mask(char *& data, mask_t *& mask)
{ {
bool exclude; bool exclude;
@ -274,18 +299,13 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr)
} }
switch (expr->kind) { switch (expr->kind) {
case value_expr_t::CONSTANT_T: case value_expr_t::O_ARG:
read_binary_number(data, expr->constant_t); case value_expr_t::INDEX:
read_binary_long(data, expr->arg_index);
break; break;
case value_expr_t::CONSTANT_I: case value_expr_t::CONSTANT:
read_binary_long(data, expr->constant_i); expr->value = new value_t;
break; read_binary_value(data, *expr->value);
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);
break; break;
case value_expr_t::F_CODE_MASK: 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); read_binary_long(data, xact->_date_eff);
xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1]; xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1];
if (read_binary_number<char>(data) == 1) { char flag = read_binary_number<char>(data);
read_binary_value_expr(data, xact->amount_expr); if (flag == 0) {
if (xact->amount_expr) xact->amount_expr->acquire();
} else {
read_binary_amount(data, xact->amount); 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) { if (*data++ == 1) {
xact->cost = new amount_t; xact->cost = new amount_t;
read_binary_amount(data, *xact->cost); read_binary_amount(data, *xact->cost);
read_binary_string(data, xact->cost_expr);
} else { } else {
xact->cost = NULL; 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); for (unsigned long i = 0, count = read_binary_long<unsigned long>(data);
i < count; i < count;
i++) { i++) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); new(xact_pool) transaction_t;
read_binary_transaction(data, xact_pool); read_binary_transaction(data, xact_pool);
if (ignore_calculated && xact_pool->flags & TRANSACTION_CALCULATED) if (ignore_calculated && xact_pool->flags & TRANSACTION_CALCULATED)
finalize = true; finalize = true;
@ -601,6 +631,9 @@ unsigned int read_binary_journal(std::istream& in,
char * item_pool = new char[pool_size]; 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; entry_t * entry_pool = (entry_t *) item_pool;
transaction_t * xact_pool = (transaction_t *) (item_pool + transaction_t * xact_pool = (transaction_t *) (item_pool +
sizeof(entry_t) * count); 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 // 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[] accounts;
delete[] commodities; delete[] commodities;
delete[] data_pool; delete[] data_pool;
@ -822,6 +852,30 @@ void write_binary_amount(std::ostream& out, const amount_t& amt)
amt.write_quantity(out); 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) void write_binary_mask(std::ostream& out, mask_t * mask)
{ {
write_binary_number(out, mask->exclude); 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); write_binary_value_expr(out, expr->left);
switch (expr->kind) { switch (expr->kind) {
case value_expr_t::CONSTANT_T: case value_expr_t::O_ARG:
write_binary_number(out, expr->constant_t); case value_expr_t::INDEX:
write_binary_long(out, expr->arg_index);
break; break;
case value_expr_t::CONSTANT_I: case value_expr_t::CONSTANT:
write_binary_long(out, expr->constant_i); write_binary_value(out, *expr->value);
break;
case value_expr_t::CONSTANT_A:
write_binary_amount(out, *(expr->constant_a));
break;
case value_expr_t::CONSTANT_V:
assert(0);
break; break;
case value_expr_t::F_CODE_MASK: case value_expr_t::F_CODE_MASK:
@ -884,14 +933,22 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
write_binary_long(out, xact->_date_eff); write_binary_long(out, xact->_date_eff);
write_binary_long(out, xact->account->ident); write_binary_long(out, xact->account->ident);
if (xact->amount_expr) { if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) {
write_binary_number<char>(out, 1);
write_binary_value_expr(out, xact->amount_expr);
} else {
write_binary_number<char>(out, 0); write_binary_number<char>(out, 0);
if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED)
write_binary_amount(out, amount_t()); write_binary_amount(out, amount_t());
else }
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_amount(out, xact->amount);
write_binary_string(out, xact->amount_expr.expr);
}
else {
write_binary_number<char>(out, 0);
write_binary_amount(out, xact->amount); write_binary_amount(out, xact->amount);
} }
@ -899,6 +956,7 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
(! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) { (! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) {
write_binary_number<char>(out, 1); write_binary_number<char>(out, 1);
write_binary_amount(out, *xact->cost); write_binary_amount(out, *xact->cost);
write_binary_string(out, xact->cost_expr);
} else { } else {
write_binary_number<char>(out, 0); write_binary_number<char>(out, 0);
} }

View file

@ -31,8 +31,8 @@ namespace {
void xact_amount(value_t& result, const details_t& details, value_expr_t *) void xact_amount(value_t& result, const details_t& details, value_expr_t *)
{ {
if (transaction_has_xdata(*details.xact) && if (transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOSITE) transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOUND)
result = transaction_xdata_(*details.xact).composite_amount; result = transaction_xdata_(*details.xact).value;
else else
result = details.xact->amount; result = details.xact->amount;
} }
@ -115,8 +115,8 @@ std::string resolve_path(const std::string& path)
void config_t::reset() void config_t::reset()
{ {
ledger::amount_expr.reset(new value_expr("a")); ledger::amount_expr = "@a";
ledger::total_expr.reset(new value_expr("O")); ledger::total_expr = "@O";
pricing_leeway = 24 * 3600; pricing_leeway = 24 * 3600;
budget_flags = BUDGET_NO_BUDGET; budget_flags = BUDGET_NO_BUDGET;
@ -126,8 +126,8 @@ void config_t::reset()
wide_register_format = ("%D %-.35P %-.38A %22.108t %!22.132T\n%/" wide_register_format = ("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
"%48|%-.38A %22.108t %!22.132T\n"); "%48|%-.38A %22.108t %!22.132T\n");
csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n"; csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n";
plot_amount_format = "%D %(S(t))\n"; plot_amount_format = "%D %(@S(@t))\n";
plot_total_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"; 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_hdr_format = "%d %Y%C%P\n";
write_xact_format = " %-34W %12o%n\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 // Process remaining command-line arguments
if (command != "e") { 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 // Setup the values of %t and %T, used in format strings
if (! amount_expr.empty()) if (! amount_expr.empty())
ledger::amount_expr.reset(new value_expr(amount_expr)); ledger::amount_expr = amount_expr;
if (! total_expr.empty()) 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 // If downloading is to be supported, configure the updater
@ -1097,6 +1100,13 @@ OPT_BEGIN(period_sort, ":") {
config->report_period_sort = optarg; config->report_period_sort = optarg;
} OPT_END(period_sort); } 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") { OPT_BEGIN(weekly, "W") {
if (config->report_period.empty()) if (config->report_period.empty())
config->report_period = "weekly"; config->report_period = "weekly";
@ -1179,11 +1189,11 @@ OPT_BEGIN(display, "d:") {
} OPT_END(display); } OPT_END(display);
OPT_BEGIN(amount, "t:") { OPT_BEGIN(amount, "t:") {
ledger::amount_expr.reset(new value_expr(optarg)); ledger::amount_expr = optarg;
} OPT_END(amount); } OPT_END(amount);
OPT_BEGIN(total, "T:") { OPT_BEGIN(total, "T:") {
ledger::total_expr.reset(new value_expr(optarg)); ledger::total_expr = optarg;
} OPT_END(total); } OPT_END(total);
OPT_BEGIN(amount_data, "j") { OPT_BEGIN(amount_data, "j") {
@ -1225,25 +1235,25 @@ OPT_BEGIN(download, "Q") {
} OPT_END(download); } OPT_END(download);
OPT_BEGIN(quantity, "O") { OPT_BEGIN(quantity, "O") {
ledger::amount_expr.reset(new value_expr("a")); ledger::amount_expr = "@a";
ledger::total_expr.reset(new value_expr("O")); ledger::total_expr = "@O";
} OPT_END(quantity); } OPT_END(quantity);
OPT_BEGIN(basis, "B") { OPT_BEGIN(basis, "B") {
ledger::amount_expr.reset(new value_expr("b")); ledger::amount_expr = "@b";
ledger::total_expr.reset(new value_expr("B")); ledger::total_expr = "@B";
} OPT_END(basis); } OPT_END(basis);
OPT_BEGIN(price, "I") { OPT_BEGIN(price, "I") {
ledger::amount_expr.reset(new value_expr("i")); ledger::amount_expr = "@i";
ledger::total_expr.reset(new value_expr("I")); ledger::total_expr = "@I";
} OPT_END(price); } OPT_END(price);
OPT_BEGIN(market, "V") { OPT_BEGIN(market, "V") {
config->show_revalued = true; config->show_revalued = true;
ledger::amount_expr.reset(new value_expr("v")); ledger::amount_expr = "@v";
ledger::total_expr.reset(new value_expr("V")); ledger::total_expr = "@V";
} OPT_END(market); } OPT_END(market);
namespace { namespace {
@ -1279,34 +1289,29 @@ OPT_BEGIN(set_price, ":") {
} OPT_END(set_price); } OPT_END(set_price);
OPT_BEGIN(performance, "g") { OPT_BEGIN(performance, "g") {
ledger::amount_expr.reset(new value_expr("P(a,m)-b")); ledger::amount_expr = "@P(@a,@m)-@b";
ledger::total_expr.reset(new value_expr("P(O,m)-B")); ledger::total_expr = "@P(@O,@m)-@B";
} OPT_END(performance); } OPT_END(performance);
OPT_BEGIN(gain, "G") { OPT_BEGIN(gain, "G") {
config->show_revalued = config->show_revalued =
config->show_revalued_only = true; config->show_revalued_only = true;
ledger::amount_expr.reset(new value_expr("a")); ledger::amount_expr = "@a";
ledger::total_expr.reset(new value_expr("G")); ledger::total_expr = "@G";
} OPT_END(gain); } OPT_END(gain);
OPT_BEGIN(average, "A") { OPT_BEGIN(average, "A") {
ledger::total_expr.reset ledger::total_expr = expand_value_expr("@A(#)", ledger::total_expr.expr);
(new value_expr(expand_value_expr("A(#)", ledger::total_expr->expr)));
} OPT_END(average); } OPT_END(average);
OPT_BEGIN(deviation, "D") { OPT_BEGIN(deviation, "D") {
ledger::total_expr.reset(new value_expr("O")); ledger::total_expr = expand_value_expr("@t-@A(#)", ledger::total_expr.expr);
ledger::total_expr.reset
(new value_expr(expand_value_expr("t-A(#)", ledger::total_expr->expr)));
} OPT_END(deviation); } OPT_END(deviation);
OPT_BEGIN(percentage, "%") { OPT_BEGIN(percentage, "%") {
ledger::total_expr.reset(new value_expr("O")); ledger::total_expr = expand_value_expr("^#&{100.0%}*(#/^#)",
ledger::total_expr.reset ledger::total_expr.expr);
(new value_expr(expand_value_expr("^#&{100.0%}*(#/^#)",
ledger::total_expr->expr)));
} OPT_END(percentage); } OPT_END(percentage);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -1332,6 +1337,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "comm-as-payee", 'x', false, opt_comm_as_payee, false }, { "comm-as-payee", 'x', false, opt_comm_as_payee, false },
{ "csv-register-format", '\0', true, opt_csv_register_format, false }, { "csv-register-format", '\0', true, opt_csv_register_format, false },
{ "current", 'c', false, opt_current, false }, { "current", 'c', false, opt_current, false },
{ "daily", '\0', false, opt_daily, false },
{ "date-format", 'y', true, opt_date_format, false }, { "date-format", 'y', true, opt_date_format, false },
{ "debug", '\0', true, opt_debug, false }, { "debug", '\0', true, opt_debug, false },
{ "descend", '\0', true, opt_descend, false }, { "descend", '\0', true, opt_descend, false },

View file

@ -107,7 +107,7 @@ class config_t
std::list<item_handler<transaction_t> *>& ptrs); 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]; extern option_t config_options[CONFIG_OPTIONS_SIZE];
void option_help(std::ostream& out); void option_help(std::ostream& out);

View file

@ -167,8 +167,9 @@ entry_t * derive_new_entry(journal_t& journal,
} }
done: done:
if (! run_hooks(journal.entry_finalize_hooks, *added) || if (! run_hooks(journal.entry_finalize_hooks, *added, false) ||
! added->finalize()) ! added->finalize() ||
! run_hooks(journal.entry_finalize_hooks, *added, true))
throw new error("Failed to finalize derived entry (check commodities)"); throw new error("Failed to finalize derived entry (check commodities)");
return added.release(); return added.release();

View file

@ -172,7 +172,7 @@ element_t * format_t::parse_elements(const std::string& fmt)
current->type = element_t::VALUE_EXPR; current->type = element_t::VALUE_EXPR;
assert(! current->val_expr); assert(! current->val_expr);
current->val_expr = new value_expr(std::string(b, p)); current->val_expr = std::string(b, p);
break; break;
} }
@ -275,7 +275,7 @@ static bool entry_state(const entry_t * entry, transaction_t::state_t * state)
} }
namespace { 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.setf(std::ios::left);
out.width(0); out.width(0);
out << "\e[31m"; out << "\e[31m";
@ -289,7 +289,7 @@ namespace {
out.width(elem->min_width); out.width(elem->min_width);
} }
void mark_plain(std::ostream& out) { inline void mark_plain(std::ostream& out) {
out << "\e[0m"; 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::AMOUNT:
case element_t::TOTAL: case element_t::TOTAL:
case element_t::VALUE_EXPR: { case element_t::VALUE_EXPR: {
value_expr * calc = NULL; value_expr calc;
switch (elem->type) { switch (elem->type) {
case element_t::AMOUNT: calc = amount_expr.get(); break; case element_t::AMOUNT: calc = amount_expr; break;
case element_t::TOTAL: calc = total_expr.get(); break; case element_t::TOTAL: calc = total_expr; break;
case element_t::VALUE_EXPR: calc = elem->val_expr; break; case element_t::VALUE_EXPR: calc = elem->val_expr; break;
default: default:
assert(0); 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) { switch (value.type) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
out << (*((bool *) value.data) ? "true" : "false"); 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: case value_t::INTEGER:
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) { if (ansi_invert) {
if (*((long *) value.data) > 0) if (*((long *) value.data) > 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true;
}
} else { } else {
if (*((long *) value.data) < 0) if (*((long *) value.data) < 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true;
}
} }
} }
out << *((long *) value.data); 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: case value_t::AMOUNT:
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) { if (ansi_invert) {
if (*((amount_t *) value.data) > 0) if (*((amount_t *) value.data) > 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true;
}
} else { } else {
if (*((amount_t *) value.data) < 0) if (*((amount_t *) value.data) < 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true;
}
} }
} }
out << *((amount_t *) value.data); 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_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) { if (ansi_invert) {
if (*bal > 0) if (*bal > 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true;
}
} else { } else {
if (*bal < 0) if (*bal < 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true;
}
} }
} }
bal->write(out, elem->min_width, bal->write(out, elem->min_width,
@ -411,7 +425,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
break; break;
} }
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) if (highlighted)
mark_plain(out); mark_plain(out);
break; break;
} }
@ -423,8 +437,15 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
if (details.xact->cost && details.xact->amount) { if (details.xact->cost && details.xact->amount) {
std::ostringstream stream; std::ostringstream stream;
stream << details.xact->amount << " @ " if (! details.xact->amount_expr.expr.empty())
<< amount_t(*details.xact->cost / 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(); details.xact->amount).unround();
disp = stream.str(); disp = stream.str();
use_disp = true; use_disp = true;
@ -450,11 +471,15 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
first->amount == - last->amount); first->amount == - last->amount);
} }
if (! use_disp) if (! use_disp) {
out << details.xact->amount; if (! details.xact->amount_expr.expr.empty())
out << details.xact->amount_expr.expr;
else else
out << details.xact->amount.strip_annotations();
} else {
out << disp; out << disp;
} }
}
break; break;
case element_t::SOURCE: case element_t::SOURCE:

View file

@ -53,19 +53,17 @@ struct element_t
std::string chars; std::string chars;
unsigned char min_width; unsigned char min_width;
unsigned char max_width; unsigned char max_width;
value_expr * val_expr; value_expr val_expr;
struct element_t * next; struct element_t * next;
element_t() : type(STRING), flags(false), element_t() : type(STRING), flags(false),
min_width(0), max_width(0), min_width(0), max_width(0), next(NULL) {
val_expr(NULL), next(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor element_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor element_t");
} }
~element_t() { ~element_t() {
DEBUG_PRINT("ledger.memory.dtors", "dtor 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 if (next) delete next; // recursive, but not too deep
} }
}; };

View file

@ -17,7 +17,6 @@ transaction_t::~transaction_t()
{ {
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t"); DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t");
if (cost) delete cost; if (cost) delete cost;
if (amount_expr) amount_expr->release();
} }
std::time_t transaction_t::actual_date() const std::time_t transaction_t::actual_date() const
@ -74,7 +73,7 @@ bool transaction_t::valid() const
return false; return false;
} }
if (flags & ~0x001f) { if (flags & ~0x003f) {
DEBUG_PRINT("ledger.validate", "transaction_t: flags are bad"); DEBUG_PRINT("ledger.validate", "transaction_t: flags are bad");
return false; return false;
} }
@ -316,35 +315,41 @@ auto_entry_t::~auto_entry_t()
delete predicate; 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(), transactions_list initial_xacts(entry.transactions.begin(),
entry.transactions.end()); entry.transactions.end());
for (transactions_list::iterator i = initial_xacts.begin(); for (transactions_list::iterator i = initial_xacts.begin();
i != initial_xacts.end(); i != initial_xacts.end();
i++) i++) {
if ((*predicate)(**i)) if ((*predicate)(**i)) {
for (transactions_list::iterator t = transactions.begin(); for (transactions_list::iterator t = transactions.begin();
t != transactions.end(); t != transactions.end();
t++) { t++) {
amount_t amt; amount_t amt;
if (! (*t)->amount.commodity()) if (! (*t)->amount.commodity()) {
if (! post)
continue;
amt = (*i)->amount * (*t)->amount; amt = (*i)->amount * (*t)->amount;
else } else {
if (post)
continue;
amt = (*t)->amount; amt = (*t)->amount;
}
account_t * account = (*t)->account; account_t * account = (*t)->account;
std::string fullname = account->fullname(); std::string fullname = account->fullname();
assert(! fullname.empty()); assert(! fullname.empty());
if (fullname == "$account" || fullname == "@account")
if (fullname == "$account")
account = (*i)->account; account = (*i)->account;
transaction_t * xact transaction_t * xact
= new transaction_t(account, amt, (*t)->flags | TRANSACTION_AUTO); = new transaction_t(account, amt, (*t)->flags | TRANSACTION_AUTO);
entry.add_transaction(xact); entry.add_transaction(xact);
} }
}
}
} }
account_t::~account_t() account_t::~account_t()
@ -514,7 +519,9 @@ bool journal_t::add_entry(entry_t * entry)
{ {
entry->journal = this; 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; entry->journal = NULL;
return false; return false;
} }

View file

@ -10,6 +10,7 @@
#include "amount.h" #include "amount.h"
#include "datetime.h" #include "datetime.h"
#include "value.h" #include "value.h"
#include "valexpr.h"
#include "error.h" #include "error.h"
#include "debug.h" #include "debug.h"
#include "util.h" #include "util.h"
@ -26,7 +27,6 @@ namespace ledger {
class entry_t; class entry_t;
class account_t; class account_t;
class value_expr_t;
class transaction_t class transaction_t
{ {
@ -38,8 +38,9 @@ class transaction_t
std::time_t _date_eff; std::time_t _date_eff;
account_t * account; account_t * account;
amount_t amount; amount_t amount;
value_expr_t * amount_expr; value_expr amount_expr;
amount_t * cost; amount_t * cost;
std::string cost_expr;
state_t state; state_t state;
unsigned short flags; unsigned short flags;
std::string note; std::string note;
@ -53,9 +54,8 @@ class transaction_t
transaction_t(account_t * _account = NULL) transaction_t(account_t * _account = NULL)
: entry(NULL), _date(0), _date_eff(0), account(_account), : entry(NULL), _date(0), _date_eff(0), account(_account),
amount_expr(NULL), cost(NULL), state(UNCLEARED), cost(NULL), state(UNCLEARED), flags(TRANSACTION_NORMAL),
flags(TRANSACTION_NORMAL), beg_pos(0), beg_line(0), beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
end_pos(0), end_line(0), data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
} }
transaction_t(account_t * _account, transaction_t(account_t * _account,
@ -63,15 +63,14 @@ class transaction_t
unsigned int _flags = TRANSACTION_NORMAL, unsigned int _flags = TRANSACTION_NORMAL,
const std::string& _note = "") const std::string& _note = "")
: entry(NULL), _date(0), _date_eff(0), account(_account), : entry(NULL), _date(0), _date_eff(0), account(_account),
amount(_amount), amount_expr(NULL), cost(NULL), amount(_amount), cost(NULL), state(UNCLEARED), flags(_flags),
state(UNCLEARED), flags(_flags), note(_note), note(_note), beg_pos(0), beg_line(0), end_pos(0), end_line(0),
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) { data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
} }
transaction_t(const transaction_t& xact) transaction_t(const transaction_t& xact)
: entry(xact.entry), _date(0), _date_eff(0), account(xact.account), : entry(xact.entry), _date(0), _date_eff(0), account(xact.account),
amount(xact.amount), amount_expr(NULL), amount(xact.amount), cost(xact.cost ? new amount_t(*xact.cost) : NULL),
cost(xact.cost ? new amount_t(*xact.cost) : NULL),
state(xact.state), flags(xact.flags), note(xact.note), state(xact.state), flags(xact.flags), note(xact.note),
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) { beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
@ -199,7 +198,7 @@ class entry_t : public entry_base_t
struct entry_finalizer_t { struct entry_finalizer_t {
virtual ~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 { class entry_context : public error_context {
@ -238,7 +237,7 @@ public:
virtual ~auto_entry_t(); 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 { virtual bool valid() const {
return true; return true;
} }
@ -248,7 +247,7 @@ class journal_t;
struct auto_entry_finalizer_t : public entry_finalizer_t { struct auto_entry_finalizer_t : public entry_finalizer_t {
journal_t * journal; journal_t * journal;
auto_entry_finalizer_t(journal_t * _journal) : journal(_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 { 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_t func;
func_finalizer_t(func_t _func) : func(_func) {} func_finalizer_t(func_t _func) : func(_func) {}
func_finalizer_t(const func_finalizer_t& other) : func(other.func) {} func_finalizer_t(const func_finalizer_t& other) : func(other.func) {}
virtual bool operator()(entry_t& entry) { virtual bool operator()(entry_t& entry, bool post) {
return func(entry); return func(entry, post);
} }
}; };
@ -365,11 +364,11 @@ void remove_hook(std::list<T>& list, T obj) {
} }
template <typename T, typename Data> 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(); for (typename std::list<T>::const_iterator i = list.begin();
i != list.end(); i != list.end();
i++) i++)
if (! (*(*i))(item)) if (! (*(*i))(item, post))
return false; return false;
return true; return true;
} }
@ -446,15 +445,16 @@ class journal_t
bool valid() const; 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(); for (auto_entries_list::iterator i = journal->auto_entries.begin();
i != journal->auto_entries.end(); i != journal->auto_entries.end();
i++) i++)
(*i)->extend_entry(entry); (*i)->extend_entry(entry, post);
} }
inline bool auto_entry_finalizer_t::operator()(entry_t& entry) { inline bool auto_entry_finalizer_t::operator()(entry_t& entry, bool post) {
extend_entry_base(journal, entry); extend_entry_base(journal, entry, post);
return true; return true;
} }

View file

@ -100,9 +100,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
command = "p"; command = "p";
else if (command == "output") else if (command == "output")
command = "w"; command = "w";
else if (command == "emacs") else if (command == "emacs" || command == "lisp")
command = "x";
else if (command == "lisp")
command = "x"; command = "x";
else if (command == "xml") else if (command == "xml")
command = "X"; command = "X";
@ -119,7 +117,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
command = "r"; command = "r";
} }
else if (command == "parse") { 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) { if (config.verbose_mode) {
std::cout << "Value expression tree:" << std::endl; std::cout << "Value expression tree:" << std::endl;
ledger::dump_value_expr(std::cout, expr.get()); 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. // Are we handling the parse or expr commands? Do so now.
if (command == "expr") { if (command == "expr") {
value_auto_ptr expr(ledger::parse_value_expr(*arg)); value_expr expr(ledger::parse_value_expr(*arg));
if (config.verbose_mode) { if (config.verbose_mode) {
std::cout << "Value expression tree:" << std::endl; std::cout << "Value expression tree:" << std::endl;
ledger::dump_value_expr(std::cout, expr.get()); ledger::dump_value_expr(std::cout, expr.get());

View file

@ -64,6 +64,35 @@ inline char * next_element(char * buf, bool variable = false)
return NULL; 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, transaction_t * parse_transaction(char * line, account_t * account,
entry_t * entry = NULL) entry_t * entry = NULL)
{ {
@ -146,54 +175,20 @@ transaction_t * parse_transaction(char * line, account_t * account,
goto finished; goto finished;
if (p == ';') if (p == ';')
goto parse_note; goto parse_note;
if (p == '(') {
try { try {
xact->amount_expr = parse_value_expr(in)->acquire(); 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) { catch (error * err) {
err_desc = "While parsing transaction amount's value expression:"; err_desc = "While parsing transaction amount:";
throw err; 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);
}
}
} }
// Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST) // Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST)
@ -215,13 +210,28 @@ transaction_t * parse_transaction(char * line, account_t * account,
if (in.good() && ! in.eof()) { if (in.good() && ! in.eof()) {
xact->cost = new amount_t; xact->cost = new amount_t;
p = peek_next_nonws(in); try {
if (p == '(') unsigned long beg = (long)in.tellg();
throw new parse_error("A transaction's cost may not be a value expression");
xact->cost->parse(in, AMOUNT_PARSE_NO_MIGRATE); if (parse_amount_expr(in, *xact->cost, xact.get()))
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << throw new parse_error("A transaction's cost must evalute to a constant value");
"Parsed cost " << *xact->cost);
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); amount_t per_unit_cost(*xact->cost);
if (per_unit) if (per_unit)
@ -730,7 +740,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
if (parse_transactions(in, account_stack.front(), *pe, if (parse_transactions(in, account_stack.front(), *pe,
"period", end_pos)) { "period", end_pos)) {
if (pe->finalize()) { if (pe->finalize()) {
extend_entry_base(journal, *pe); extend_entry_base(journal, *pe, true);
journal->period_entries.push_back(pe); journal->period_entries.push_back(pe);
pe->src_idx = src_idx; pe->src_idx = src_idx;
pe->beg_pos = beg_pos; pe->beg_pos = beg_pos;

File diff suppressed because it is too large Load diff

179
valexpr.h
View file

@ -1,7 +1,6 @@
#ifndef _VALEXPR_H #ifndef _VALEXPR_H
#define _VALEXPR_H #define _VALEXPR_H
#include "journal.h"
#include "value.h" #include "value.h"
#include "error.h" #include "error.h"
#include "mask.h" #include "mask.h"
@ -10,6 +9,10 @@
namespace ledger { namespace ledger {
class entry_t;
class transaction_t;
class account_t;
struct details_t struct details_t
{ {
const entry_t * entry; const entry_t * entry;
@ -37,10 +40,9 @@ struct value_expr_t
{ {
enum kind_t { enum kind_t {
// Constants // Constants
CONSTANT_I, CONSTANT,
CONSTANT_T, ARG_INDEX,
CONSTANT_A, ZERO,
CONSTANT_V,
CONSTANTS, CONSTANTS,
@ -125,11 +127,9 @@ struct value_expr_t
value_expr_t * left; value_expr_t * left;
union { union {
datetime_t * constant_t; value_t * value;
long constant_i;
amount_t * constant_a;
value_t * constant_v;
mask_t * mask; mask_t * mask;
unsigned int arg_index; // used by ARG_INDEX and O_ARG
value_expr_t * right; value_expr_t * right;
}; };
@ -137,9 +137,6 @@ struct value_expr_t
: kind(_kind), refc(0), left(NULL), right(NULL) { : kind(_kind), refc(0), left(NULL), right(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t " << this); 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(); ~value_expr_t();
void release() const { void release() const {
@ -180,12 +177,18 @@ struct value_expr_t
void compute(value_t& result, void compute(value_t& result,
const details_t& details = details_t(), const details_t& details = details_t(),
value_expr_t * context = NULL) const; value_expr_t * context = NULL) const;
value_t compute(const details_t& details = details_t(), value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) const { value_expr_t * context = NULL) const {
value_t temp; value_t temp;
compute(temp, details, context); compute(temp, details, context);
return temp; 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 { class valexpr_context : public error_context {
@ -273,37 +276,21 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
const transaction_t * xact, const transaction_t * xact,
value_expr_t * context = NULL); value_expr_t * context = NULL);
struct scope_t; #define PARSE_VALEXPR_NORMAL 0x00
value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope); #define PARSE_VALEXPR_PARTIAL 0x01
#define PARSE_VALEXPR_RELAXED 0x02
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);
}
value_expr_t * parse_value_expr(std::istream& in, value_expr_t * parse_value_expr(std::istream& in,
scope_t * scope = NULL, 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, inline value_expr_t *
parse_value_expr(const std::string& str,
scope_t * scope = NULL, scope_t * scope = NULL,
const bool partial = false) { const short flags = PARSE_VALEXPR_RELAXED) {
std::istringstream stream(str); std::istringstream stream(str);
try { try {
return parse_value_expr(stream, scope, partial); return parse_value_expr(stream, scope, flags);
} }
catch (error * err) { catch (error * err) {
err->context.push_back 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, inline value_expr_t *
parse_value_expr(const char * p,
scope_t * scope = NULL, scope_t * scope = NULL,
const bool partial = false) { const short flags = PARSE_VALEXPR_RELAXED) {
return parse_value_expr(std::string(p), scope, partial); return parse_value_expr(std::string(p), scope, flags);
} }
void dump_value_expr(std::ostream& out, const value_expr_t * node, 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, bool write_value_expr(std::ostream& out,
const value_expr_t * node, const value_expr_t * node,
const bool relaxed = true,
const value_expr_t * node_to_find = NULL, const value_expr_t * node_to_find = NULL,
unsigned long * start_pos = NULL, unsigned long * start_pos = NULL,
unsigned long * end_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_expr_t * ptr;
value_auto_ptr() : ptr(NULL) {} public:
explicit value_auto_ptr(value_expr_t * _ptr) std::string expr;
: ptr(_ptr ? _ptr->acquire() : NULL) {}
~value_auto_ptr() { 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) if (ptr)
ptr->release(); 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() { value_expr_t& operator*() const throw() {
return *ptr; return *ptr;
} }
value_expr_t * operator->() const throw() { value_expr_t * operator->() const throw() {
return ptr; return ptr;
} }
value_expr_t * get() const throw() { return ptr; } value_expr_t * get() const throw() { return ptr; }
value_expr_t * release() throw() { value_expr_t * release() throw() {
value_expr_t * tmp = ptr; value_expr_t * tmp = ptr;
@ -390,41 +422,19 @@ struct value_auto_ptr {
if (p != ptr) { if (p != ptr) {
if (ptr) if (ptr)
ptr->release(); 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, virtual void compute(value_t& result,
const details_t& details = details_t(), const details_t& details = details_t(),
value_expr_t * context = NULL) { 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(), virtual value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) { value_expr_t * context = NULL) {
value_t temp; value_t temp;
guarded_compute(parsed, temp, details, context); guarded_compute(ptr, temp, details, context);
return temp; return temp;
} }
@ -435,35 +445,40 @@ public:
unsigned long * end_pos); unsigned long * end_pos);
}; };
extern std::auto_ptr<value_expr> amount_expr; extern value_expr amount_expr;
extern std::auto_ptr<value_expr> total_expr; extern value_expr total_expr;
inline void compute_amount(value_t& result, inline void compute_amount(value_t& result,
const details_t& details = details_t()) { const details_t& details = details_t()) {
if (amount_expr.get()) if (amount_expr)
amount_expr->compute(result, details); amount_expr->compute(result, details);
} }
inline value_t compute_amount(const details_t& details = details_t()) { inline value_t compute_amount(const details_t& details = details_t()) {
if (amount_expr.get()) if (amount_expr)
return amount_expr->compute(details); return amount_expr->compute(details);
} }
inline void compute_total(value_t& result, inline void compute_total(value_t& result,
const details_t& details = details_t()) { const details_t& details = details_t()) {
if (total_expr.get()) if (total_expr)
total_expr->compute(result, details); total_expr->compute(result, details);
} }
inline value_t compute_total(const details_t& details = details_t()) { inline value_t compute_total(const details_t& details = details_t()) {
if (total_expr.get()) if (total_expr)
return total_expr->compute(details); 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, inline void parse_value_definition(const std::string& str,
scope_t * scope = NULL) { scope_t * scope = NULL) {
value_auto_ptr expr std::istringstream def(str);
(parse_boolean_expr(str, scope ? scope : global_scope.get())); value_expr expr
(parse_boolean_expr(def, scope ? scope : global_scope.get(),
PARSE_VALEXPR_RELAXED));
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

48
walk.cc
View file

@ -39,8 +39,8 @@ transaction_xdata_t& transaction_xdata(const transaction_t& xact)
void add_transaction_to(const transaction_t& xact, value_t& value) void add_transaction_to(const transaction_t& xact, value_t& value)
{ {
if (transaction_has_xdata(xact) && if (transaction_has_xdata(xact) &&
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) { transaction_xdata_(xact).dflags & TRANSACTION_COMPOUND) {
value += transaction_xdata_(xact).composite_amount; value += transaction_xdata_(xact).value;
} }
else if (xact.cost || ! value.realzero()) { else if (xact.cost || ! value.realzero()) {
value.add(xact.amount, xact.cost); value.add(xact.amount, xact.cost);
@ -162,8 +162,8 @@ void calc_transactions::operator()(transaction_t& xact)
void invert_transactions::operator()(transaction_t& xact) void invert_transactions::operator()(transaction_t& xact)
{ {
if (transaction_has_xdata(xact) && if (transaction_has_xdata(xact) &&
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) { transaction_xdata_(xact).dflags & TRANSACTION_COMPOUND) {
transaction_xdata_(xact).composite_amount.negate(); transaction_xdata_(xact).value.negate();
} else { } else {
xact.amount.negate(); xact.amount.negate();
if (xact.cost) if (xact.cost)
@ -209,29 +209,31 @@ void handle_value(const value_t& value,
transaction_xdata_t& xdata(transaction_xdata(xact)); transaction_xdata_t& xdata(transaction_xdata(xact));
switch (value.type) {
case value_t::BOOLEAN:
xact.amount = *((bool *) value.data);
break;
case value_t::INTEGER:
xact.amount = *((long *) value.data);
break;
case value_t::AMOUNT:
xact.amount = *((amount_t *) value.data);
break;
case value_t::BALANCE:
case value_t::BALANCE_PAIR:
xdata.composite_amount = value;
flags |= TRANSACTION_COMPOSITE;
break;
}
if (date) if (date)
xdata.date = date; xdata.date = date;
if (flags) if (flags)
xdata.dflags |= flags; xdata.dflags |= flags;
value_t temp(value);
switch (value.type) {
case value_t::BOOLEAN:
case value_t::DATETIME:
case value_t::INTEGER:
temp.cast(value_t::AMOUNT);
// fall through...
case value_t::AMOUNT:
xact.amount = *((amount_t *) temp.data);
break;
case value_t::BALANCE:
case value_t::BALANCE_PAIR:
xdata.value = temp;
flags |= TRANSACTION_COMPOUND;
break;
}
handler(xact); handler(xact);
} }
@ -830,7 +832,7 @@ void walk_accounts(account_t& account,
const std::string& sort_string) const std::string& sort_string)
{ {
if (! sort_string.empty()) { if (! sort_string.empty()) {
value_auto_ptr sort_order; value_expr sort_order;
sort_order.reset(parse_value_expr(sort_string)); sort_order.reset(parse_value_expr(sort_string));
walk_accounts(account, handler, sort_order.get()); walk_accounts(account, handler, sort_order.get());
} else { } else {

4
walk.h
View file

@ -80,14 +80,14 @@ bool compare_items<account_t>::operator()(const account_t * left,
#define TRANSACTION_DISPLAYED 0x0008 #define TRANSACTION_DISPLAYED 0x0008
#define TRANSACTION_NO_TOTAL 0x0010 #define TRANSACTION_NO_TOTAL 0x0010
#define TRANSACTION_SORT_CALC 0x0020 #define TRANSACTION_SORT_CALC 0x0020
#define TRANSACTION_COMPOSITE 0x0040 #define TRANSACTION_COMPOUND 0x0040
#define TRANSACTION_MATCHES 0x0080 #define TRANSACTION_MATCHES 0x0080
struct transaction_xdata_t struct transaction_xdata_t
{ {
value_t total; value_t total;
value_t sort_value; value_t sort_value;
value_t composite_amount; value_t value;
unsigned int index; unsigned int index;
unsigned short dflags; unsigned short dflags;
std::time_t date; std::time_t date;

4
xml.cc
View file

@ -435,9 +435,9 @@ void format_xml_entries::format_last_entry()
} }
output_stream << " <tr:amount>\n"; output_stream << " <tr:amount>\n";
if (transaction_xdata_(**i).dflags & TRANSACTION_COMPOSITE) if (transaction_xdata_(**i).dflags & TRANSACTION_COMPOUND)
xml_write_value(output_stream, xml_write_value(output_stream,
transaction_xdata_(**i).composite_amount, 10); transaction_xdata_(**i).value, 10);
else else
xml_write_value(output_stream, value_t((*i)->amount), 10); xml_write_value(output_stream, value_t((*i)->amount), 10);
output_stream << " </tr:amount>\n"; output_stream << " </tr:amount>\n";