ledger/valexpr.cc
John Wiegley 00fe8bc815 Added a `terminus' global, which if set marks the "current time" as
seen by the value expression logic.  This has the effect of changing
valexprs that test against the current time, such as for calculating
the market value of commodities.
2008-04-13 02:41:20 -04:00

1107 lines
26 KiB
C++

#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;
std::time_t terminus = now;
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.xact)
result = long(details.xact->date());
else if (details.entry)
result = long(details.entry->date());
else
result = long(terminus);
break;
case CLEARED:
if (details.xact)
result = details.xact->state == transaction_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_CODE_MASK:
assert(mask);
if (details.entry)
result = mask->match(details.entry->code);
else
result = false;
break;
case F_PAYEE_MASK:
assert(mask);
if (details.entry)
result = mask->match(details.entry->payee);
else
result = false;
break;
case F_NOTE_MASK:
assert(mask);
if (details.xact)
result = mask->match(details.xact->note);
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_COMMODITY_MASK:
assert(mask);
if (details.xact)
result = mask->match(details.xact->amount.commodity().symbol);
else
result = false;
break;
case F_VALUE: {
assert(left);
left->compute(result, details);
std::time_t moment = terminus;
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.xact)
moment = 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 = terminus;
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 'c':
case 'C':
case 'p':
case 'w':
case 'W':
case 'e':
case '/': {
bool code_mask = c == 'c';
bool commodity_mask = c == 'C';
bool payee_mask = c == 'p';
bool note_mask = c == 'e';
bool short_account_mask = c == 'w';
if (c == '/') {
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;
}
}
} else {
in.get(c);
}
// Read in the regexp
READ_INTO(in, buf, 255, c, c != '/');
if (c != '/')
unexpected(c, '/');
value_expr_t::kind_t kind;
if (short_account_mask)
kind = value_expr_t::F_SHORT_ACCOUNT_MASK;
else if (code_mask)
kind = value_expr_t::F_CODE_MASK;
else if (commodity_mask)
kind = value_expr_t::F_COMMODITY_MASK;
else if (payee_mask)
kind = value_expr_t::F_PAYEE_MASK;
else if (note_mask)
kind = value_expr_t::F_NOTE_MASK;
else
kind = value_expr_t::F_ACCOUNT_MASK;
in.get(c);
node.reset(new value_expr_t(kind));
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_CODE_MASK:
assert(node->mask);
out << "M_CODE(" << node->mask->pattern << ')';
break;
case value_expr_t::F_PAYEE_MASK:
assert(node->mask);
out << "M_PAYEE(" << node->mask->pattern << ')';
break;
case value_expr_t::F_NOTE_MASK:
assert(node->mask);
out << "M_NOTE(" << node->mask->pattern << ')';
break;
case value_expr_t::F_ACCOUNT_MASK:
assert(node->mask);
out << "M_ACCT(" << node->mask->pattern << ')';
break;
case value_expr_t::F_SHORT_ACCOUNT_MASK:
assert(node->mask);
out << "M_SACCT(" << node->mask->pattern << ')';
break;
case value_expr_t::F_COMMODITY_MASK:
assert(node->mask);
out << "M_COMM(" << 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