Tons of corrections and fixes to value expressions and lot figures.
This commit is contained in:
parent
ab748ed13e
commit
32bdfe20d9
17 changed files with 866 additions and 658 deletions
42
amount.cc
42
amount.cc
|
|
@ -932,9 +932,41 @@ void parse_quantity(std::istream& in, std::string& value)
|
|||
char c = peek_next_nonws(in);
|
||||
READ_INTO(in, buf, 255, c,
|
||||
std::isdigit(c) || c == '-' || c == '.' || c == ',');
|
||||
|
||||
int len = std::strlen(buf);
|
||||
while (len > 0 && ! std::isdigit(buf[len - 1])) {
|
||||
buf[--len] = '\0';
|
||||
in.unget();
|
||||
}
|
||||
|
||||
value = buf;
|
||||
}
|
||||
|
||||
// Invalid commodity characters:
|
||||
// SPACE, TAB, NEWLINE, RETURN
|
||||
// 0-9 . , ; - + * / ^ ? : & | ! =
|
||||
// < > { } [ ] ( ) @
|
||||
|
||||
int invalid_chars[256] = {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
||||
/* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
|
||||
/* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
|
||||
/* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
|
||||
/* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
void parse_commodity(std::istream& in, std::string& symbol)
|
||||
{
|
||||
char buf[256];
|
||||
|
|
@ -947,8 +979,7 @@ void parse_commodity(std::istream& in, std::string& symbol)
|
|||
else
|
||||
throw new amount_error("Quoted commodity symbol lacks closing quote");
|
||||
} else {
|
||||
READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) &&
|
||||
c != '-' && c != '.');
|
||||
READ_INTO(in, buf, 255, c, ! invalid_chars[(unsigned char)c]);
|
||||
}
|
||||
symbol = buf;
|
||||
}
|
||||
|
|
@ -1041,7 +1072,7 @@ void amount_t::parse(std::istream& in, unsigned char flags)
|
|||
}
|
||||
|
||||
char n;
|
||||
if (std::isdigit(c) || c == '.') {
|
||||
if (std::isdigit(c)) {
|
||||
parse_quantity(in, quant);
|
||||
|
||||
if (! in.eof() && ((n = in.peek()) != '\n')) {
|
||||
|
|
@ -1065,7 +1096,7 @@ void amount_t::parse(std::istream& in, unsigned char flags)
|
|||
|
||||
parse_quantity(in, quant);
|
||||
|
||||
if (! in.eof() && ((n = in.peek()) != '\n'))
|
||||
if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n'))
|
||||
parse_annotations(in, price, date, tag);
|
||||
}
|
||||
}
|
||||
|
|
@ -1651,6 +1682,9 @@ namespace {
|
|||
const std::time_t date,
|
||||
const std::string& tag)
|
||||
{
|
||||
if (price < 0)
|
||||
throw new amount_error("A commodity's price may not be negative");
|
||||
|
||||
std::ostringstream name;
|
||||
|
||||
comm.write(name);
|
||||
|
|
|
|||
50
balance.h
50
balance.h
|
|
@ -216,7 +216,15 @@ class balance_t
|
|||
return true;
|
||||
}
|
||||
bool operator<(const amount_t& amt) const {
|
||||
return amount(amt.commodity()) < amt;
|
||||
if (amt.commodity())
|
||||
return amount(amt.commodity()) < amt;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++)
|
||||
if ((*i).second < amt)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
template <typename T>
|
||||
bool operator<(T val) const {
|
||||
|
|
@ -244,7 +252,15 @@ class balance_t
|
|||
return true;
|
||||
}
|
||||
bool operator<=(const amount_t& amt) const {
|
||||
return amount(amt.commodity()) <= amt;
|
||||
if (amt.commodity())
|
||||
return amount(amt.commodity()) <= amt;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++)
|
||||
if ((*i).second <= amt)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
template <typename T>
|
||||
bool operator<=(T val) const {
|
||||
|
|
@ -275,7 +291,15 @@ class balance_t
|
|||
return true;
|
||||
}
|
||||
bool operator>(const amount_t& amt) const {
|
||||
return amount(amt.commodity()) > amt;
|
||||
if (amt.commodity())
|
||||
return amount(amt.commodity()) > amt;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++)
|
||||
if ((*i).second > amt)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
template <typename T>
|
||||
bool operator>(T val) const {
|
||||
|
|
@ -303,7 +327,15 @@ class balance_t
|
|||
return true;
|
||||
}
|
||||
bool operator>=(const amount_t& amt) const {
|
||||
return amount(amt.commodity()) >= amt;
|
||||
if (amt.commodity())
|
||||
return amount(amt.commodity()) >= amt;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++)
|
||||
if ((*i).second >= amt)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
template <typename T>
|
||||
bool operator>=(T val) const {
|
||||
|
|
@ -327,7 +359,15 @@ class balance_t
|
|||
return i == amounts.end() && j == bal.amounts.end();
|
||||
}
|
||||
bool operator==(const amount_t& amt) const {
|
||||
return amounts.size() == 1 && (*amounts.begin()).second == amt;
|
||||
if (amt.commodity())
|
||||
return amounts.size() == 1 && (*amounts.begin()).second == amt;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++)
|
||||
if ((*i).second == amt)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
template <typename T>
|
||||
bool operator==(T val) const {
|
||||
|
|
|
|||
132
binary.cc
132
binary.cc
|
|
@ -245,6 +245,31 @@ inline void read_binary_amount(char *& data, amount_t& amt)
|
|||
amt.read_quantity(data);
|
||||
}
|
||||
|
||||
inline void read_binary_value(char *& data, value_t& val)
|
||||
{
|
||||
val.type = static_cast<value_t::type_t>(read_binary_long<int>(data));
|
||||
|
||||
switch (val.type) {
|
||||
case value_t::BOOLEAN:
|
||||
*((bool *) val.data) = read_binary_number<char>(data) == 1;
|
||||
break;
|
||||
case value_t::INTEGER:
|
||||
read_binary_long(data, *((long *) val.data));
|
||||
break;
|
||||
case value_t::DATETIME:
|
||||
read_binary_number(data, ((datetime_t *) val.data)->when);
|
||||
break;
|
||||
case value_t::AMOUNT:
|
||||
read_binary_amount(data, *((amount_t *) val.data));
|
||||
break;
|
||||
|
||||
case value_t::BALANCE:
|
||||
case value_t::BALANCE_PAIR:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void read_binary_mask(char *& data, mask_t *& mask)
|
||||
{
|
||||
bool exclude;
|
||||
|
|
@ -274,18 +299,13 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr)
|
|||
}
|
||||
|
||||
switch (expr->kind) {
|
||||
case value_expr_t::CONSTANT_T:
|
||||
read_binary_number(data, expr->constant_t);
|
||||
case value_expr_t::O_ARG:
|
||||
case value_expr_t::INDEX:
|
||||
read_binary_long(data, expr->arg_index);
|
||||
break;
|
||||
case value_expr_t::CONSTANT_I:
|
||||
read_binary_long(data, expr->constant_i);
|
||||
break;
|
||||
case value_expr_t::CONSTANT_A:
|
||||
expr->constant_a = new amount_t();
|
||||
read_binary_amount(data, *(expr->constant_a));
|
||||
break;
|
||||
case value_expr_t::CONSTANT_V:
|
||||
assert(0);
|
||||
case value_expr_t::CONSTANT:
|
||||
expr->value = new value_t;
|
||||
read_binary_value(data, *expr->value);
|
||||
break;
|
||||
|
||||
case value_expr_t::F_CODE_MASK:
|
||||
|
|
@ -314,16 +334,26 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
|
|||
read_binary_long(data, xact->_date_eff);
|
||||
xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1];
|
||||
|
||||
if (read_binary_number<char>(data) == 1) {
|
||||
read_binary_value_expr(data, xact->amount_expr);
|
||||
if (xact->amount_expr) xact->amount_expr->acquire();
|
||||
} else {
|
||||
char flag = read_binary_number<char>(data);
|
||||
if (flag == 0) {
|
||||
read_binary_amount(data, xact->amount);
|
||||
}
|
||||
else if (flag == 1) {
|
||||
read_binary_amount(data, xact->amount);
|
||||
read_binary_string(data, xact->amount_expr.expr);
|
||||
}
|
||||
else {
|
||||
value_expr_t * ptr = NULL;
|
||||
read_binary_value_expr(data, ptr);
|
||||
assert(ptr);
|
||||
xact->amount_expr.reset(ptr);
|
||||
read_binary_string(data, xact->amount_expr.expr);
|
||||
}
|
||||
|
||||
if (*data++ == 1) {
|
||||
xact->cost = new amount_t;
|
||||
read_binary_amount(data, *xact->cost);
|
||||
read_binary_string(data, xact->cost_expr);
|
||||
} else {
|
||||
xact->cost = NULL;
|
||||
}
|
||||
|
|
@ -358,7 +388,7 @@ inline void read_binary_entry_base(char *& data, entry_base_t * entry,
|
|||
for (unsigned long i = 0, count = read_binary_long<unsigned long>(data);
|
||||
i < count;
|
||||
i++) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
||||
new(xact_pool) transaction_t;
|
||||
read_binary_transaction(data, xact_pool);
|
||||
if (ignore_calculated && xact_pool->flags & TRANSACTION_CALCULATED)
|
||||
finalize = true;
|
||||
|
|
@ -601,6 +631,9 @@ unsigned int read_binary_journal(std::istream& in,
|
|||
|
||||
char * item_pool = new char[pool_size];
|
||||
|
||||
journal->item_pool = item_pool;
|
||||
journal->item_pool_end = item_pool + pool_size;
|
||||
|
||||
entry_t * entry_pool = (entry_t *) item_pool;
|
||||
transaction_t * xact_pool = (transaction_t *) (item_pool +
|
||||
sizeof(entry_t) * count);
|
||||
|
|
@ -718,9 +751,6 @@ unsigned int read_binary_journal(std::istream& in,
|
|||
|
||||
// Clean up and return the number of entries read
|
||||
|
||||
journal->item_pool = item_pool;
|
||||
journal->item_pool_end = item_pool + pool_size;
|
||||
|
||||
delete[] accounts;
|
||||
delete[] commodities;
|
||||
delete[] data_pool;
|
||||
|
|
@ -822,6 +852,30 @@ void write_binary_amount(std::ostream& out, const amount_t& amt)
|
|||
amt.write_quantity(out);
|
||||
}
|
||||
|
||||
void write_binary_value(std::ostream& out, const value_t& val)
|
||||
{
|
||||
write_binary_long(out, (int)val.type);
|
||||
|
||||
switch (val.type) {
|
||||
case value_t::BOOLEAN:
|
||||
write_binary_number<char>(out, *((bool *) val.data) ? 1 : 0);
|
||||
break;
|
||||
case value_t::INTEGER:
|
||||
write_binary_long(out, *((long *) val.data));
|
||||
break;
|
||||
case value_t::DATETIME:
|
||||
write_binary_number(out, ((datetime_t *) val.data)->when);
|
||||
break;
|
||||
case value_t::AMOUNT:
|
||||
write_binary_amount(out, *((amount_t *) val.data));
|
||||
break;
|
||||
|
||||
case value_t::BALANCE:
|
||||
case value_t::BALANCE_PAIR:
|
||||
throw new error("Cannot write a balance to the binary cache");
|
||||
}
|
||||
}
|
||||
|
||||
void write_binary_mask(std::ostream& out, mask_t * mask)
|
||||
{
|
||||
write_binary_number(out, mask->exclude);
|
||||
|
|
@ -842,17 +896,12 @@ void write_binary_value_expr(std::ostream& out, const value_expr_t * expr)
|
|||
write_binary_value_expr(out, expr->left);
|
||||
|
||||
switch (expr->kind) {
|
||||
case value_expr_t::CONSTANT_T:
|
||||
write_binary_number(out, expr->constant_t);
|
||||
case value_expr_t::O_ARG:
|
||||
case value_expr_t::INDEX:
|
||||
write_binary_long(out, expr->arg_index);
|
||||
break;
|
||||
case value_expr_t::CONSTANT_I:
|
||||
write_binary_long(out, expr->constant_i);
|
||||
break;
|
||||
case value_expr_t::CONSTANT_A:
|
||||
write_binary_amount(out, *(expr->constant_a));
|
||||
break;
|
||||
case value_expr_t::CONSTANT_V:
|
||||
assert(0);
|
||||
case value_expr_t::CONSTANT:
|
||||
write_binary_value(out, *expr->value);
|
||||
break;
|
||||
|
||||
case value_expr_t::F_CODE_MASK:
|
||||
|
|
@ -884,21 +933,30 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
|
|||
write_binary_long(out, xact->_date_eff);
|
||||
write_binary_long(out, xact->account->ident);
|
||||
|
||||
if (xact->amount_expr) {
|
||||
write_binary_number<char>(out, 1);
|
||||
write_binary_value_expr(out, xact->amount_expr);
|
||||
} else {
|
||||
if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) {
|
||||
write_binary_number<char>(out, 0);
|
||||
if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED)
|
||||
write_binary_amount(out, amount_t());
|
||||
else
|
||||
write_binary_amount(out, xact->amount);
|
||||
write_binary_amount(out, amount_t());
|
||||
}
|
||||
else if (xact->amount_expr) {
|
||||
write_binary_number<char>(out, 2);
|
||||
write_binary_value_expr(out, xact->amount_expr.get());
|
||||
write_binary_string(out, xact->amount_expr.expr);
|
||||
}
|
||||
else if (! xact->amount_expr.expr.empty()) {
|
||||
write_binary_number<char>(out, 1);
|
||||
write_binary_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);
|
||||
}
|
||||
|
||||
if (xact->cost &&
|
||||
(! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) {
|
||||
write_binary_number<char>(out, 1);
|
||||
write_binary_amount(out, *xact->cost);
|
||||
write_binary_string(out, xact->cost_expr);
|
||||
} else {
|
||||
write_binary_number<char>(out, 0);
|
||||
}
|
||||
|
|
|
|||
68
config.cc
68
config.cc
|
|
@ -31,8 +31,8 @@ namespace {
|
|||
void xact_amount(value_t& result, const details_t& details, value_expr_t *)
|
||||
{
|
||||
if (transaction_has_xdata(*details.xact) &&
|
||||
transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOSITE)
|
||||
result = transaction_xdata_(*details.xact).composite_amount;
|
||||
transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOUND)
|
||||
result = transaction_xdata_(*details.xact).value;
|
||||
else
|
||||
result = details.xact->amount;
|
||||
}
|
||||
|
|
@ -115,8 +115,8 @@ std::string resolve_path(const std::string& path)
|
|||
|
||||
void config_t::reset()
|
||||
{
|
||||
ledger::amount_expr.reset(new value_expr("a"));
|
||||
ledger::total_expr.reset(new value_expr("O"));
|
||||
ledger::amount_expr = "@a";
|
||||
ledger::total_expr = "@O";
|
||||
|
||||
pricing_leeway = 24 * 3600;
|
||||
budget_flags = BUDGET_NO_BUDGET;
|
||||
|
|
@ -126,8 +126,8 @@ void config_t::reset()
|
|||
wide_register_format = ("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
|
||||
"%48|%-.38A %22.108t %!22.132T\n");
|
||||
csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n";
|
||||
plot_amount_format = "%D %(S(t))\n";
|
||||
plot_total_format = "%D %(S(T))\n";
|
||||
plot_amount_format = "%D %(@S(@t))\n";
|
||||
plot_total_format = "%D %(@S(@T))\n";
|
||||
print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n";
|
||||
write_hdr_format = "%d %Y%C%P\n";
|
||||
write_xact_format = " %-34W %12o%n\n";
|
||||
|
|
@ -315,6 +315,9 @@ void config_t::process_options(const std::string& command,
|
|||
}
|
||||
}
|
||||
|
||||
if (command != "b" && command != "r")
|
||||
amount_t::keep_base = true;
|
||||
|
||||
// Process remaining command-line arguments
|
||||
|
||||
if (command != "e") {
|
||||
|
|
@ -360,9 +363,9 @@ void config_t::process_options(const std::string& command,
|
|||
// Setup the values of %t and %T, used in format strings
|
||||
|
||||
if (! amount_expr.empty())
|
||||
ledger::amount_expr.reset(new value_expr(amount_expr));
|
||||
ledger::amount_expr = amount_expr;
|
||||
if (! total_expr.empty())
|
||||
ledger::total_expr.reset(new value_expr(total_expr));
|
||||
ledger::total_expr = total_expr;
|
||||
|
||||
// If downloading is to be supported, configure the updater
|
||||
|
||||
|
|
@ -1097,6 +1100,13 @@ OPT_BEGIN(period_sort, ":") {
|
|||
config->report_period_sort = optarg;
|
||||
} OPT_END(period_sort);
|
||||
|
||||
OPT_BEGIN(daily, "") {
|
||||
if (config->report_period.empty())
|
||||
config->report_period = "daily";
|
||||
else
|
||||
config->report_period = std::string("daily ") + config->report_period;
|
||||
} OPT_END(daily);
|
||||
|
||||
OPT_BEGIN(weekly, "W") {
|
||||
if (config->report_period.empty())
|
||||
config->report_period = "weekly";
|
||||
|
|
@ -1179,11 +1189,11 @@ OPT_BEGIN(display, "d:") {
|
|||
} OPT_END(display);
|
||||
|
||||
OPT_BEGIN(amount, "t:") {
|
||||
ledger::amount_expr.reset(new value_expr(optarg));
|
||||
ledger::amount_expr = optarg;
|
||||
} OPT_END(amount);
|
||||
|
||||
OPT_BEGIN(total, "T:") {
|
||||
ledger::total_expr.reset(new value_expr(optarg));
|
||||
ledger::total_expr = optarg;
|
||||
} OPT_END(total);
|
||||
|
||||
OPT_BEGIN(amount_data, "j") {
|
||||
|
|
@ -1225,25 +1235,25 @@ OPT_BEGIN(download, "Q") {
|
|||
} OPT_END(download);
|
||||
|
||||
OPT_BEGIN(quantity, "O") {
|
||||
ledger::amount_expr.reset(new value_expr("a"));
|
||||
ledger::total_expr.reset(new value_expr("O"));
|
||||
ledger::amount_expr = "@a";
|
||||
ledger::total_expr = "@O";
|
||||
} OPT_END(quantity);
|
||||
|
||||
OPT_BEGIN(basis, "B") {
|
||||
ledger::amount_expr.reset(new value_expr("b"));
|
||||
ledger::total_expr.reset(new value_expr("B"));
|
||||
ledger::amount_expr = "@b";
|
||||
ledger::total_expr = "@B";
|
||||
} OPT_END(basis);
|
||||
|
||||
OPT_BEGIN(price, "I") {
|
||||
ledger::amount_expr.reset(new value_expr("i"));
|
||||
ledger::total_expr.reset(new value_expr("I"));
|
||||
ledger::amount_expr = "@i";
|
||||
ledger::total_expr = "@I";
|
||||
} OPT_END(price);
|
||||
|
||||
OPT_BEGIN(market, "V") {
|
||||
config->show_revalued = true;
|
||||
|
||||
ledger::amount_expr.reset(new value_expr("v"));
|
||||
ledger::total_expr.reset(new value_expr("V"));
|
||||
ledger::amount_expr = "@v";
|
||||
ledger::total_expr = "@V";
|
||||
} OPT_END(market);
|
||||
|
||||
namespace {
|
||||
|
|
@ -1279,34 +1289,29 @@ OPT_BEGIN(set_price, ":") {
|
|||
} OPT_END(set_price);
|
||||
|
||||
OPT_BEGIN(performance, "g") {
|
||||
ledger::amount_expr.reset(new value_expr("P(a,m)-b"));
|
||||
ledger::total_expr.reset(new value_expr("P(O,m)-B"));
|
||||
ledger::amount_expr = "@P(@a,@m)-@b";
|
||||
ledger::total_expr = "@P(@O,@m)-@B";
|
||||
} OPT_END(performance);
|
||||
|
||||
OPT_BEGIN(gain, "G") {
|
||||
config->show_revalued =
|
||||
config->show_revalued_only = true;
|
||||
|
||||
ledger::amount_expr.reset(new value_expr("a"));
|
||||
ledger::total_expr.reset(new value_expr("G"));
|
||||
ledger::amount_expr = "@a";
|
||||
ledger::total_expr = "@G";
|
||||
} OPT_END(gain);
|
||||
|
||||
OPT_BEGIN(average, "A") {
|
||||
ledger::total_expr.reset
|
||||
(new value_expr(expand_value_expr("A(#)", ledger::total_expr->expr)));
|
||||
ledger::total_expr = expand_value_expr("@A(#)", ledger::total_expr.expr);
|
||||
} OPT_END(average);
|
||||
|
||||
OPT_BEGIN(deviation, "D") {
|
||||
ledger::total_expr.reset(new value_expr("O"));
|
||||
ledger::total_expr.reset
|
||||
(new value_expr(expand_value_expr("t-A(#)", ledger::total_expr->expr)));
|
||||
ledger::total_expr = expand_value_expr("@t-@A(#)", ledger::total_expr.expr);
|
||||
} OPT_END(deviation);
|
||||
|
||||
OPT_BEGIN(percentage, "%") {
|
||||
ledger::total_expr.reset(new value_expr("O"));
|
||||
ledger::total_expr.reset
|
||||
(new value_expr(expand_value_expr("^#&{100.0%}*(#/^#)",
|
||||
ledger::total_expr->expr)));
|
||||
ledger::total_expr = expand_value_expr("^#&{100.0%}*(#/^#)",
|
||||
ledger::total_expr.expr);
|
||||
} OPT_END(percentage);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -1332,6 +1337,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
|
|||
{ "comm-as-payee", 'x', false, opt_comm_as_payee, false },
|
||||
{ "csv-register-format", '\0', true, opt_csv_register_format, false },
|
||||
{ "current", 'c', false, opt_current, false },
|
||||
{ "daily", '\0', false, opt_daily, false },
|
||||
{ "date-format", 'y', true, opt_date_format, false },
|
||||
{ "debug", '\0', true, opt_debug, false },
|
||||
{ "descend", '\0', true, opt_descend, false },
|
||||
|
|
|
|||
2
config.h
2
config.h
|
|
@ -107,7 +107,7 @@ class config_t
|
|||
std::list<item_handler<transaction_t> *>& ptrs);
|
||||
};
|
||||
|
||||
#define CONFIG_OPTIONS_SIZE 90
|
||||
#define CONFIG_OPTIONS_SIZE 91
|
||||
extern option_t config_options[CONFIG_OPTIONS_SIZE];
|
||||
|
||||
void option_help(std::ostream& out);
|
||||
|
|
|
|||
|
|
@ -167,8 +167,9 @@ entry_t * derive_new_entry(journal_t& journal,
|
|||
}
|
||||
|
||||
done:
|
||||
if (! run_hooks(journal.entry_finalize_hooks, *added) ||
|
||||
! added->finalize())
|
||||
if (! run_hooks(journal.entry_finalize_hooks, *added, false) ||
|
||||
! added->finalize() ||
|
||||
! run_hooks(journal.entry_finalize_hooks, *added, true))
|
||||
throw new error("Failed to finalize derived entry (check commodities)");
|
||||
|
||||
return added.release();
|
||||
|
|
|
|||
65
format.cc
65
format.cc
|
|
@ -172,7 +172,7 @@ element_t * format_t::parse_elements(const std::string& fmt)
|
|||
current->type = element_t::VALUE_EXPR;
|
||||
|
||||
assert(! current->val_expr);
|
||||
current->val_expr = new value_expr(std::string(b, p));
|
||||
current->val_expr = std::string(b, p);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ static bool entry_state(const entry_t * entry, transaction_t::state_t * state)
|
|||
}
|
||||
|
||||
namespace {
|
||||
void mark_red(std::ostream& out, const element_t * elem) {
|
||||
inline void mark_red(std::ostream& out, const element_t * elem) {
|
||||
out.setf(std::ios::left);
|
||||
out.width(0);
|
||||
out << "\e[31m";
|
||||
|
|
@ -289,7 +289,7 @@ namespace {
|
|||
out.width(elem->min_width);
|
||||
}
|
||||
|
||||
void mark_plain(std::ostream& out) {
|
||||
inline void mark_plain(std::ostream& out) {
|
||||
out << "\e[0m";
|
||||
}
|
||||
}
|
||||
|
|
@ -317,10 +317,10 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
case element_t::AMOUNT:
|
||||
case element_t::TOTAL:
|
||||
case element_t::VALUE_EXPR: {
|
||||
value_expr * calc = NULL;
|
||||
value_expr calc;
|
||||
switch (elem->type) {
|
||||
case element_t::AMOUNT: calc = amount_expr.get(); break;
|
||||
case element_t::TOTAL: calc = total_expr.get(); break;
|
||||
case element_t::AMOUNT: calc = amount_expr; break;
|
||||
case element_t::TOTAL: calc = total_expr; break;
|
||||
case element_t::VALUE_EXPR: calc = elem->val_expr; break;
|
||||
default:
|
||||
assert(0);
|
||||
|
|
@ -348,6 +348,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
}
|
||||
}
|
||||
|
||||
bool highlighted = false;
|
||||
|
||||
switch (value.type) {
|
||||
case value_t::BOOLEAN:
|
||||
out << (*((bool *) value.data) ? "true" : "false");
|
||||
|
|
@ -356,11 +358,15 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
case value_t::INTEGER:
|
||||
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
|
||||
if (ansi_invert) {
|
||||
if (*((long *) value.data) > 0)
|
||||
if (*((long *) value.data) > 0) {
|
||||
mark_red(out, elem);
|
||||
highlighted = true;
|
||||
}
|
||||
} else {
|
||||
if (*((long *) value.data) < 0)
|
||||
if (*((long *) value.data) < 0) {
|
||||
mark_red(out, elem);
|
||||
highlighted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
out << *((long *) value.data);
|
||||
|
|
@ -373,11 +379,15 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
case value_t::AMOUNT:
|
||||
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
|
||||
if (ansi_invert) {
|
||||
if (*((amount_t *) value.data) > 0)
|
||||
if (*((amount_t *) value.data) > 0) {
|
||||
mark_red(out, elem);
|
||||
highlighted = true;
|
||||
}
|
||||
} else {
|
||||
if (*((amount_t *) value.data) < 0)
|
||||
if (*((amount_t *) value.data) < 0) {
|
||||
mark_red(out, elem);
|
||||
highlighted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
out << *((amount_t *) value.data);
|
||||
|
|
@ -393,11 +403,15 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
|
||||
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
|
||||
if (ansi_invert) {
|
||||
if (*bal > 0)
|
||||
if (*bal > 0) {
|
||||
mark_red(out, elem);
|
||||
highlighted = true;
|
||||
}
|
||||
} else {
|
||||
if (*bal < 0)
|
||||
if (*bal < 0) {
|
||||
mark_red(out, elem);
|
||||
highlighted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
bal->write(out, elem->min_width,
|
||||
|
|
@ -411,7 +425,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
break;
|
||||
}
|
||||
|
||||
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT)
|
||||
if (highlighted)
|
||||
mark_plain(out);
|
||||
break;
|
||||
}
|
||||
|
|
@ -419,13 +433,20 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
case element_t::OPT_AMOUNT:
|
||||
if (details.xact) {
|
||||
std::string disp;
|
||||
bool use_disp = false;
|
||||
bool use_disp = false;
|
||||
|
||||
if (details.xact->cost && details.xact->amount) {
|
||||
std::ostringstream stream;
|
||||
stream << details.xact->amount << " @ "
|
||||
<< amount_t(*details.xact->cost /
|
||||
details.xact->amount).unround();
|
||||
if (! details.xact->amount_expr.expr.empty())
|
||||
stream << details.xact->amount_expr.expr;
|
||||
else
|
||||
stream << details.xact->amount.strip_annotations();
|
||||
|
||||
if (! details.xact->cost_expr.empty())
|
||||
stream << details.xact->cost_expr;
|
||||
else
|
||||
stream << " @ " << amount_t(*details.xact->cost /
|
||||
details.xact->amount).unround();
|
||||
disp = stream.str();
|
||||
use_disp = true;
|
||||
}
|
||||
|
|
@ -450,10 +471,14 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
first->amount == - last->amount);
|
||||
}
|
||||
|
||||
if (! use_disp)
|
||||
out << details.xact->amount;
|
||||
else
|
||||
if (! use_disp) {
|
||||
if (! details.xact->amount_expr.expr.empty())
|
||||
out << details.xact->amount_expr.expr;
|
||||
else
|
||||
out << details.xact->amount.strip_annotations();
|
||||
} else {
|
||||
out << disp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
6
format.h
6
format.h
|
|
@ -53,19 +53,17 @@ struct element_t
|
|||
std::string chars;
|
||||
unsigned char min_width;
|
||||
unsigned char max_width;
|
||||
value_expr * val_expr;
|
||||
value_expr val_expr;
|
||||
|
||||
struct element_t * next;
|
||||
|
||||
element_t() : type(STRING), flags(false),
|
||||
min_width(0), max_width(0),
|
||||
val_expr(NULL), next(NULL) {
|
||||
min_width(0), max_width(0), next(NULL) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor element_t");
|
||||
}
|
||||
|
||||
~element_t() {
|
||||
DEBUG_PRINT("ledger.memory.dtors", "dtor element_t");
|
||||
if (val_expr) delete val_expr;
|
||||
if (next) delete next; // recursive, but not too deep
|
||||
}
|
||||
};
|
||||
|
|
|
|||
27
journal.cc
27
journal.cc
|
|
@ -17,7 +17,6 @@ transaction_t::~transaction_t()
|
|||
{
|
||||
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t");
|
||||
if (cost) delete cost;
|
||||
if (amount_expr) amount_expr->release();
|
||||
}
|
||||
|
||||
std::time_t transaction_t::actual_date() const
|
||||
|
|
@ -74,7 +73,7 @@ bool transaction_t::valid() const
|
|||
return false;
|
||||
}
|
||||
|
||||
if (flags & ~0x001f) {
|
||||
if (flags & ~0x003f) {
|
||||
DEBUG_PRINT("ledger.validate", "transaction_t: flags are bad");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -316,35 +315,41 @@ auto_entry_t::~auto_entry_t()
|
|||
delete predicate;
|
||||
}
|
||||
|
||||
void auto_entry_t::extend_entry(entry_base_t& entry)
|
||||
void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
|
||||
{
|
||||
transactions_list initial_xacts(entry.transactions.begin(),
|
||||
entry.transactions.end());
|
||||
|
||||
for (transactions_list::iterator i = initial_xacts.begin();
|
||||
i != initial_xacts.end();
|
||||
i++)
|
||||
if ((*predicate)(**i))
|
||||
i++) {
|
||||
if ((*predicate)(**i)) {
|
||||
for (transactions_list::iterator t = transactions.begin();
|
||||
t != transactions.end();
|
||||
t++) {
|
||||
amount_t amt;
|
||||
if (! (*t)->amount.commodity())
|
||||
if (! (*t)->amount.commodity()) {
|
||||
if (! post)
|
||||
continue;
|
||||
amt = (*i)->amount * (*t)->amount;
|
||||
else
|
||||
} else {
|
||||
if (post)
|
||||
continue;
|
||||
amt = (*t)->amount;
|
||||
}
|
||||
|
||||
account_t * account = (*t)->account;
|
||||
std::string fullname = account->fullname();
|
||||
assert(! fullname.empty());
|
||||
|
||||
if (fullname == "$account")
|
||||
if (fullname == "$account" || fullname == "@account")
|
||||
account = (*i)->account;
|
||||
|
||||
transaction_t * xact
|
||||
= new transaction_t(account, amt, (*t)->flags | TRANSACTION_AUTO);
|
||||
entry.add_transaction(xact);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
account_t::~account_t()
|
||||
|
|
@ -514,7 +519,9 @@ bool journal_t::add_entry(entry_t * entry)
|
|||
{
|
||||
entry->journal = this;
|
||||
|
||||
if (! run_hooks(entry_finalize_hooks, *entry) || ! entry->finalize()) {
|
||||
if (! run_hooks(entry_finalize_hooks, *entry, false) ||
|
||||
! entry->finalize() ||
|
||||
! run_hooks(entry_finalize_hooks, *entry, true)) {
|
||||
entry->journal = NULL;
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
44
journal.h
44
journal.h
|
|
@ -10,6 +10,7 @@
|
|||
#include "amount.h"
|
||||
#include "datetime.h"
|
||||
#include "value.h"
|
||||
#include "valexpr.h"
|
||||
#include "error.h"
|
||||
#include "debug.h"
|
||||
#include "util.h"
|
||||
|
|
@ -26,7 +27,6 @@ namespace ledger {
|
|||
|
||||
class entry_t;
|
||||
class account_t;
|
||||
class value_expr_t;
|
||||
|
||||
class transaction_t
|
||||
{
|
||||
|
|
@ -38,8 +38,9 @@ class transaction_t
|
|||
std::time_t _date_eff;
|
||||
account_t * account;
|
||||
amount_t amount;
|
||||
value_expr_t * amount_expr;
|
||||
value_expr amount_expr;
|
||||
amount_t * cost;
|
||||
std::string cost_expr;
|
||||
state_t state;
|
||||
unsigned short flags;
|
||||
std::string note;
|
||||
|
|
@ -53,9 +54,8 @@ class transaction_t
|
|||
|
||||
transaction_t(account_t * _account = NULL)
|
||||
: entry(NULL), _date(0), _date_eff(0), account(_account),
|
||||
amount_expr(NULL), cost(NULL), state(UNCLEARED),
|
||||
flags(TRANSACTION_NORMAL), beg_pos(0), beg_line(0),
|
||||
end_pos(0), end_line(0), data(NULL) {
|
||||
cost(NULL), state(UNCLEARED), flags(TRANSACTION_NORMAL),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
||||
}
|
||||
transaction_t(account_t * _account,
|
||||
|
|
@ -63,15 +63,14 @@ class transaction_t
|
|||
unsigned int _flags = TRANSACTION_NORMAL,
|
||||
const std::string& _note = "")
|
||||
: entry(NULL), _date(0), _date_eff(0), account(_account),
|
||||
amount(_amount), amount_expr(NULL), cost(NULL),
|
||||
state(UNCLEARED), flags(_flags), note(_note),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
|
||||
amount(_amount), cost(NULL), state(UNCLEARED), flags(_flags),
|
||||
note(_note), beg_pos(0), beg_line(0), end_pos(0), end_line(0),
|
||||
data(NULL) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
||||
}
|
||||
transaction_t(const transaction_t& xact)
|
||||
: entry(xact.entry), _date(0), _date_eff(0), account(xact.account),
|
||||
amount(xact.amount), amount_expr(NULL),
|
||||
cost(xact.cost ? new amount_t(*xact.cost) : NULL),
|
||||
amount(xact.amount), cost(xact.cost ? new amount_t(*xact.cost) : NULL),
|
||||
state(xact.state), flags(xact.flags), note(xact.note),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
||||
|
|
@ -199,7 +198,7 @@ class entry_t : public entry_base_t
|
|||
|
||||
struct entry_finalizer_t {
|
||||
virtual ~entry_finalizer_t() {}
|
||||
virtual bool operator()(entry_t& entry) = 0;
|
||||
virtual bool operator()(entry_t& entry, bool post) = 0;
|
||||
};
|
||||
|
||||
class entry_context : public error_context {
|
||||
|
|
@ -238,7 +237,7 @@ public:
|
|||
|
||||
virtual ~auto_entry_t();
|
||||
|
||||
virtual void extend_entry(entry_base_t& entry);
|
||||
virtual void extend_entry(entry_base_t& entry, bool post);
|
||||
virtual bool valid() const {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -248,7 +247,7 @@ class journal_t;
|
|||
struct auto_entry_finalizer_t : public entry_finalizer_t {
|
||||
journal_t * journal;
|
||||
auto_entry_finalizer_t(journal_t * _journal) : journal(_journal) {}
|
||||
virtual bool operator()(entry_t& entry);
|
||||
virtual bool operator()(entry_t& entry, bool post);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -342,12 +341,12 @@ std::ostream& operator<<(std::ostream& out, const account_t& account);
|
|||
|
||||
|
||||
struct func_finalizer_t : public entry_finalizer_t {
|
||||
typedef bool (*func_t)(entry_t& entry);
|
||||
typedef bool (*func_t)(entry_t& entry, bool post);
|
||||
func_t func;
|
||||
func_finalizer_t(func_t _func) : func(_func) {}
|
||||
func_finalizer_t(const func_finalizer_t& other) : func(other.func) {}
|
||||
virtual bool operator()(entry_t& entry) {
|
||||
return func(entry);
|
||||
virtual bool operator()(entry_t& entry, bool post) {
|
||||
return func(entry, post);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -365,11 +364,11 @@ void remove_hook(std::list<T>& list, T obj) {
|
|||
}
|
||||
|
||||
template <typename T, typename Data>
|
||||
bool run_hooks(std::list<T>& list, Data& item) {
|
||||
bool run_hooks(std::list<T>& list, Data& item, bool post) {
|
||||
for (typename std::list<T>::const_iterator i = list.begin();
|
||||
i != list.end();
|
||||
i++)
|
||||
if (! (*(*i))(item))
|
||||
if (! (*(*i))(item, post))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -446,15 +445,16 @@ class journal_t
|
|||
bool valid() const;
|
||||
};
|
||||
|
||||
inline void extend_entry_base(journal_t * journal, entry_base_t& entry) {
|
||||
inline void extend_entry_base(journal_t * journal, entry_base_t& entry,
|
||||
bool post) {
|
||||
for (auto_entries_list::iterator i = journal->auto_entries.begin();
|
||||
i != journal->auto_entries.end();
|
||||
i++)
|
||||
(*i)->extend_entry(entry);
|
||||
(*i)->extend_entry(entry, post);
|
||||
}
|
||||
|
||||
inline bool auto_entry_finalizer_t::operator()(entry_t& entry) {
|
||||
extend_entry_base(journal, entry);
|
||||
inline bool auto_entry_finalizer_t::operator()(entry_t& entry, bool post) {
|
||||
extend_entry_base(journal, entry, post);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
8
main.cc
8
main.cc
|
|
@ -100,9 +100,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
|
|||
command = "p";
|
||||
else if (command == "output")
|
||||
command = "w";
|
||||
else if (command == "emacs")
|
||||
command = "x";
|
||||
else if (command == "lisp")
|
||||
else if (command == "emacs" || command == "lisp")
|
||||
command = "x";
|
||||
else if (command == "xml")
|
||||
command = "X";
|
||||
|
|
@ -119,7 +117,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
|
|||
command = "r";
|
||||
}
|
||||
else if (command == "parse") {
|
||||
value_auto_ptr expr(ledger::parse_value_expr(*arg));
|
||||
value_expr expr(ledger::parse_value_expr(*arg));
|
||||
if (config.verbose_mode) {
|
||||
std::cout << "Value expression tree:" << std::endl;
|
||||
ledger::dump_value_expr(std::cout, expr.get());
|
||||
|
|
@ -243,7 +241,7 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
|
|||
// Are we handling the parse or expr commands? Do so now.
|
||||
|
||||
if (command == "expr") {
|
||||
value_auto_ptr expr(ledger::parse_value_expr(*arg));
|
||||
value_expr expr(ledger::parse_value_expr(*arg));
|
||||
if (config.verbose_mode) {
|
||||
std::cout << "Value expression tree:" << std::endl;
|
||||
ledger::dump_value_expr(std::cout, expr.get());
|
||||
|
|
|
|||
116
textual.cc
116
textual.cc
|
|
@ -64,6 +64,35 @@ inline char * next_element(char * buf, bool variable = false)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
value_expr parse_amount_expr(std::istream& in, amount_t& amount,
|
||||
transaction_t * xact)
|
||||
{
|
||||
value_expr expr(parse_value_expr(in, NULL, PARSE_VALEXPR_RELAXED |
|
||||
PARSE_VALEXPR_PARTIAL)->acquire());
|
||||
|
||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||
"Parsed an amount expression");
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
DEBUG_IF("ledger.textual.parse") {
|
||||
if (_debug_stream) {
|
||||
ledger::dump_value_expr(*_debug_stream, expr);
|
||||
*_debug_stream << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (! compute_amount(expr, amount, xact))
|
||||
throw new parse_error("Amount expression failed to compute");
|
||||
|
||||
if (expr->kind == value_expr_t::CONSTANT)
|
||||
expr = NULL;
|
||||
|
||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||
"The transaction amount is " << xact->amount);
|
||||
return expr;
|
||||
}
|
||||
|
||||
transaction_t * parse_transaction(char * line, account_t * account,
|
||||
entry_t * entry = NULL)
|
||||
{
|
||||
|
|
@ -146,53 +175,19 @@ transaction_t * parse_transaction(char * line, account_t * account,
|
|||
goto finished;
|
||||
if (p == ';')
|
||||
goto parse_note;
|
||||
if (p == '(') {
|
||||
try {
|
||||
xact->amount_expr = parse_value_expr(in)->acquire();
|
||||
}
|
||||
catch (error * err) {
|
||||
err_desc = "While parsing transaction amount's value expression:";
|
||||
throw err;
|
||||
}
|
||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||
"Parsed an amount expression");
|
||||
#ifdef DEBUG_ENABLED
|
||||
DEBUG_IF("ledger.textual.parse") {
|
||||
if (_debug_stream) {
|
||||
ledger::dump_value_expr(*_debug_stream, xact->amount_expr);
|
||||
*_debug_stream << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (! compute_amount(xact->amount_expr, xact->amount, xact.get()))
|
||||
throw new parse_error("Value expression for amount failed to compute");
|
||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||
"The computed amount is " << xact->amount);
|
||||
} else {
|
||||
xact->amount.parse(in, AMOUNT_PARSE_NO_REDUCE);
|
||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||
"Parsed amount " << xact->amount);
|
||||
|
||||
// Parse any inline math
|
||||
p = peek_next_nonws(in);
|
||||
while (in.good() && ! in.eof() && (p == '-' || p == '+')) {
|
||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||
"Parsed inline math operator " << p);
|
||||
in.get(p);
|
||||
amount_t temp;
|
||||
temp.parse(in, AMOUNT_PARSE_NO_REDUCE);
|
||||
switch (p) {
|
||||
case '-':
|
||||
xact->amount -= temp;
|
||||
break;
|
||||
case '+':
|
||||
xact->amount += temp;
|
||||
break;
|
||||
}
|
||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||
"Calculated amount is " << xact->amount);
|
||||
p = peek_next_nonws(in);
|
||||
}
|
||||
try {
|
||||
unsigned long beg = (long)in.tellg();
|
||||
|
||||
xact->amount_expr =
|
||||
parse_amount_expr(in, xact->amount, xact.get());
|
||||
|
||||
unsigned long end = (long)in.tellg();
|
||||
xact->amount_expr.expr = std::string(line, beg, end - beg);
|
||||
}
|
||||
catch (error * err) {
|
||||
err_desc = "While parsing transaction amount:";
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,13 +210,28 @@ transaction_t * parse_transaction(char * line, account_t * account,
|
|||
if (in.good() && ! in.eof()) {
|
||||
xact->cost = new amount_t;
|
||||
|
||||
p = peek_next_nonws(in);
|
||||
if (p == '(')
|
||||
throw new parse_error("A transaction's cost may not be a value expression");
|
||||
try {
|
||||
unsigned long beg = (long)in.tellg();
|
||||
|
||||
xact->cost->parse(in, AMOUNT_PARSE_NO_MIGRATE);
|
||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||
"Parsed cost " << *xact->cost);
|
||||
if (parse_amount_expr(in, *xact->cost, xact.get()))
|
||||
throw new parse_error("A transaction's cost must evalute to a constant value");
|
||||
|
||||
unsigned long end = (long)in.tellg();
|
||||
|
||||
if (per_unit)
|
||||
xact->cost_expr = (std::string("@") +
|
||||
std::string(line, beg, end - beg));
|
||||
else
|
||||
xact->cost_expr = (std::string("@@") +
|
||||
std::string(line, beg, end - beg));
|
||||
}
|
||||
catch (error * err) {
|
||||
err_desc = "While parsing transaction cost:";
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (*xact->cost < 0)
|
||||
throw new parse_error("A transaction's cost may not be a negative value");
|
||||
|
||||
amount_t per_unit_cost(*xact->cost);
|
||||
if (per_unit)
|
||||
|
|
@ -730,7 +740,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
|||
if (parse_transactions(in, account_stack.front(), *pe,
|
||||
"period", end_pos)) {
|
||||
if (pe->finalize()) {
|
||||
extend_entry_base(journal, *pe);
|
||||
extend_entry_base(journal, *pe, true);
|
||||
journal->period_entries.push_back(pe);
|
||||
pe->src_idx = src_idx;
|
||||
pe->beg_pos = beg_pos;
|
||||
|
|
|
|||
720
valexpr.cc
720
valexpr.cc
File diff suppressed because it is too large
Load diff
183
valexpr.h
183
valexpr.h
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef _VALEXPR_H
|
||||
#define _VALEXPR_H
|
||||
|
||||
#include "journal.h"
|
||||
#include "value.h"
|
||||
#include "error.h"
|
||||
#include "mask.h"
|
||||
|
|
@ -10,6 +9,10 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
class entry_t;
|
||||
class transaction_t;
|
||||
class account_t;
|
||||
|
||||
struct details_t
|
||||
{
|
||||
const entry_t * entry;
|
||||
|
|
@ -37,10 +40,9 @@ struct value_expr_t
|
|||
{
|
||||
enum kind_t {
|
||||
// Constants
|
||||
CONSTANT_I,
|
||||
CONSTANT_T,
|
||||
CONSTANT_A,
|
||||
CONSTANT_V,
|
||||
CONSTANT,
|
||||
ARG_INDEX,
|
||||
ZERO,
|
||||
|
||||
CONSTANTS,
|
||||
|
||||
|
|
@ -125,11 +127,9 @@ struct value_expr_t
|
|||
value_expr_t * left;
|
||||
|
||||
union {
|
||||
datetime_t * constant_t;
|
||||
long constant_i;
|
||||
amount_t * constant_a;
|
||||
value_t * constant_v;
|
||||
value_t * value;
|
||||
mask_t * mask;
|
||||
unsigned int arg_index; // used by ARG_INDEX and O_ARG
|
||||
value_expr_t * right;
|
||||
};
|
||||
|
||||
|
|
@ -137,9 +137,6 @@ struct value_expr_t
|
|||
: kind(_kind), refc(0), left(NULL), right(NULL) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t " << this);
|
||||
}
|
||||
value_expr_t(const value_expr_t&) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t (copy) " << this);
|
||||
}
|
||||
~value_expr_t();
|
||||
|
||||
void release() const {
|
||||
|
|
@ -180,12 +177,18 @@ struct value_expr_t
|
|||
void compute(value_t& result,
|
||||
const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) const;
|
||||
|
||||
value_t compute(const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) const {
|
||||
value_t temp;
|
||||
compute(temp, details, context);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
value_expr_t(const value_expr_t&) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t (copy) " << this);
|
||||
}
|
||||
};
|
||||
|
||||
class valexpr_context : public error_context {
|
||||
|
|
@ -273,37 +276,21 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
|
|||
const transaction_t * xact,
|
||||
value_expr_t * context = NULL);
|
||||
|
||||
struct scope_t;
|
||||
value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope);
|
||||
|
||||
inline value_expr_t * parse_boolean_expr(const std::string& str,
|
||||
scope_t * scope = NULL) {
|
||||
std::istringstream stream(str);
|
||||
try {
|
||||
return parse_boolean_expr(stream, scope);
|
||||
}
|
||||
catch (error * err) {
|
||||
err->context.push_back
|
||||
(new error_context("While parsing value expression: " + str));
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
inline value_expr_t * parse_boolean_expr(const char * p,
|
||||
scope_t * scope = NULL) {
|
||||
return parse_boolean_expr(std::string(p), scope);
|
||||
}
|
||||
#define PARSE_VALEXPR_NORMAL 0x00
|
||||
#define PARSE_VALEXPR_PARTIAL 0x01
|
||||
#define PARSE_VALEXPR_RELAXED 0x02
|
||||
|
||||
value_expr_t * parse_value_expr(std::istream& in,
|
||||
scope_t * scope = NULL,
|
||||
const bool partial = false);
|
||||
const short flags = PARSE_VALEXPR_RELAXED);
|
||||
|
||||
inline value_expr_t * parse_value_expr(const std::string& str,
|
||||
scope_t * scope = NULL,
|
||||
const bool partial = false) {
|
||||
inline value_expr_t *
|
||||
parse_value_expr(const std::string& str,
|
||||
scope_t * scope = NULL,
|
||||
const short flags = PARSE_VALEXPR_RELAXED) {
|
||||
std::istringstream stream(str);
|
||||
try {
|
||||
return parse_value_expr(stream, scope, partial);
|
||||
return parse_value_expr(stream, scope, flags);
|
||||
}
|
||||
catch (error * err) {
|
||||
err->context.push_back
|
||||
|
|
@ -313,10 +300,11 @@ inline value_expr_t * parse_value_expr(const std::string& str,
|
|||
}
|
||||
}
|
||||
|
||||
inline value_expr_t * parse_value_expr(const char * p,
|
||||
scope_t * scope = NULL,
|
||||
const bool partial = false) {
|
||||
return parse_value_expr(std::string(p), scope, partial);
|
||||
inline value_expr_t *
|
||||
parse_value_expr(const char * p,
|
||||
scope_t * scope = NULL,
|
||||
const short flags = PARSE_VALEXPR_RELAXED) {
|
||||
return parse_value_expr(std::string(p), scope, flags);
|
||||
}
|
||||
|
||||
void dump_value_expr(std::ostream& out, const value_expr_t * node,
|
||||
|
|
@ -324,6 +312,7 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
|
|||
|
||||
bool write_value_expr(std::ostream& out,
|
||||
const value_expr_t * node,
|
||||
const bool relaxed = true,
|
||||
const value_expr_t * node_to_find = NULL,
|
||||
unsigned long * start_pos = NULL,
|
||||
unsigned long * end_pos = NULL);
|
||||
|
|
@ -359,27 +348,70 @@ inline value_t guarded_compute(const value_expr_t * expr,
|
|||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This class is used so that during the "in between" stages of value
|
||||
// expression parsing -- while no one yet holds a reference to the
|
||||
// value_expr_t object -- we can be assured of deletion should an
|
||||
// exception happen to whip by.
|
||||
|
||||
struct value_auto_ptr {
|
||||
class value_expr
|
||||
{
|
||||
value_expr_t * ptr;
|
||||
value_auto_ptr() : ptr(NULL) {}
|
||||
explicit value_auto_ptr(value_expr_t * _ptr)
|
||||
: ptr(_ptr ? _ptr->acquire() : NULL) {}
|
||||
~value_auto_ptr() {
|
||||
public:
|
||||
std::string expr;
|
||||
|
||||
value_expr() : ptr(NULL) {}
|
||||
|
||||
value_expr(const std::string& _expr) : expr(_expr) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr");
|
||||
if (! _expr.empty())
|
||||
ptr = parse_value_expr(expr)->acquire();
|
||||
else
|
||||
ptr = NULL;
|
||||
}
|
||||
value_expr(value_expr_t * _ptr)
|
||||
: ptr(_ptr ? _ptr->acquire(): NULL) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr");
|
||||
}
|
||||
value_expr(const value_expr& other)
|
||||
: ptr(other.ptr ? other.ptr->acquire() : NULL),
|
||||
expr(other.expr) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr");
|
||||
}
|
||||
virtual ~value_expr() {
|
||||
DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr");
|
||||
if (ptr)
|
||||
ptr->release();
|
||||
}
|
||||
|
||||
value_expr& operator=(const std::string& _expr) {
|
||||
expr = _expr;
|
||||
reset(parse_value_expr(expr));
|
||||
return *this;
|
||||
}
|
||||
value_expr& operator=(value_expr_t * _expr) {
|
||||
expr = "";
|
||||
reset(_expr);
|
||||
return *this;
|
||||
}
|
||||
value_expr& operator=(const value_expr& _expr) {
|
||||
expr = _expr.expr;
|
||||
reset(_expr.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const throw() {
|
||||
return ptr != NULL;
|
||||
}
|
||||
operator std::string() const throw() {
|
||||
return expr;
|
||||
}
|
||||
operator value_expr_t *() const throw() {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
value_expr_t& operator*() const throw() {
|
||||
return *ptr;
|
||||
}
|
||||
value_expr_t * operator->() const throw() {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
value_expr_t * get() const throw() { return ptr; }
|
||||
value_expr_t * release() throw() {
|
||||
value_expr_t * tmp = ptr;
|
||||
|
|
@ -390,41 +422,19 @@ struct value_auto_ptr {
|
|||
if (p != ptr) {
|
||||
if (ptr)
|
||||
ptr->release();
|
||||
ptr = p->acquire();
|
||||
ptr = p ? p->acquire() : NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class value_expr
|
||||
{
|
||||
value_expr_t * parsed;
|
||||
public:
|
||||
std::string expr;
|
||||
|
||||
value_expr(const std::string& _expr) : expr(_expr) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr");
|
||||
parsed = parse_value_expr(expr)->acquire();
|
||||
}
|
||||
value_expr(value_expr_t * _parsed) : parsed(_parsed->acquire()) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr");
|
||||
}
|
||||
virtual ~value_expr() {
|
||||
DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr");
|
||||
if (parsed)
|
||||
parsed->release();
|
||||
}
|
||||
|
||||
virtual void compute(value_t& result,
|
||||
const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) {
|
||||
guarded_compute(parsed, result, details, context);
|
||||
guarded_compute(ptr, result, details, context);
|
||||
}
|
||||
virtual value_t compute(const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) {
|
||||
value_t temp;
|
||||
guarded_compute(parsed, temp, details, context);
|
||||
guarded_compute(ptr, temp, details, context);
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
|
@ -435,35 +445,40 @@ public:
|
|||
unsigned long * end_pos);
|
||||
};
|
||||
|
||||
extern std::auto_ptr<value_expr> amount_expr;
|
||||
extern std::auto_ptr<value_expr> total_expr;
|
||||
extern value_expr amount_expr;
|
||||
extern value_expr total_expr;
|
||||
|
||||
inline void compute_amount(value_t& result,
|
||||
const details_t& details = details_t()) {
|
||||
if (amount_expr.get())
|
||||
if (amount_expr)
|
||||
amount_expr->compute(result, details);
|
||||
}
|
||||
|
||||
inline value_t compute_amount(const details_t& details = details_t()) {
|
||||
if (amount_expr.get())
|
||||
if (amount_expr)
|
||||
return amount_expr->compute(details);
|
||||
}
|
||||
|
||||
inline void compute_total(value_t& result,
|
||||
const details_t& details = details_t()) {
|
||||
if (total_expr.get())
|
||||
if (total_expr)
|
||||
total_expr->compute(result, details);
|
||||
}
|
||||
|
||||
inline value_t compute_total(const details_t& details = details_t()) {
|
||||
if (total_expr.get())
|
||||
if (total_expr)
|
||||
return total_expr->compute(details);
|
||||
}
|
||||
|
||||
value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope,
|
||||
const short flags);
|
||||
|
||||
inline void parse_value_definition(const std::string& str,
|
||||
scope_t * scope = NULL) {
|
||||
value_auto_ptr expr
|
||||
(parse_boolean_expr(str, scope ? scope : global_scope.get()));
|
||||
std::istringstream def(str);
|
||||
value_expr expr
|
||||
(parse_boolean_expr(def, scope ? scope : global_scope.get(),
|
||||
PARSE_VALEXPR_RELAXED));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
48
walk.cc
48
walk.cc
|
|
@ -39,8 +39,8 @@ transaction_xdata_t& transaction_xdata(const transaction_t& xact)
|
|||
void add_transaction_to(const transaction_t& xact, value_t& value)
|
||||
{
|
||||
if (transaction_has_xdata(xact) &&
|
||||
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) {
|
||||
value += transaction_xdata_(xact).composite_amount;
|
||||
transaction_xdata_(xact).dflags & TRANSACTION_COMPOUND) {
|
||||
value += transaction_xdata_(xact).value;
|
||||
}
|
||||
else if (xact.cost || ! value.realzero()) {
|
||||
value.add(xact.amount, xact.cost);
|
||||
|
|
@ -162,8 +162,8 @@ void calc_transactions::operator()(transaction_t& xact)
|
|||
void invert_transactions::operator()(transaction_t& xact)
|
||||
{
|
||||
if (transaction_has_xdata(xact) &&
|
||||
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) {
|
||||
transaction_xdata_(xact).composite_amount.negate();
|
||||
transaction_xdata_(xact).dflags & TRANSACTION_COMPOUND) {
|
||||
transaction_xdata_(xact).value.negate();
|
||||
} else {
|
||||
xact.amount.negate();
|
||||
if (xact.cost)
|
||||
|
|
@ -209,29 +209,31 @@ void handle_value(const value_t& value,
|
|||
|
||||
transaction_xdata_t& xdata(transaction_xdata(xact));
|
||||
|
||||
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)
|
||||
xdata.date = date;
|
||||
if (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);
|
||||
}
|
||||
|
||||
|
|
@ -830,7 +832,7 @@ void walk_accounts(account_t& account,
|
|||
const std::string& sort_string)
|
||||
{
|
||||
if (! sort_string.empty()) {
|
||||
value_auto_ptr sort_order;
|
||||
value_expr sort_order;
|
||||
sort_order.reset(parse_value_expr(sort_string));
|
||||
walk_accounts(account, handler, sort_order.get());
|
||||
} else {
|
||||
|
|
|
|||
4
walk.h
4
walk.h
|
|
@ -80,14 +80,14 @@ bool compare_items<account_t>::operator()(const account_t * left,
|
|||
#define TRANSACTION_DISPLAYED 0x0008
|
||||
#define TRANSACTION_NO_TOTAL 0x0010
|
||||
#define TRANSACTION_SORT_CALC 0x0020
|
||||
#define TRANSACTION_COMPOSITE 0x0040
|
||||
#define TRANSACTION_COMPOUND 0x0040
|
||||
#define TRANSACTION_MATCHES 0x0080
|
||||
|
||||
struct transaction_xdata_t
|
||||
{
|
||||
value_t total;
|
||||
value_t sort_value;
|
||||
value_t composite_amount;
|
||||
value_t value;
|
||||
unsigned int index;
|
||||
unsigned short dflags;
|
||||
std::time_t date;
|
||||
|
|
|
|||
4
xml.cc
4
xml.cc
|
|
@ -435,9 +435,9 @@ void format_xml_entries::format_last_entry()
|
|||
}
|
||||
|
||||
output_stream << " <tr:amount>\n";
|
||||
if (transaction_xdata_(**i).dflags & TRANSACTION_COMPOSITE)
|
||||
if (transaction_xdata_(**i).dflags & TRANSACTION_COMPOUND)
|
||||
xml_write_value(output_stream,
|
||||
transaction_xdata_(**i).composite_amount, 10);
|
||||
transaction_xdata_(**i).value, 10);
|
||||
else
|
||||
xml_write_value(output_stream, value_t((*i)->amount), 10);
|
||||
output_stream << " </tr:amount>\n";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue