1085 lines
26 KiB
C++
1085 lines
26 KiB
C++
#include "valexpr.h"
|
|
#include "walk.h"
|
|
#include "error.h"
|
|
#include "datetime.h"
|
|
#include "debug.h"
|
|
#include "util.h"
|
|
|
|
namespace ledger {
|
|
|
|
std::auto_ptr<value_expr_t> amount_expr;
|
|
std::auto_ptr<value_expr_t> total_expr;
|
|
|
|
std::time_t terminus;
|
|
|
|
details_t::details_t(const transaction_t& _xact)
|
|
: entry(_xact.entry), xact(&_xact), account(xact_account(_xact))
|
|
{
|
|
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
|
|
}
|
|
|
|
bool compute_amount(value_expr_t * expr, amount_t& amt, transaction_t& xact)
|
|
{
|
|
value_t result;
|
|
expr->compute(result, details_t(xact));
|
|
switch (result.type) {
|
|
case value_t::BOOLEAN:
|
|
amt = *((bool *) result.data);
|
|
break;
|
|
case value_t::INTEGER:
|
|
amt = *((long *) result.data);
|
|
break;
|
|
case value_t::AMOUNT:
|
|
amt = *((amount_t *) result.data);
|
|
break;
|
|
|
|
case value_t::BALANCE:
|
|
case value_t::BALANCE_PAIR:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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 PENDING:
|
|
if (details.xact)
|
|
result = details.xact->state == transaction_t::PENDING;
|
|
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_FUNC: {
|
|
if (constant_s == "min" || constant_s == "max") {
|
|
assert(left);
|
|
if (! right) {
|
|
left->compute(result, details);
|
|
break;
|
|
}
|
|
value_t temp;
|
|
left->compute(temp, details);
|
|
assert(right->kind == O_ARG);
|
|
right->left->compute(result, details);
|
|
|
|
if (constant_s == "min") {
|
|
if (temp < result)
|
|
result = temp;
|
|
} else {
|
|
if (temp > result)
|
|
result = temp;
|
|
}
|
|
}
|
|
else if (constant_s == "price") {
|
|
assert(left);
|
|
left->compute(result, details);
|
|
|
|
std::time_t moment = terminus;
|
|
if (right) {
|
|
assert(right->kind == O_ARG);
|
|
switch (right->left->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->left->constant_t;
|
|
break;
|
|
default:
|
|
throw compute_error("Invalid date passed to @price(value,date)");
|
|
}
|
|
}
|
|
|
|
result = result.value(moment);
|
|
}
|
|
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 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 'Y': node.reset(new value_expr_t(value_expr_t::PENDING)); 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_FUNC));
|
|
node->constant_s = buf;
|
|
|
|
in.get(c);
|
|
if (peek_next_nonws(in) == ')') {
|
|
in.get(c);
|
|
} else {
|
|
value_expr_t * cur = node.get();
|
|
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::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::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::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 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
|