summaryrefslogtreecommitdiff
path: root/valexpr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'valexpr.cc')
-rw-r--r--valexpr.cc1037
1 files changed, 1037 insertions, 0 deletions
diff --git a/valexpr.cc b/valexpr.cc
new file mode 100644
index 00000000..ee02dafe
--- /dev/null
+++ b/valexpr.cc
@@ -0,0 +1,1037 @@
+#include "valexpr.h"
+#include "walk.h"
+#include "error.h"
+#include "datetime.h"
+#include "debug.h"
+#include "util.h"
+#ifdef USE_BOOST_PYTHON
+#include "py_eval.h"
+#endif
+
+namespace ledger {
+
+std::auto_ptr<value_expr_t> amount_expr;
+std::auto_ptr<value_expr_t> total_expr;
+
+void value_expr_t::compute(value_t& result, const details_t& details) const
+{
+ switch (kind) {
+ case CONSTANT_I:
+ result = constant_i;
+ break;
+ case CONSTANT_T:
+ result = long(constant_t);
+ break;
+
+ case CONSTANT_A:
+ result = constant_a;
+ break;
+
+ 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;
+ else
+ result = details.xact->amount;
+ }
+ else if (details.account && account_has_xdata(*details.account)) {
+ result = account_xdata(*details.account).value;
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case COST:
+ if (details.xact) {
+ bool set = false;
+ if (transaction_has_xdata(*details.xact)) {
+ transaction_xdata_t& xdata(transaction_xdata_(*details.xact));
+ if (xdata.dflags & TRANSACTION_COMPOSITE) {
+ if (xdata.composite_amount.type == value_t::BALANCE_PAIR &&
+ ((balance_pair_t *) xdata.composite_amount.data)->cost)
+ result = *((balance_pair_t *) xdata.composite_amount.data)->cost;
+ else
+ result = xdata.composite_amount;
+ set = true;
+ }
+ }
+
+ if (! set) {
+ if (details.xact->cost)
+ result = *details.xact->cost;
+ else
+ result = details.xact->amount;
+ }
+ }
+ else if (details.account && account_has_xdata(*details.account)) {
+ result = account_xdata(*details.account).value.cost();
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case TOTAL:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = transaction_xdata_(*details.xact).total;
+ else if (details.account && account_has_xdata(*details.account))
+ result = account_xdata(*details.account).total;
+ else
+ result = 0L;
+ break;
+ case COST_TOTAL:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = transaction_xdata_(*details.xact).total.cost();
+ else if (details.account && account_has_xdata(*details.account))
+ result = account_xdata(*details.account).total.cost();
+ else
+ result = 0L;
+ break;
+
+ case VALUE_EXPR:
+ if (amount_expr.get())
+ amount_expr->compute(result, details);
+ else
+ result = 0L;
+ break;
+ case TOTAL_EXPR:
+ if (total_expr.get())
+ total_expr->compute(result, details);
+ else
+ result = 0L;
+ break;
+
+ case DATE:
+ if (details.xact && transaction_has_xdata(*details.xact) &&
+ transaction_xdata_(*details.xact).date)
+ result = long(transaction_xdata_(*details.xact).date);
+ else if (details.entry)
+ result = long(details.entry->date);
+ else
+ result = long(now);
+ break;
+
+ case CLEARED:
+ if (details.entry)
+ result = details.entry->state == entry_t::CLEARED;
+ else
+ result = false;
+ break;
+
+ case REAL:
+ if (details.xact)
+ result = ! (details.xact->flags & TRANSACTION_VIRTUAL);
+ else
+ result = true;
+ break;
+
+ case ACTUAL:
+ if (details.xact)
+ result = ! (details.xact->flags & TRANSACTION_AUTO);
+ else
+ result = true;
+ break;
+
+ case INDEX:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = long(transaction_xdata_(*details.xact).index + 1);
+ else if (details.account && account_has_xdata(*details.account))
+ result = long(account_xdata(*details.account).count);
+ else
+ result = 0L;
+ break;
+
+ case COUNT:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = long(transaction_xdata_(*details.xact).index + 1);
+ else if (details.account && account_has_xdata(*details.account))
+ result = long(account_xdata(*details.account).total_count);
+ else
+ result = 0L;
+ break;
+
+ case DEPTH:
+ if (details.account)
+ result = long(details.account->depth);
+ else
+ result = 0L;
+ break;
+
+ case F_ARITH_MEAN:
+ if (details.xact && transaction_has_xdata(*details.xact)) {
+ assert(left);
+ left->compute(result, details);
+ result /= amount_t(long(transaction_xdata_(*details.xact).index + 1));
+ }
+ else if (details.account && account_has_xdata(*details.account) &&
+ account_xdata(*details.account).total_count) {
+ assert(left);
+ left->compute(result, details);
+ result /= amount_t(long(account_xdata(*details.account).total_count));
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case F_PARENT:
+ if (details.account && details.account->parent)
+ left->compute(result, details_t(*details.account->parent));
+ break;
+
+ case F_NEG:
+ assert(left);
+ left->compute(result, details);
+ result.negate();
+ break;
+
+ case F_ABS:
+ assert(left);
+ left->compute(result, details);
+ result.abs();
+ break;
+
+ case F_STRIP: {
+ assert(left);
+ left->compute(result, details);
+
+ balance_t * bal = NULL;
+ switch (result.type) {
+ case value_t::BALANCE_PAIR:
+ bal = &((balance_pair_t *) result.data)->quantity;
+ // fall through...
+
+ case value_t::BALANCE:
+ if (! bal)
+ bal = (balance_t *) result.data;
+
+ if (bal->amounts.size() < 2) {
+ result.cast(value_t::AMOUNT);
+ } else {
+ value_t temp;
+ for (amounts_map::const_iterator i = bal->amounts.begin();
+ i != bal->amounts.end();
+ i++) {
+ amount_t x = (*i).second;
+ x.clear_commodity();
+ temp += x;
+ }
+ result = temp;
+ assert(temp.type == value_t::AMOUNT);
+ }
+ // fall through...
+
+ case value_t::AMOUNT:
+ ((amount_t *) result.data)->clear_commodity();
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ case F_PAYEE_MASK:
+ assert(mask);
+ if (details.entry)
+ result = mask->match(details.entry->payee);
+ else
+ result = false;
+ break;
+
+ case F_ACCOUNT_MASK:
+ assert(mask);
+ if (details.account)
+ result = mask->match(details.account->fullname());
+ else
+ result = false;
+ break;
+
+ case F_SHORT_ACCOUNT_MASK:
+ assert(mask);
+ if (details.account)
+ result = mask->match(details.account->name);
+ else
+ result = false;
+ break;
+
+ case F_VALUE: {
+ assert(left);
+ left->compute(result, details);
+
+ std::time_t moment = now;
+ if (right) {
+ switch (right->kind) {
+ case DATE:
+ if (details.xact && transaction_has_xdata(*details.xact) &&
+ transaction_xdata_(*details.xact).date)
+ moment = transaction_xdata_(*details.xact).date;
+ else if (details.entry)
+ moment = details.entry->date;
+ break;
+ case CONSTANT_T:
+ moment = right->constant_t;
+ break;
+ default:
+ throw compute_error("Invalid date passed to P(value,date)");
+ }
+ }
+
+ result = result.value(moment);
+ break;
+ }
+
+ case F_INTERP_FUNC: {
+#ifdef USE_BOOST_PYTHON
+ if (! python_call(constant_s, right, details, result))
+ result = 0L;
+#else
+ result = 0L;
+#endif
+ break;
+ }
+
+ case O_NOT:
+ left->compute(result, details);
+ result.negate();
+ break;
+
+ case O_QUES: {
+ assert(left);
+ assert(right);
+ assert(right->kind == O_COL);
+ left->compute(result, details);
+ if (result)
+ right->left->compute(result, details);
+ else
+ right->right->compute(result, details);
+ break;
+ }
+
+ case O_AND:
+ assert(left);
+ assert(right);
+ left->compute(result, details);
+ if (result)
+ right->compute(result, details);
+ break;
+
+ case O_OR:
+ assert(left);
+ assert(right);
+ left->compute(result, details);
+ if (! result)
+ right->compute(result, details);
+ break;
+
+ case O_EQ:
+ case O_LT:
+ case O_LTE:
+ case O_GT:
+ case O_GTE: {
+ assert(left);
+ assert(right);
+ value_t temp;
+ left->compute(temp, details);
+ right->compute(result, details);
+ switch (kind) {
+ case O_EQ: result = temp == result; break;
+ case O_LT: result = temp < result; break;
+ case O_LTE: result = temp <= result; break;
+ case O_GT: result = temp > result; break;
+ case O_GTE: result = temp >= result; break;
+ default: assert(0); break;
+ }
+ break;
+ }
+
+ case O_ADD:
+ case O_SUB:
+ case O_MUL:
+ case O_DIV: {
+ assert(left);
+ assert(right);
+ value_t temp;
+ right->compute(temp, details);
+ left->compute(result, details);
+ switch (kind) {
+ case O_ADD: result += temp; break;
+ case O_SUB: result -= temp; break;
+ case O_MUL: result *= temp; break;
+ case O_DIV: result /= temp; break;
+ default: assert(0); break;
+ }
+ break;
+ }
+
+ case LAST:
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static inline void unexpected(char c, char wanted = '\0') {
+ if ((unsigned char) c == 0xff) {
+ if (wanted)
+ throw value_expr_error(std::string("Missing '") + wanted + "'");
+ else
+ throw value_expr_error("Unexpected end");
+ } else {
+ if (wanted)
+ throw value_expr_error(std::string("Invalid char '") + c +
+ "' (wanted '" + wanted + "')");
+ else
+ throw value_expr_error(std::string("Invalid char '") + c + "'");
+ }
+}
+
+value_expr_t * parse_value_term(std::istream& in);
+
+inline value_expr_t * parse_value_term(const char * p) {
+ std::istringstream stream(p);
+ return parse_value_term(stream);
+}
+
+value_expr_t * parse_value_term(std::istream& in)
+{
+ std::auto_ptr<value_expr_t> node;
+
+ char buf[256];
+ char c = peek_next_nonws(in);
+ if (std::isdigit(c)) {
+ READ_INTO(in, buf, 255, c, std::isdigit(c));
+
+ node.reset(new value_expr_t(value_expr_t::CONSTANT_I));
+ node->constant_i = std::atol(buf);
+ return node.release();
+ }
+ else if (c == '{') {
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != '}');
+ if (c == '}')
+ in.get(c);
+ else
+ unexpected(c, '}');
+
+ node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
+ node->constant_a.parse(buf);
+ return node.release();
+ }
+
+ in.get(c);
+ switch (c) {
+ // Basic terms
+ case 'm':
+ node.reset(new value_expr_t(value_expr_t::CONSTANT_T));
+ node->constant_t = now;
+ break;
+
+ case 'a': node.reset(new value_expr_t(value_expr_t::AMOUNT)); break;
+ case 'b': node.reset(new value_expr_t(value_expr_t::COST)); break;
+ case 'd': node.reset(new value_expr_t(value_expr_t::DATE)); break;
+ case 'X': node.reset(new value_expr_t(value_expr_t::CLEARED)); break;
+ case 'R': node.reset(new value_expr_t(value_expr_t::REAL)); break;
+ case 'L': node.reset(new value_expr_t(value_expr_t::ACTUAL)); break;
+ case 'n': node.reset(new value_expr_t(value_expr_t::INDEX)); break;
+ case 'N': node.reset(new value_expr_t(value_expr_t::COUNT)); break;
+ case 'l': node.reset(new value_expr_t(value_expr_t::DEPTH)); break;
+ case 'O': node.reset(new value_expr_t(value_expr_t::TOTAL)); break;
+ case 'B': node.reset(new value_expr_t(value_expr_t::COST_TOTAL)); break;
+
+ // Relating to format_t
+ case 't': node.reset(new value_expr_t(value_expr_t::VALUE_EXPR)); break;
+ case 'T': node.reset(new value_expr_t(value_expr_t::TOTAL_EXPR)); break;
+
+ // Compound terms
+ case 'v': node.reset(parse_value_expr("P(a,d)")); break;
+ case 'V': node.reset(parse_value_term("P(O,d)")); break;
+ case 'g': node.reset(parse_value_expr("v-b")); break;
+ case 'G': node.reset(parse_value_expr("V-B")); break;
+
+ // Functions
+ case '^':
+ node.reset(new value_expr_t(value_expr_t::F_PARENT));
+ node->left = parse_value_term(in);
+ break;
+
+ case '-':
+ node.reset(new value_expr_t(value_expr_t::F_NEG));
+ node->left = parse_value_term(in);
+ break;
+
+ case 'U':
+ node.reset(new value_expr_t(value_expr_t::F_ABS));
+ node->left = parse_value_term(in);
+ break;
+
+ case 'S':
+ node.reset(new value_expr_t(value_expr_t::F_STRIP));
+ node->left = parse_value_term(in);
+ break;
+
+ case 'A':
+ node.reset(new value_expr_t(value_expr_t::F_ARITH_MEAN));
+ node->left = parse_value_term(in);
+ break;
+
+ case 'P':
+ node.reset(new value_expr_t(value_expr_t::F_VALUE));
+ if (peek_next_nonws(in) == '(') {
+ in.get(c);
+ node->left = parse_value_expr(in, true);
+ if (peek_next_nonws(in) == ',') {
+ in.get(c);
+ node->right = parse_value_expr(in, true);
+ }
+ in.get(c);
+ if (c != ')')
+ unexpected(c, ')');
+ } else {
+ node->left = parse_value_term(in);
+ }
+ break;
+
+ // Other
+ case '/': {
+ bool payee_mask = false;
+ bool short_account_mask = false;
+
+ c = peek_next_nonws(in);
+ if (c == '/') {
+ in.get(c);
+ c = in.peek();
+ if (c == '/') {
+ in.get(c);
+ c = in.peek();
+ short_account_mask = true;
+ } else {
+ payee_mask = true;
+ }
+ }
+
+ READ_INTO(in, buf, 255, c, c != '/');
+ if (c != '/')
+ unexpected(c, '/');
+
+ in.get(c);
+ node.reset(new value_expr_t(short_account_mask ?
+ value_expr_t::F_SHORT_ACCOUNT_MASK :
+ (payee_mask ? value_expr_t::F_PAYEE_MASK :
+ value_expr_t::F_ACCOUNT_MASK)));
+ node->mask = new mask_t(buf);
+ break;
+ }
+
+ case '@': {
+ READ_INTO(in, buf, 255, c, c != '(');
+ if (c != '(')
+ unexpected(c, '(');
+
+ node.reset(new value_expr_t(value_expr_t::F_INTERP_FUNC));
+ node->constant_s = buf;
+
+ in.get(c);
+ if (peek_next_nonws(in) == ')') {
+ in.get(c);
+ } else {
+ node->right = new value_expr_t(value_expr_t::O_ARG);
+ value_expr_t * cur = node->right;
+ cur->left = parse_value_expr(in, true);
+ in.get(c);
+ while (! in.eof() && c == ',') {
+ cur->right = new value_expr_t(value_expr_t::O_ARG);
+ cur = cur->right;
+ cur->left = parse_value_expr(in, true);
+ in.get(c);
+ }
+ if (c != ')')
+ unexpected(c, ')');
+ }
+ break;
+ }
+
+ case '(':
+ node.reset(parse_value_expr(in, true));
+ in.get(c);
+ if (c != ')')
+ unexpected(c, ')');
+ break;
+
+ case '[': {
+ READ_INTO(in, buf, 255, c, c != ']');
+ if (c != ']')
+ unexpected(c, ']');
+ in.get(c);
+
+ node.reset(new value_expr_t(value_expr_t::CONSTANT_T));
+
+ interval_t timespan(buf);
+ node->constant_t = timespan.first();
+ break;
+ }
+
+ default:
+ in.unget();
+ break;
+ }
+
+ return node.release();
+}
+
+value_expr_t * parse_mul_expr(std::istream& in)
+{
+ std::auto_ptr<value_expr_t> node(parse_value_term(in));
+
+ if (node.get() && ! in.eof()) {
+ char c = peek_next_nonws(in);
+ while (c == '*' || c == '/') {
+ in.get(c);
+ switch (c) {
+ case '*': {
+ std::auto_ptr<value_expr_t> prev(node.release());
+ node.reset(new value_expr_t(value_expr_t::O_MUL));
+ node->left = prev.release();
+ node->right = parse_value_term(in);
+ break;
+ }
+
+ case '/': {
+ std::auto_ptr<value_expr_t> prev(node.release());
+ node.reset(new value_expr_t(value_expr_t::O_DIV));
+ node->left = prev.release();
+ node->right = parse_value_term(in);
+ break;
+ }
+ }
+ c = peek_next_nonws(in);
+ }
+ }
+
+ return node.release();
+}
+
+value_expr_t * parse_add_expr(std::istream& in)
+{
+ std::auto_ptr<value_expr_t> node(parse_mul_expr(in));
+
+ if (node.get() && ! in.eof()) {
+ char c = peek_next_nonws(in);
+ while (c == '+' || c == '-') {
+ in.get(c);
+ switch (c) {
+ case '+': {
+ std::auto_ptr<value_expr_t> prev(node.release());
+ node.reset(new value_expr_t(value_expr_t::O_ADD));
+ node->left = prev.release();
+ node->right = parse_mul_expr(in);
+ break;
+ }
+
+ case '-': {
+ std::auto_ptr<value_expr_t> prev(node.release());
+ node.reset(new value_expr_t(value_expr_t::O_SUB));
+ node->left = prev.release();
+ node->right = parse_mul_expr(in);
+ break;
+ }
+ }
+ c = peek_next_nonws(in);
+ }
+ }
+
+ return node.release();
+}
+
+value_expr_t * parse_logic_expr(std::istream& in)
+{
+ std::auto_ptr<value_expr_t> node;
+
+ if (peek_next_nonws(in) == '!') {
+ char c;
+ in.get(c);
+ node.reset(new value_expr_t(value_expr_t::O_NOT));
+ node->left = parse_logic_expr(in);
+ return node.release();
+ }
+
+ node.reset(parse_add_expr(in));
+
+ if (node.get() && ! in.eof()) {
+ char c = peek_next_nonws(in);
+ if (c == '=' || c == '<' || c == '>') {
+ in.get(c);
+ switch (c) {
+ case '=': {
+ std::auto_ptr<value_expr_t> prev(node.release());
+ node.reset(new value_expr_t(value_expr_t::O_EQ));
+ node->left = prev.release();
+ node->right = parse_add_expr(in);
+ break;
+ }
+
+ case '<': {
+ std::auto_ptr<value_expr_t> 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->left = prev.release();
+ node->right = parse_add_expr(in);
+ break;
+ }
+
+ case '>': {
+ std::auto_ptr<value_expr_t> 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->left = prev.release();
+ node->right = parse_add_expr(in);
+ break;
+ }
+
+ default:
+ if (! in.eof())
+ unexpected(c);
+ break;
+ }
+ }
+ }
+
+ return node.release();
+}
+
+value_expr_t * parse_value_expr(std::istream& in, const bool partial)
+{
+ std::auto_ptr<value_expr_t> node(parse_logic_expr(in));
+
+ if (node.get() && ! in.eof()) {
+ char c = peek_next_nonws(in);
+ while (c == '&' || c == '|' || c == '?') {
+ in.get(c);
+ switch (c) {
+ case '&': {
+ std::auto_ptr<value_expr_t> prev(node.release());
+ node.reset(new value_expr_t(value_expr_t::O_AND));
+ node->left = prev.release();
+ node->right = parse_logic_expr(in);
+ break;
+ }
+
+ case '|': {
+ std::auto_ptr<value_expr_t> prev(node.release());
+ node.reset(new value_expr_t(value_expr_t::O_OR));
+ node->left = prev.release();
+ node->right = parse_logic_expr(in);
+ break;
+ }
+
+ case '?': {
+ std::auto_ptr<value_expr_t> prev(node.release());
+ node.reset(new value_expr_t(value_expr_t::O_QUES));
+ node->left = prev.release();
+ value_expr_t * choices;
+ node->right = choices = new value_expr_t(value_expr_t::O_COL);
+ choices->left = parse_logic_expr(in);
+ c = peek_next_nonws(in);
+ if (c != ':')
+ unexpected(c, ':');
+ in.get(c);
+ choices->right = parse_logic_expr(in);
+ break;
+ }
+
+ default:
+ if (! in.eof())
+ unexpected(c);
+ break;
+ }
+ c = peek_next_nonws(in);
+ }
+ }
+
+ char c;
+ if (! node.get()) {
+ in.get(c);
+ if (in.eof())
+ throw value_expr_error(std::string("Failed to parse value expression"));
+ else
+ unexpected(c);
+ } else if (! partial) {
+ in.get(c);
+ if (! in.eof())
+ unexpected(c);
+ else
+ in.unget();
+ }
+
+ return node.release();
+}
+
+#ifdef DEBUG_ENABLED
+
+void dump_value_expr(std::ostream& out, const value_expr_t * node)
+{
+ switch (node->kind) {
+ case value_expr_t::CONSTANT_I:
+ out << "UINT[" << node->constant_i << ']';
+ break;
+ case value_expr_t::CONSTANT_T:
+ out << "DATE/TIME[" << node->constant_t << ']';
+ break;
+ case value_expr_t::CONSTANT_A:
+ out << "CONST[" << node->constant_a << ']';
+ break;
+
+ case value_expr_t::AMOUNT: out << "AMOUNT"; break;
+ case value_expr_t::COST: out << "COST"; break;
+ case value_expr_t::DATE: out << "DATE"; break;
+ case value_expr_t::CLEARED: out << "CLEARED"; 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::COST_TOTAL: out << "COST_TOTAL"; break;
+
+ case value_expr_t::F_ARITH_MEAN:
+ out << "MEAN(";
+ dump_value_expr(out, node->left);
+ out << ')';
+ break;
+
+ case value_expr_t::F_NEG:
+ out << "ABS(";
+ dump_value_expr(out, node->left);
+ out << ')';
+ break;
+
+ case value_expr_t::F_ABS:
+ out << "ABS(";
+ dump_value_expr(out, node->left);
+ out << ')';
+ break;
+
+ case value_expr_t::F_STRIP:
+ out << "STRIP(";
+ dump_value_expr(out, node->left);
+ out << ')';
+ break;
+
+ case value_expr_t::F_PAYEE_MASK:
+ assert(node->mask);
+ out << "P_MASK(" << node->mask->pattern << ')';
+ break;
+
+ case value_expr_t::F_ACCOUNT_MASK:
+ assert(node->mask);
+ out << "A_MASK(" << node->mask->pattern << ')';
+ break;
+
+ case value_expr_t::F_SHORT_ACCOUNT_MASK:
+ assert(node->mask);
+ out << "A_SMASK(" << node->mask->pattern << ')';
+ break;
+
+ case value_expr_t::F_VALUE:
+ out << "VALUE(";
+ dump_value_expr(out, node->left);
+ if (node->right) {
+ out << ", ";
+ dump_value_expr(out, node->right);
+ }
+ out << ')';
+ break;
+
+ case value_expr_t::F_INTERP_FUNC:
+ out << "F_INTERP[" << node->constant_s << "](";
+ dump_value_expr(out, node->right);
+ out << ')';
+ break;
+
+ case value_expr_t::O_NOT:
+ out << '!';
+ dump_value_expr(out, node->left);
+ break;
+
+ case value_expr_t::O_ARG:
+ dump_value_expr(out, node->left);
+ if (node->right) {
+ out << ',';
+ dump_value_expr(out, node->right);
+ }
+ break;
+
+ case value_expr_t::O_QUES:
+ dump_value_expr(out, node->left);
+ out << '?';
+ dump_value_expr(out, node->right->left);
+ out << ':';
+ dump_value_expr(out, node->right->right);
+ break;
+
+ case value_expr_t::O_AND:
+ case value_expr_t::O_OR:
+ out << '(';
+ dump_value_expr(out, node->left);
+ switch (node->kind) {
+ case value_expr_t::O_AND: out << " & "; break;
+ case value_expr_t::O_OR: out << " | "; break;
+ default: assert(0); break;
+ }
+ dump_value_expr(out, node->right);
+ out << ')';
+ break;
+
+ case value_expr_t::O_EQ:
+ case value_expr_t::O_LT:
+ case value_expr_t::O_LTE:
+ case value_expr_t::O_GT:
+ case value_expr_t::O_GTE:
+ out << '(';
+ dump_value_expr(out, node->left);
+ switch (node->kind) {
+ case value_expr_t::O_EQ: out << '='; break;
+ case value_expr_t::O_LT: out << '<'; break;
+ case value_expr_t::O_LTE: out << "<="; break;
+ case value_expr_t::O_GT: out << '>'; break;
+ case value_expr_t::O_GTE: out << ">="; break;
+ default: assert(0); break;
+ }
+ dump_value_expr(out, node->right);
+ out << ')';
+ break;
+
+ case value_expr_t::O_ADD:
+ case value_expr_t::O_SUB:
+ case value_expr_t::O_MUL:
+ case value_expr_t::O_DIV:
+ out << '(';
+ dump_value_expr(out, node->left);
+ switch (node->kind) {
+ case value_expr_t::O_ADD: out << '+'; break;
+ case value_expr_t::O_SUB: out << '-'; break;
+ case value_expr_t::O_MUL: out << '*'; break;
+ case value_expr_t::O_DIV: out << '/'; break;
+ default: assert(0); break;
+ }
+ dump_value_expr(out, node->right);
+ out << ')';
+ break;
+
+ case value_expr_t::LAST:
+ default:
+ assert(0);
+ break;
+ }
+}
+
+#endif // DEBUG_ENABLED
+
+} // namespace ledger
+
+#ifdef USE_BOOST_PYTHON
+
+#include <boost/python.hpp>
+
+using namespace boost::python;
+using namespace ledger;
+
+value_t py_compute_1(value_expr_t& value_expr, const details_t& item)
+{
+ value_t result;
+ value_expr.compute(result, item);
+ return result;
+}
+
+template <typename T>
+value_t py_compute(value_expr_t& value_expr, const T& item)
+{
+ value_t result;
+ value_expr.compute(result, details_t(item));
+ return result;
+}
+
+value_expr_t * py_parse_value_expr_1(const std::string& str)
+{
+ return parse_value_expr(str);
+}
+
+value_expr_t * py_parse_value_expr_2(const std::string& str, const bool partial)
+{
+ return parse_value_expr(str, partial);
+}
+
+#define EXC_TRANSLATOR(type) \
+ void exc_translate_ ## type(const type& err) { \
+ PyErr_SetString(PyExc_RuntimeError, err.what()); \
+ }
+
+EXC_TRANSLATOR(value_expr_error)
+EXC_TRANSLATOR(compute_error)
+EXC_TRANSLATOR(mask_error)
+
+void export_valexpr()
+{
+ class_< details_t > ("Details", init<const entry_t&>())
+ .def(init<const transaction_t&>())
+ .def(init<const account_t&>())
+ .add_property("entry",
+ make_getter(&details_t::entry,
+ return_value_policy<reference_existing_object>()))
+ .add_property("xact",
+ make_getter(&details_t::xact,
+ return_value_policy<reference_existing_object>()))
+ .add_property("account",
+ make_getter(&details_t::account,
+ return_value_policy<reference_existing_object>()))
+ ;
+
+ class_< value_expr_t > ("ValueExpr", init<value_expr_t::kind_t>())
+ .def("compute", py_compute_1)
+ .def("compute", py_compute<account_t>)
+ .def("compute", py_compute<entry_t>)
+ .def("compute", py_compute<transaction_t>)
+ ;
+
+ def("parse_value_expr", py_parse_value_expr_1,
+ return_value_policy<manage_new_object>());
+ def("parse_value_expr", py_parse_value_expr_2,
+ return_value_policy<manage_new_object>());
+
+ class_< item_predicate<transaction_t> >
+ ("TransactionPredicate", init<std::string>())
+ .def("__call__", &item_predicate<transaction_t>::operator())
+ ;
+
+ class_< item_predicate<account_t> >
+ ("AccountPredicate", init<std::string>())
+ .def("__call__", &item_predicate<account_t>::operator())
+ ;
+
+#define EXC_TRANSLATE(type) \
+ register_exception_translator<type>(&exc_translate_ ## type);
+
+ EXC_TRANSLATE(value_expr_error);
+ EXC_TRANSLATE(compute_error);
+ EXC_TRANSLATE(mask_error);
+}
+
+#endif // USE_BOOST_PYTHON
+
+#ifdef TEST
+
+int main(int argc, char *argv[])
+{
+ ledger::dump_value_expr(std::cout, ledger::parse_value_expr(argv[1]));
+ std::cout << std::endl;
+}
+
+#endif // TEST