summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2006-03-19 21:11:49 +0000
committerJohn Wiegley <johnw@newartisans.com>2008-04-13 02:41:31 -0400
commit32bdfe20d98f1c8e6c0aabe9b26f0507326d2baa (patch)
tree50a4c5490f842ff4241ed6c574d3debd0ed6e1b6
parentab748ed13ed87369e2e4ed5a617b5de12750fda6 (diff)
downloadfork-ledger-32bdfe20d98f1c8e6c0aabe9b26f0507326d2baa.tar.gz
fork-ledger-32bdfe20d98f1c8e6c0aabe9b26f0507326d2baa.tar.bz2
fork-ledger-32bdfe20d98f1c8e6c0aabe9b26f0507326d2baa.zip
Tons of corrections and fixes to value expressions and lot figures.
-rw-r--r--amount.cc42
-rw-r--r--balance.h50
-rw-r--r--binary.cc130
-rw-r--r--config.cc68
-rw-r--r--config.h2
-rw-r--r--derive.cc5
-rw-r--r--format.cc65
-rw-r--r--format.h6
-rw-r--r--journal.cc27
-rw-r--r--journal.h44
-rw-r--r--main.cc8
-rw-r--r--textual.cc116
-rw-r--r--valexpr.cc726
-rw-r--r--valexpr.h183
-rw-r--r--walk.cc36
-rw-r--r--walk.h4
-rw-r--r--xml.cc4
17 files changed, 862 insertions, 654 deletions
diff --git a/amount.cc b/amount.cc
index 078e03a0..d0cd5ef9 100644
--- a/amount.cc
+++ b/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);
diff --git a/balance.h b/balance.h
index c115ba45..d18ffd60 100644
--- a/balance.h
+++ b/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 {
diff --git a/binary.cc b/binary.cc
index 8f1d6c7b..1ca7551f 100644
--- a/binary.cc
+++ b/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);
- break;
- case value_expr_t::CONSTANT_I:
- write_binary_long(out, expr->constant_i);
+ case value_expr_t::O_ARG:
+ case value_expr_t::INDEX:
+ write_binary_long(out, expr->arg_index);
break;
- case value_expr_t::CONSTANT_A:
- write_binary_amount(out, *(expr->constant_a));
- break;
- case value_expr_t::CONSTANT_V:
- assert(0);
+ case value_expr_t::CONSTANT:
+ write_binary_value(out, *expr->value);
break;
case value_expr_t::F_CODE_MASK:
@@ -884,21 +933,30 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
write_binary_long(out, xact->_date_eff);
write_binary_long(out, xact->account->ident);
- if (xact->amount_expr) {
+ if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) {
+ write_binary_number<char>(out, 0);
+ write_binary_amount(out, amount_t());
+ }
+ else if (xact->amount_expr) {
+ write_binary_number<char>(out, 2);
+ write_binary_value_expr(out, xact->amount_expr.get());
+ write_binary_string(out, xact->amount_expr.expr);
+ }
+ else if (! xact->amount_expr.expr.empty()) {
write_binary_number<char>(out, 1);
- write_binary_value_expr(out, xact->amount_expr);
- } else {
+ write_binary_amount(out, xact->amount);
+ write_binary_string(out, xact->amount_expr.expr);
+ }
+ else {
write_binary_number<char>(out, 0);
- if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED)
- write_binary_amount(out, amount_t());
- else
- write_binary_amount(out, xact->amount);
+ write_binary_amount(out, xact->amount);
}
if (xact->cost &&
(! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) {
write_binary_number<char>(out, 1);
write_binary_amount(out, *xact->cost);
+ write_binary_string(out, xact->cost_expr);
} else {
write_binary_number<char>(out, 0);
}
diff --git a/config.cc b/config.cc
index a1ba6496..85db5a7c 100644
--- a/config.cc
+++ b/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 },
diff --git a/config.h b/config.h
index f642147d..2758d5de 100644
--- a/config.h
+++ b/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);
diff --git a/derive.cc b/derive.cc
index 837353cc..0e6df28a 100644
--- a/derive.cc
+++ b/derive.cc
@@ -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();
diff --git a/format.cc b/format.cc
index e1248b41..5a5184d4 100644
--- a/format.cc
+++ b/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;
diff --git a/format.h b/format.h
index df9d2dcc..ccbb0f42 100644
--- a/format.h
+++ b/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
}
};
diff --git a/journal.cc b/journal.cc
index 68e3825c..a8d9c849 100644
--- a/journal.cc
+++ b/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;
}
diff --git a/journal.h b/journal.h
index 7096e8bb..3d0927cf 100644
--- a/journal.h
+++ b/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;
}
diff --git a/main.cc b/main.cc
index 705ee3af..84397b15 100644
--- a/main.cc
+++ b/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());
diff --git a/textual.cc b/textual.cc
index 3577145c..da8fc04b 100644
--- a/textual.cc
+++ b/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;
diff --git a/valexpr.cc b/valexpr.cc
index 8de4571a..51d29a48 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -7,8 +7,8 @@
namespace ledger {
-std::auto_ptr<value_expr> amount_expr;
-std::auto_ptr<value_expr> total_expr;
+value_expr amount_expr;
+value_expr total_expr;
std::auto_ptr<scope_t> global_scope;
std::time_t terminus;
@@ -63,18 +63,9 @@ value_expr_t::~value_expr_t()
delete mask;
break;
- case CONSTANT_A:
- assert(constant_a);
- delete constant_a;
- break;
-
- case CONSTANT_T:
- assert(constant_t);
- delete constant_t;
- break;
-
- case CONSTANT_I:
- case CONSTANT_V:
+ case CONSTANT:
+ assert(value);
+ delete value;
break;
default:
@@ -103,15 +94,15 @@ namespace {
if (! expr)
return NULL;
- value_auto_ptr temp;
+ value_expr temp;
if (expr->kind != value_expr_t::O_COM) {
if (expr->kind < value_expr_t::TERMINALS) {
temp.reset(expr);
} else {
- temp.reset(new value_expr_t(value_expr_t::CONSTANT_V));
- temp->constant_v = new value_t();
- expr->compute(*(temp->constant_v), details, context);
+ temp.reset(new value_expr_t(value_expr_t::CONSTANT));
+ temp->value = new value_t;
+ expr->compute(*(temp->value), details, context);
}
} else {
temp.reset(new value_expr_t(value_expr_t::O_COM));
@@ -146,17 +137,16 @@ void value_expr_t::compute(value_t& result, const details_t& details,
{
try {
switch (kind) {
- case CONSTANT_I:
- result = constant_i;
- break;
- case CONSTANT_T:
- result = *constant_t;
- break;
- case CONSTANT_A:
- result = *constant_a;
+ case ZERO:
+ result = 0L;
break;
- case CONSTANT_V:
- result = *constant_v;
+
+ case ARG_INDEX:
+ throw new compute_error("Cannot directly compute an arg_index");
+
+ case CONSTANT:
+ assert(value);
+ result = *value;
break;
case F_NOW:
@@ -166,8 +156,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case AMOUNT:
if (details.xact) {
if (transaction_has_xdata(*details.xact) &&
- transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOSITE)
- result = transaction_xdata_(*details.xact).composite_amount;
+ transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOUND)
+ result = transaction_xdata_(*details.xact).value;
else
result = details.xact->amount;
}
@@ -184,8 +174,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
bool set = false;
if (transaction_has_xdata(*details.xact)) {
transaction_xdata_t& xdata(transaction_xdata_(*details.xact));
- if (xdata.dflags & TRANSACTION_COMPOSITE) {
- result = xdata.composite_amount.price();
+ if (xdata.dflags & TRANSACTION_COMPOUND) {
+ result = xdata.value.price();
set = true;
}
}
@@ -205,8 +195,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
bool set = false;
if (transaction_has_xdata(*details.xact)) {
transaction_xdata_t& xdata(transaction_xdata_(*details.xact));
- if (xdata.dflags & TRANSACTION_COMPOSITE) {
- result = xdata.composite_amount.cost();
+ if (xdata.dflags & TRANSACTION_COMPOUND) {
+ result = xdata.value.cost();
set = true;
}
}
@@ -353,31 +343,31 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break;
case F_PRICE: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result = result.price();
break;
}
case F_DATE: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result = result.date();
break;
}
case F_DATECMP: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result = result.date();
if (! result)
break;
- index = 0;
- expr = find_leaf(context, 1, index);
+ arg_index = 0;
+ expr = find_leaf(context, 1, arg_index);
value_t moment;
expr->compute(moment, details, context);
if (moment.type == value_t::DATETIME) {
@@ -394,8 +384,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_YEAR:
case F_MONTH:
case F_DAY: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
// jww (2006-03-05): Generate an error if result is not a DATETIME
@@ -417,8 +407,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
}
case F_ARITH_MEAN: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
if (details.xact && transaction_has_xdata(*details.xact)) {
expr->compute(result, details, context);
result /= amount_t(long(transaction_xdata_(*details.xact).index + 1));
@@ -440,24 +430,24 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break;
case F_ABS: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result.abs();
break;
}
case F_ROUND: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
result.round();
break;
}
case F_COMMODITY: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
if (result.type != value_t::AMOUNT)
throw new compute_error("Argument to commodity() must be a commoditized amount",
@@ -469,17 +459,18 @@ void value_expr_t::compute(value_t& result, const details_t& details,
}
case F_SET_COMMODITY: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
value_t temp;
expr->compute(temp, details, context);
- index = 0;
- expr = find_leaf(context, 1, index);
+ arg_index = 0;
+ expr = find_leaf(context, 1, arg_index);
expr->compute(result, details, context);
if (result.type != value_t::AMOUNT)
- throw new compute_error("Second argument to set_commodity() must be a commoditized amount",
- new valexpr_context(expr));
+ throw new compute_error
+ ("Second argument to set_commodity() must be a commoditized amount",
+ new valexpr_context(expr));
amount_t one("1");
one.set_commodity(((amount_t *) result.data)->commodity());
result = one;
@@ -489,8 +480,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
}
case F_QUANTITY: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
balance_t * bal = NULL;
@@ -578,10 +569,10 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break;
case O_ARG: {
- int index = 0;
+ int arg_index = 0;
assert(left);
- assert(left->kind == CONSTANT_I);
- value_expr_t * expr = find_leaf(context, left->constant_i, index);
+ assert(left->kind == ARG_INDEX);
+ value_expr_t * expr = find_leaf(context, left->arg_index, arg_index);
if (expr)
expr->compute(result, details, context);
else
@@ -602,7 +593,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case O_REF: {
assert(left);
if (right) {
- value_auto_ptr args(reduce_leaves(right, details, context));
+ value_expr args(reduce_leaves(right, details, context));
left->compute(result, details, args.get());
} else {
left->compute(result, details, context);
@@ -611,12 +602,12 @@ void value_expr_t::compute(value_t& result, const details_t& details,
}
case F_VALUE: {
- int index = 0;
- value_expr_t * expr = find_leaf(context, 0, index);
+ int arg_index = 0;
+ value_expr_t * expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context);
- index = 0;
- expr = find_leaf(context, 1, index);
+ arg_index = 0;
+ expr = find_leaf(context, 1, arg_index);
value_t moment;
expr->compute(moment, details, context);
if (moment.type != value_t::DATETIME)
@@ -750,30 +741,64 @@ static inline void unexpected(char c, char wanted = '\0') {
}
}
-value_expr_t * parse_value_term(std::istream& in, scope_t * scope);
+value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
+ const short flags);
-inline value_expr_t * parse_value_term(const char * p, scope_t * scope) {
+inline value_expr_t * parse_value_term(const char * p, scope_t * scope,
+ const short flags) {
std::istringstream stream(p);
- return parse_value_term(stream, scope);
+ return parse_value_term(stream, scope, flags);
}
-value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
+value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
+ const short flags)
{
- value_auto_ptr node;
+ value_expr node;
char buf[256];
char c = peek_next_nonws(in);
- if (std::isdigit(c) || c == '.') {
- READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.');
- if (std::strchr(buf, '.')) {
- node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
- node->constant_a = new amount_t;
- node->constant_a->parse(buf, AMOUNT_PARSE_NO_MIGRATE);
- } else {
- node.reset(new value_expr_t(value_expr_t::CONSTANT_I));
- node->constant_i = std::atol(buf);
+ if (flags & PARSE_VALEXPR_RELAXED) {
+ if (c == '@') {
+ in.get(c);
+ c = peek_next_nonws(in);
}
+ else if (! (c == '(' || c == '[' || c == '{' || c == '/')) {
+ amount_t temp;
+ char prev_c = c;
+ unsigned long pos = 0;
+ // When in relaxed parsing mode, we do want to migrate commodity
+ // flags, so that any precision specified by the user updates
+ // the current maximum precision displayed.
+ try {
+ pos = (long)in.tellg();
+ temp.parse(in);
+ }
+ catch (amount_error * err) {
+ // If the amount had no commodity, it must be an unambiguous
+ // variable reference
+ if (std::strcmp(err->what(), "No quantity specified for amount") == 0) {
+ in.clear();
+ in.seekg(pos, std::ios::beg);
+ c = prev_c;
+ goto parse_ident;
+ } else {
+ throw err;
+ }
+ }
+ node.reset(new value_expr_t(value_expr_t::CONSTANT));
+ node->value = new value_t(temp);
+ goto parsed;
+ }
+ }
+
+ parse_ident:
+ if (std::isdigit(c) || c == '.') {
+ READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.');
+ amount_t temp;
+ temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
+ node.reset(new value_expr_t(value_expr_t::CONSTANT));
+ node->value = new value_t(temp);
goto parsed;
}
else if (std::isalnum(c) || c == '_') {
@@ -818,7 +843,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
if (definition) {
std::auto_ptr<scope_t> params(new scope_t(scope));
- int index = 0;
+ int arg_index = 0;
if (have_args) {
bool done = false;
@@ -838,8 +863,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
// Define the parameter so that on lookup the parser will find
// an O_ARG value.
node.reset(new value_expr_t(value_expr_t::O_ARG));
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = index++;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = arg_index++;
params->define(ident, node.release());
}
@@ -851,20 +876,19 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
}
// Define the value associated with the defined identifier
- value_auto_ptr def(parse_boolean_expr(in, params.get()));
+ value_expr def(parse_boolean_expr(in, params.get(), flags));
if (! def.get())
throw new value_expr_error(std::string("Definition failed for '") + buf + "'");
node.reset(new value_expr_t(value_expr_t::O_DEF));
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = index;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = arg_index;
node->set_right(def.release());
scope->define(buf, node.release());
// Returning a dummy value in place of the definition
- node.reset(new value_expr_t(value_expr_t::CONSTANT_I));
- node->constant_i = 0;
+ node.reset(new value_expr_t(value_expr_t::ZERO));
} else {
assert(scope);
value_expr_t * def = scope->lookup(buf);
@@ -873,7 +897,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
(buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' ||
buf[0] == 'w' || buf[0] == 'W' || buf[0] == 'e'))
goto find_term;
- throw new value_expr_error(std::string("Unknown identifier '") + buf + "'");
+ throw new value_expr_error(std::string("Unknown identifier '") +
+ buf + "'");
}
else if (def->kind == value_expr_t::O_DEF) {
node.reset(new value_expr_t(value_expr_t::O_REF));
@@ -883,7 +908,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
if (have_args) {
in.clear();
in.seekg(beg, std::ios::beg);
- value_auto_ptr args(parse_value_expr(in, scope, true));
+ value_expr args
+ (parse_value_expr(in, scope, flags | PARSE_VALEXPR_PARTIAL));
if (peek_next_nonws(in) != ')') {
in.get(c);
@@ -897,11 +923,10 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
}
}
- if (count != def->left->constant_i) {
+ if (count != def->left->arg_index) {
std::ostringstream errmsg;
errmsg << "Wrong number of arguments to '" << buf
- << "': saw " << count
- << ", wanted " << def->left->constant_i;
+ << "': saw " << count << ", wanted " << def->left->arg_index;
throw new value_expr_error(errmsg.str());
}
}
@@ -918,7 +943,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
// Functions
case '^':
node.reset(new value_expr_t(value_expr_t::F_PARENT));
- node->set_left(parse_value_term(in, scope));
+ node->set_left(parse_value_term(in, scope, flags));
break;
// Other
@@ -979,34 +1004,21 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
}
case '{': {
- int paren_depth = 0;
- int i = 0;
- while (i < 255 && ! in.eof()) {
- in.get(c);
- if (c == '{') {
- paren_depth++;
- }
- else if (c == '}') {
- if (paren_depth == 0)
- break;
- paren_depth--;
- }
- buf[i++] = c;
- }
- buf[i] = '\0';
-
+ amount_t temp;
+ temp.parse(in, AMOUNT_PARSE_NO_MIGRATE);
+ in.get(c);
if (c != '}')
unexpected(c, '}');
- node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
- node->constant_a = new amount_t;
- node->constant_a->parse(buf, AMOUNT_PARSE_NO_MIGRATE);
+ node.reset(new value_expr_t(value_expr_t::CONSTANT));
+ node->value = new value_t(temp);
break;
}
case '(': {
std::auto_ptr<scope_t> locals(new scope_t(scope));
- node.reset(parse_value_expr(in, locals.get(), true));
+ node.reset(parse_value_expr(in, locals.get(),
+ flags | PARSE_VALEXPR_PARTIAL));
in.get(c);
if (c != ')')
unexpected(c, ')');
@@ -1019,10 +1031,9 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
unexpected(c, ']');
in.get(c);
- node.reset(new value_expr_t(value_expr_t::CONSTANT_T));
-
+ node.reset(new value_expr_t(value_expr_t::CONSTANT));
interval_t timespan(buf);
- node->constant_t = new datetime_t(timespan.first());
+ node->value = new value_t(datetime_t(timespan.first()));
break;
}
@@ -1035,19 +1046,20 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
return node.release();
}
-value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope)
+value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope,
+ const short flags)
{
- value_auto_ptr node;
+ value_expr node;
if (peek_next_nonws(in) == '%') {
char c;
in.get(c);
node.reset(new value_expr_t(value_expr_t::O_PERC));
- node->set_left(parse_value_term(in, scope));
+ node->set_left(parse_value_term(in, scope, flags));
return node.release();
}
- node.reset(parse_value_term(in, scope));
+ node.reset(parse_value_term(in, scope, flags));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
@@ -1055,18 +1067,18 @@ value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope)
in.get(c);
switch (c) {
case '*': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_MUL));
node->set_left(prev.release());
- node->set_right(parse_value_term(in, scope));
+ node->set_right(parse_value_term(in, scope, flags));
break;
}
case '/': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_DIV));
node->set_left(prev.release());
- node->set_right(parse_value_term(in, scope));
+ node->set_right(parse_value_term(in, scope, flags));
break;
}
}
@@ -1077,20 +1089,17 @@ value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope)
return node.release();
}
-value_expr_t * parse_add_expr(std::istream& in, scope_t * scope)
+value_expr_t * parse_add_expr(std::istream& in, scope_t * scope,
+ const short flags)
{
- value_auto_ptr node;
+ value_expr node;
if (peek_next_nonws(in) == '-') {
char c;
in.get(c);
- value_auto_ptr expr(parse_mul_expr(in, scope));
- if (expr->kind == value_expr_t::CONSTANT_I) {
- expr->constant_i = - expr->constant_i;
- return expr.release();
- }
- else if (expr->kind == value_expr_t::CONSTANT_A) {
- expr->constant_a->negate();
+ value_expr expr(parse_mul_expr(in, scope, flags));
+ if (expr->kind == value_expr_t::CONSTANT) {
+ expr->value->negate();
return expr.release();
}
node.reset(new value_expr_t(value_expr_t::O_NEG));
@@ -1098,7 +1107,7 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope)
return node.release();
}
- node.reset(parse_mul_expr(in, scope));
+ node.reset(parse_mul_expr(in, scope, flags));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
@@ -1106,18 +1115,18 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope)
in.get(c);
switch (c) {
case '+': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_ADD));
node->set_left(prev.release());
- node->set_right(parse_mul_expr(in, scope));
+ node->set_right(parse_mul_expr(in, scope, flags));
break;
}
case '-': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_SUB));
node->set_left(prev.release());
- node->set_right(parse_mul_expr(in, scope));
+ node->set_right(parse_mul_expr(in, scope, flags));
break;
}
}
@@ -1128,19 +1137,20 @@ value_expr_t * parse_add_expr(std::istream& in, scope_t * scope)
return node.release();
}
-value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope)
+value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope,
+ const short flags)
{
- value_auto_ptr node;
+ value_expr node;
if (peek_next_nonws(in) == '!') {
char c;
in.get(c);
node.reset(new value_expr_t(value_expr_t::O_NOT));
- node->set_left(parse_add_expr(in, scope));
+ node->set_left(parse_add_expr(in, scope, flags));
return node.release();
}
- node.reset(parse_add_expr(in, scope));
+ node.reset(parse_add_expr(in, scope, flags));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
@@ -1154,35 +1164,35 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope)
in.get(c);
else
unexpected(c, '=');
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(negate ? value_expr_t::O_NEQ :
value_expr_t::O_EQ));
node->set_left(prev.release());
- node->set_right(parse_add_expr(in, scope));
+ node->set_right(parse_add_expr(in, scope, flags));
break;
}
case '<': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_LT));
if (peek_next_nonws(in) == '=') {
in.get(c);
node->kind = value_expr_t::O_LTE;
}
node->set_left(prev.release());
- node->set_right(parse_add_expr(in, scope));
+ node->set_right(parse_add_expr(in, scope, flags));
break;
}
case '>': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_GT));
if (peek_next_nonws(in) == '=') {
in.get(c);
node->kind = value_expr_t::O_GTE;
}
node->set_left(prev.release());
- node->set_right(parse_add_expr(in, scope));
+ node->set_right(parse_add_expr(in, scope, flags));
break;
}
@@ -1197,9 +1207,10 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope)
return node.release();
}
-value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope)
+value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope,
+ const short flags)
{
- value_auto_ptr node(parse_logic_expr(in, scope));
+ value_expr node(parse_logic_expr(in, scope, flags));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
@@ -1207,32 +1218,32 @@ value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope)
in.get(c);
switch (c) {
case '&': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_AND));
node->set_left(prev.release());
- node->set_right(parse_logic_expr(in, scope));
+ node->set_right(parse_logic_expr(in, scope, flags));
break;
}
case '|': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_OR));
node->set_left(prev.release());
- node->set_right(parse_logic_expr(in, scope));
+ node->set_right(parse_logic_expr(in, scope, flags));
break;
}
case '?': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_QUES));
node->set_left(prev.release());
node->set_right(new value_expr_t(value_expr_t::O_COL));
- node->right->set_left(parse_logic_expr(in, scope));
+ node->right->set_left(parse_logic_expr(in, scope, flags));
c = peek_next_nonws(in);
if (c != ':')
unexpected(c, ':');
in.get(c);
- node->right->set_right(parse_logic_expr(in, scope));
+ node->right->set_right(parse_logic_expr(in, scope, flags));
break;
}
@@ -1331,43 +1342,43 @@ void init_value_expr()
// Functions
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_ABS));
globals->define("U", node);
globals->define("abs", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_ROUND));
globals->define("round", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_QUANTITY));
globals->define("S", node);
globals->define("quant", node);
globals->define("quantity", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_COMMODITY));
globals->define("comm", node);
globals->define("commodity", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 2;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 2;
node->set_right(new value_expr_t(value_expr_t::F_SET_COMMODITY));
globals->define("setcomm", node);
globals->define("set_commodity", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_ARITH_MEAN));
globals->define("A", node);
globals->define("avg", node);
@@ -1375,87 +1386,87 @@ void init_value_expr()
globals->define("average", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 2;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 2;
node->set_right(new value_expr_t(value_expr_t::F_VALUE));
globals->define("P", node);
- parse_value_definition("value=P(t,m)", globals);
- parse_value_definition("total_value=P(T,m)", globals);
- parse_value_definition("valueof(x)=P(x,m)", globals);
- parse_value_definition("datedvalueof(x,y)=P(x,y)", globals);
+ parse_value_definition("@value=@P(@t,@m)", globals);
+ parse_value_definition("@total_value=@P(@T,@m)", globals);
+ parse_value_definition("@valueof(x)=@P(@x,@m)", globals);
+ parse_value_definition("@datedvalueof(x,y)=@P(@x,@y)", globals);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_PRICE));
globals->define("priceof", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_DATE));
globals->define("dateof", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 2;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 2;
node->set_right(new value_expr_t(value_expr_t::F_DATECMP));
globals->define("datecmp", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_YEAR));
globals->define("yearof", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_MONTH));
globals->define("monthof", node);
node = new value_expr_t(value_expr_t::O_DEF);
- node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
- node->left->constant_i = 1;
+ node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
+ node->left->arg_index = 1;
node->set_right(new value_expr_t(value_expr_t::F_DAY));
globals->define("dayof", node);
- parse_value_definition("year=yearof(d)", globals);
- parse_value_definition("month=monthof(d)", globals);
- parse_value_definition("day=dayof(d)", globals);
+ parse_value_definition("@year=@yearof(@d)", globals);
+ parse_value_definition("@month=@monthof(@d)", globals);
+ parse_value_definition("@day=@dayof(@d)", globals);
// Macros
- node = parse_value_expr("P(a,d)");
+ node = parse_value_expr("@P(@a,@d)");
globals->define("v", node);
globals->define("market", node);
- node = parse_value_expr("P(O,d)");
+ node = parse_value_expr("@P(@O,@d)");
globals->define("V", node);
globals->define("total_market", node);
- node = parse_value_expr("v-b");
+ node = parse_value_expr("@v-@b");
globals->define("g", node);
globals->define("gain", node);
- node = parse_value_expr("V-B");
+ node = parse_value_expr("@V-@B");
globals->define("G", node);
globals->define("total_gain", node);
- parse_value_definition("min(x,y)=x<y?x:y", globals);
- parse_value_definition("max(x,y)=x>y?x:y", globals);
+ parse_value_definition("@min(x,y)=@x<@y?@x:@y", globals);
+ parse_value_definition("@max(x,y)=@x>@y?@x:@y", globals);
}
value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
- const bool partial)
+ const short flags)
{
if (! global_scope.get())
init_value_expr();
std::auto_ptr<scope_t> this_scope(new scope_t(scope ? scope :
global_scope.get()));
- value_auto_ptr node;
- node.reset(parse_boolean_expr(in, this_scope.get()));
+ value_expr node;
+ node.reset(parse_boolean_expr(in, this_scope.get(), flags));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
@@ -1463,10 +1474,10 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
in.get(c);
switch (c) {
case ',': {
- value_auto_ptr prev(node.release());
+ value_expr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_COM));
node->set_left(prev.release());
- node->set_right(parse_logic_expr(in, this_scope.get()));
+ node->set_right(parse_logic_expr(in, this_scope.get(), flags));
break;
}
@@ -1486,7 +1497,7 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
throw new value_expr_error(std::string("Failed to parse value expression"));
else
unexpected(c);
- } else if (! partial) {
+ } else if (! (flags & PARSE_VALEXPR_PARTIAL)) {
in.get(c);
if (! in.eof())
unexpected(c);
@@ -1499,7 +1510,7 @@ value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
valexpr_context::valexpr_context(const ledger::value_expr_t * _expr,
const std::string& desc) throw()
- : expr(NULL), error_node(_expr), error_context(desc)
+ : expr(_expr), error_node(_expr), error_context(desc)
{
error_node->acquire();
}
@@ -1513,7 +1524,7 @@ valexpr_context::~valexpr_context() throw()
void valexpr_context::describe(std::ostream& out) const throw()
{
if (! expr) {
- out << "Valexpr_context expr not set!" << std::endl;
+ out << "valexpr_context expr not set!" << std::endl;
return;
}
@@ -1524,7 +1535,8 @@ void valexpr_context::describe(std::ostream& out) const throw()
unsigned long start = (long)out.tellp() - 1;
unsigned long begin;
unsigned long end;
- bool found = ledger::write_value_expr(out, expr, error_node, &begin, &end);
+ bool found = ledger::write_value_expr(out, expr, true,
+ error_node, &begin, &end);
out << std::endl;
if (found) {
out << " ";
@@ -1540,132 +1552,122 @@ void valexpr_context::describe(std::ostream& out) const throw()
bool write_value_expr(std::ostream& out,
const value_expr_t * node,
+ const bool relaxed,
const value_expr_t * node_to_find,
unsigned long * start_pos,
unsigned long * end_pos)
{
+ int arg_index = 0;
bool found = false;
+ value_expr_t * expr;
if (start_pos && node == node_to_find) {
*start_pos = (long)out.tellp() - 1;
found = true;
}
+ std::string symbol;
+
switch (node->kind) {
- case value_expr_t::CONSTANT_I:
- out << node->constant_i;
- break;
- case value_expr_t::CONSTANT_T:
- out << "[" << *(node->constant_t) << ']';
- break;
- case value_expr_t::CONSTANT_A:
- out << "{" << *(node->constant_a) << '}';
- break;
- case value_expr_t::CONSTANT_V:
- out << "{" << *(node->constant_v) << '}';
- break;
-
- case value_expr_t::AMOUNT: out << "amount"; break;
- case value_expr_t::PRICE: out << "price"; break;
- case value_expr_t::COST: out << "cost"; break;
- case value_expr_t::DATE: out << "date"; break;
- case value_expr_t::ACT_DATE: out << "actual_date"; break;
- case value_expr_t::EFF_DATE: out << "effective_date"; break;
- case value_expr_t::CLEARED: out << "cleared"; break;
- case value_expr_t::PENDING: out << "pending"; break;
- case value_expr_t::REAL: out << "real"; break;
- case value_expr_t::ACTUAL: out << "actual"; break;
- case value_expr_t::INDEX: out << "index"; break;
- case value_expr_t::COUNT: out << "count"; break;
- case value_expr_t::DEPTH: out << "depth"; break;
- case value_expr_t::TOTAL: out << "total"; break;
- case value_expr_t::PRICE_TOTAL: out << "total_price"; break;
- case value_expr_t::COST_TOTAL: out << "total_cost"; break;
- case value_expr_t::F_NOW: out << "now"; break;
+ case value_expr_t::ZERO:
+ out << '0';
+ break;
+ case value_expr_t::ARG_INDEX:
+ break;
+
+ case value_expr_t::CONSTANT:
+ switch (node->value->type) {
+ case value_t::BOOLEAN:
+ assert(0);
+ break;
+ case value_t::DATETIME:
+ out << '[' << *(node->value) << ']';
+ break;
+ case value_t::INTEGER:
+ case value_t::AMOUNT:
+ if (! relaxed)
+ out << '{';
+ out << *(node->value);
+ if (! relaxed)
+ out << '}';
+ break;
+ case value_t::BALANCE:
+ case value_t::BALANCE_PAIR:
+ assert(0);
+ break;
+ }
+ break;
+
+ case value_expr_t::AMOUNT:
+ symbol = "amount"; break;
+ case value_expr_t::PRICE:
+ symbol = "price"; break;
+ case value_expr_t::COST:
+ symbol = "cost"; break;
+ case value_expr_t::DATE:
+ symbol = "date"; break;
+ case value_expr_t::ACT_DATE:
+ symbol = "actual_date"; break;
+ case value_expr_t::EFF_DATE:
+ symbol = "effective_date"; break;
+ case value_expr_t::CLEARED:
+ symbol = "cleared"; break;
+ case value_expr_t::PENDING:
+ symbol = "pending"; break;
+ case value_expr_t::REAL:
+ symbol = "real"; break;
+ case value_expr_t::ACTUAL:
+ symbol = "actual"; break;
+ case value_expr_t::INDEX:
+ symbol = "index"; break;
+ case value_expr_t::COUNT:
+ symbol = "count"; break;
+ case value_expr_t::DEPTH:
+ symbol = "depth"; break;
+ case value_expr_t::TOTAL:
+ symbol = "total"; break;
+ case value_expr_t::PRICE_TOTAL:
+ symbol = "total_price"; break;
+ case value_expr_t::COST_TOTAL:
+ symbol = "total_cost"; break;
+ case value_expr_t::F_NOW:
+ symbol = "now"; break;
case value_expr_t::VALUE_EXPR:
- if (write_value_expr(out, amount_expr->parsed,
+ if (write_value_expr(out, amount_expr.get(), relaxed,
node_to_find, start_pos, end_pos))
found = true;
break;
case value_expr_t::TOTAL_EXPR:
- if (write_value_expr(out, total_expr->parsed,
+ if (write_value_expr(out, total_expr.get(), relaxed,
node_to_find, start_pos, end_pos))
found = true;
break;
case value_expr_t::F_ARITH_MEAN:
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_ABS: out << "abs"; break;
- out << "abs(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_QUANTITY: out << "quantity"; break;
- out << "quantity(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_COMMODITY: out << "commodity"; break;
- out << "commodity(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_SET_COMMODITY: out << "set_commodity"; break;
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_VALUE: out << "valueof"; break;
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_PRICE: out << "priceof"; break;
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_DATE: out << "dateof"; break;
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_DATECMP: out << "datecmp"; break;
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_YEAR: out << "yearof"; break;
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_MONTH: out << "monthof"; break;
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case value_expr_t::F_DAY: out << "dayof"; break;
- out << "average(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
+ symbol = "average"; break;
+ case value_expr_t::F_ABS:
+ symbol = "abs"; break;
+ case value_expr_t::F_QUANTITY:
+ symbol = "quantity"; break;
+ case value_expr_t::F_COMMODITY:
+ symbol = "commodity"; break;
+ case value_expr_t::F_SET_COMMODITY:
+ symbol = "set_commodity"; break;
+ case value_expr_t::F_VALUE:
+ symbol = "valueof"; break;
+ case value_expr_t::F_PRICE:
+ symbol = "priceof"; break;
+ case value_expr_t::F_DATE:
+ symbol = "dateof"; break;
+ case value_expr_t::F_DATECMP:
+ symbol = "datecmp"; break;
+ case value_expr_t::F_YEAR:
+ symbol = "yearof"; break;
+ case value_expr_t::F_MONTH:
+ symbol = "monthof"; break;
+ case value_expr_t::F_DAY:
+ symbol = "dayof"; break;
case value_expr_t::F_CODE_MASK:
out << "c/" << node->mask->pattern << "/";
@@ -1688,160 +1690,169 @@ bool write_value_expr(std::ostream& out,
case value_expr_t::O_NOT:
out << "!";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
break;
case value_expr_t::O_NEG:
out << "-";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
break;
case value_expr_t::O_PERC:
out << "%";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
break;
case value_expr_t::O_ARG:
- out << "arg" << node->constant_i;
+ out << "@arg" << node->arg_index;
break;
case value_expr_t::O_DEF:
out << "O_DEF";
break;
+
case value_expr_t::O_REF:
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ found = true;
+ if (node->right) {
+ out << "(";
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ found = true;
+ out << ")";
+ }
break;
case value_expr_t::O_COM:
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ", ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
break;
case value_expr_t::O_QUES:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " ? ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_COL:
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " : ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
break;
- case value_expr_t::O_AND: out << "O_AND"; break;
+ case value_expr_t::O_AND:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " & ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
- case value_expr_t::O_OR: out << "O_OR"; break;
+ case value_expr_t::O_OR:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " | ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_NEQ:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " != ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_EQ:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " == ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_LT:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " < ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_LTE:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " <= ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_GT:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " > ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_GTE:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " >= ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_ADD:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " + ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_SUB:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " - ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_MUL:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " * ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case value_expr_t::O_DIV:
out << "(";
- if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << " / ";
- if (write_value_expr(out, node->right, node_to_find, start_pos, end_pos))
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
@@ -1852,6 +1863,12 @@ bool write_value_expr(std::ostream& out,
break;
}
+ if (! symbol.empty()) {
+ if (commodity_t::find(symbol))
+ out << '@';
+ out << symbol;
+ }
+
if (end_pos && node == node_to_find)
*end_pos = (long)out.tellp() - 1;
@@ -1869,17 +1886,14 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
out << " ";
switch (node->kind) {
- case value_expr_t::CONSTANT_I:
- out << "CONSTANT_I - " << node->constant_i;
- break;
- case value_expr_t::CONSTANT_T:
- out << "CONSTANT_T - [" << *(node->constant_t) << ']';
+ case value_expr_t::ZERO:
+ out << "ZERO";
break;
- case value_expr_t::CONSTANT_A:
- out << "CONSTANT_A - {" << *(node->constant_a) << '}';
+ case value_expr_t::ARG_INDEX:
+ out << "ARG_INDEX - " << node->arg_index;
break;
- case value_expr_t::CONSTANT_V:
- out << "CONSTANT_V - {" << *(node->constant_v) << '}';
+ case value_expr_t::CONSTANT:
+ out << "CONSTANT - " << *(node->value);
break;
case value_expr_t::AMOUNT: out << "AMOUNT"; break;
diff --git a/valexpr.h b/valexpr.h
index 429a35cf..6e9c8bd6 100644
--- a/valexpr.h
+++ b/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));
}
//////////////////////////////////////////////////////////////////////
diff --git a/walk.cc b/walk.cc
index 8a60cfaa..a91bf189 100644
--- a/walk.cc
+++ b/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));
+ if (date)
+ xdata.date = date;
+ if (flags)
+ xdata.dflags |= flags;
+
+ value_t temp(value);
+
switch (value.type) {
case value_t::BOOLEAN:
- xact.amount = *((bool *) value.data);
- break;
+ case value_t::DATETIME:
case value_t::INTEGER:
- xact.amount = *((long *) value.data);
- break;
+ temp.cast(value_t::AMOUNT);
+ // fall through...
+
case value_t::AMOUNT:
- xact.amount = *((amount_t *) value.data);
+ xact.amount = *((amount_t *) temp.data);
break;
case value_t::BALANCE:
case value_t::BALANCE_PAIR:
- xdata.composite_amount = value;
- flags |= TRANSACTION_COMPOSITE;
+ xdata.value = temp;
+ flags |= TRANSACTION_COMPOUND;
break;
}
- if (date)
- xdata.date = date;
- if (flags)
- xdata.dflags |= flags;
-
handler(xact);
}
@@ -830,7 +832,7 @@ void walk_accounts(account_t& account,
const std::string& sort_string)
{
if (! sort_string.empty()) {
- value_auto_ptr sort_order;
+ value_expr sort_order;
sort_order.reset(parse_value_expr(sort_string));
walk_accounts(account, handler, sort_order.get());
} else {
diff --git a/walk.h b/walk.h
index 1e377aba..6da2e9d2 100644
--- a/walk.h
+++ b/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;
diff --git a/xml.cc b/xml.cc
index 5b060296..23bb8d84 100644
--- a/xml.cc
+++ b/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";