summaryrefslogtreecommitdiff
path: root/valexpr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'valexpr.cc')
-rw-r--r--valexpr.cc1164
1 files changed, 746 insertions, 418 deletions
diff --git a/valexpr.cc b/valexpr.cc
index 00fce46f..2b8f106e 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -7,9 +7,10 @@
namespace ledger {
-std::auto_ptr<value_expr_t> amount_expr;
-std::auto_ptr<value_expr_t> total_expr;
+std::auto_ptr<value_calc> amount_expr;
+std::auto_ptr<value_calc> total_expr;
+std::auto_ptr<scope_t> global_scope;
std::time_t terminus;
details_t::details_t(const transaction_t& _xact)
@@ -40,7 +41,103 @@ bool compute_amount(value_expr_t * expr, amount_t& amt, transaction_t& xact)
return true;
}
-void value_expr_t::compute(value_t& result, const details_t& details) const
+value_expr_t::~value_expr_t()
+{
+ DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr_t");
+
+ DEBUG_PRINT("ledger.valexpr.memory", "Destroying " << this);
+ assert(refc == 0);
+
+ if (left)
+ left->release();
+
+ switch (kind) {
+ case F_CODE_MASK:
+ case F_PAYEE_MASK:
+ case F_NOTE_MASK:
+ case F_ACCOUNT_MASK:
+ case F_SHORT_ACCOUNT_MASK:
+ case F_COMMODITY_MASK:
+ assert(mask);
+ delete mask;
+ break;
+
+ case CONSTANT_A:
+ assert(constant_a);
+ delete constant_a;
+ break;
+
+ case CONSTANT_I:
+ case CONSTANT_T:
+ case CONSTANT_V:
+ break;
+
+ default:
+ if (kind > TERMINALS && right)
+ right->release();
+ break;
+ }
+}
+
+namespace {
+ int count_leaves(value_expr_t * expr)
+ {
+ int count = 0;
+ if (expr->kind != value_expr_t::O_COM) {
+ count = 1;
+ } else {
+ count += count_leaves(expr->left);
+ count += count_leaves(expr->right);
+ }
+ return count;
+ }
+
+ value_expr_t * reduce_leaves(value_expr_t * expr, const details_t& details,
+ value_expr_t * context)
+ {
+ if (expr == NULL)
+ return NULL;
+
+ value_auto_ptr 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);
+ }
+ } else {
+ temp.reset(new value_expr_t(value_expr_t::O_COM));
+ temp->set_left(reduce_leaves(expr->left, details, context));
+ temp->set_right(reduce_leaves(expr->right, details, context));
+ }
+ return temp.release();
+ }
+
+ value_expr_t * find_leaf(value_expr_t * context, int goal, int& found)
+ {
+ if (context == NULL)
+ return NULL;
+
+ if (context->kind != value_expr_t::O_COM) {
+ if (goal == found++)
+ return context;
+ } else {
+ value_expr_t * expr = find_leaf(context->left, goal, found);
+ if (expr)
+ return expr;
+ expr = find_leaf(context->right, goal, found);
+ if (expr)
+ return expr;
+ }
+ return NULL;
+ }
+}
+
+void value_expr_t::compute(value_t& result, const details_t& details,
+ value_expr_t * context) const
{
switch (kind) {
case CONSTANT_I:
@@ -49,18 +146,25 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
case CONSTANT_T:
result = long(constant_t);
break;
-
case CONSTANT_A:
- result = constant_a;
+ result = *constant_a;
+ break;
+ case CONSTANT_V:
+ result = *constant_v;
+ break;
+
+ case F_NOW:
+ result = long(terminus);
break;
case AMOUNT:
+ case PRICE:
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;
+ result = translate_amount(details.xact->amount);
}
else if (details.account && account_has_xdata(*details.account)) {
result = account_xdata(*details.account).value;
@@ -68,6 +172,8 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
else {
result = 0L;
}
+ if (kind == PRICE)
+ result = result.factor_price();
break;
case COST:
@@ -89,7 +195,7 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
if (details.xact->cost)
result = *details.xact->cost;
else
- result = details.xact->amount;
+ result = translate_amount(details.xact->amount);
}
}
else if (details.account && account_has_xdata(*details.account)) {
@@ -101,12 +207,15 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
break;
case TOTAL:
+ case PRICE_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;
+ if (kind == PRICE_TOTAL)
+ result = result.factor_price();
break;
case COST_TOTAL:
if (details.xact && transaction_has_xdata(*details.xact))
@@ -119,21 +228,17 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
case VALUE_EXPR:
if (amount_expr.get())
- amount_expr->compute(result, details);
+ amount_expr->compute(result, details, context);
else
result = 0L;
break;
case TOTAL_EXPR:
if (total_expr.get())
- total_expr->compute(result, details);
+ total_expr->compute(result, details, context);
else
result = 0L;
break;
- case F_NOW:
- result = long(terminus);
- break;
-
case DATE:
if (details.xact && transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).date)
@@ -198,43 +303,72 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
result = 0L;
break;
- case F_ARITH_MEAN:
+ case F_ARITH_MEAN: {
+ int index = 0;
+ value_expr_t * expr = find_leaf(context, 0, index);
if (details.xact && transaction_has_xdata(*details.xact)) {
- assert(left);
- left->compute(result, details);
+ expr->compute(result, details, context);
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);
+ expr->compute(result, details, context);
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));
+ left->compute(result, details_t(*details.account->parent), context);
break;
- case F_NEG:
- assert(left);
- left->compute(result, details);
- result.negate();
+ case F_ABS: {
+ int index = 0;
+ value_expr_t * expr = find_leaf(context, 0, index);
+ expr->compute(result, details, context);
+ result.abs();
break;
+ }
- case F_ABS:
- assert(left);
- left->compute(result, details);
- result.abs();
+ case F_COMMODITY: {
+ int index = 0;
+ value_expr_t * expr = find_leaf(context, 0, index);
+ expr->compute(result, details, context);
+ if (result.type != value_t::AMOUNT)
+ throw compute_error("Argument to commodity() must be a commoditized amount");
+ amount_t temp("1");
+ temp.set_commodity(((amount_t *) result.data)->commodity());
+ result = temp;
break;
+ }
- case F_STRIP: {
- assert(left);
- left->compute(result, details);
+ case F_SET_COMMODITY: {
+ int index = 0;
+ value_expr_t * expr = find_leaf(context, 0, index);
+ value_t temp;
+ expr->compute(temp, details, context);
+
+ index = 0;
+ expr = find_leaf(context, 1, index);
+ expr->compute(result, details, context);
+ if (result.type != value_t::AMOUNT)
+ throw compute_error("Second argument to set_commodity() must be a commoditized amount");
+ amount_t one("1");
+ one.set_commodity(((amount_t *) result.data)->commodity());
+ result = one;
+
+ result *= temp;
+ break;
+ }
+
+ case F_QUANTITY: {
+ int index = 0;
+ value_expr_t * expr = find_leaf(context, 0, index);
+ expr->compute(result, details, context);
balance_t * bal = NULL;
switch (result.type) {
@@ -320,82 +454,65 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
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 F_NOW:
- break; // already set to now
- 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)");
- }
- }
+ case O_ARG: {
+ int index = 0;
+ assert(left);
+ assert(left->kind == CONSTANT_I);
+ value_expr_t * expr = find_leaf(context, left->constant_i, index);
+ if (expr)
+ expr->compute(result, details, context);
+ else
+ result = 0L;
+ break;
+ }
- result = result.value(moment);
+ case O_COM:
+ assert(left);
+ assert(right);
+ left->compute(result, details, context);
+ right->compute(result, details, context);
+ break;
+
+ case O_DEF:
+ throw compute_error("Cannot compute function definition");
+
+ case O_REF: {
+ assert(left);
+ if (right) {
+ value_auto_ptr args(reduce_leaves(right, details, context));
+ left->compute(result, details, args.get());
+ } else {
+ left->compute(result, details, context);
}
break;
}
case F_VALUE: {
- assert(left);
- left->compute(result, details);
+ int index = 0;
+ value_expr_t * expr = find_leaf(context, 0, index);
+ expr->compute(result, details, context);
+
+ index = 0;
+ expr = find_leaf(context, 1, index);
std::time_t moment = terminus;
- if (right) {
- switch (right->kind) {
- case F_NOW:
- break; // already set to now
- 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)");
- }
+ switch (expr->kind) {
+ case F_NOW:
+ break; // already set to now
+ 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 = expr->constant_t;
+ break;
+ default:
+ throw compute_error("Invalid date passed to P(value,date)");
}
result = result.value(moment);
@@ -403,7 +520,7 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
}
case O_NOT:
- left->compute(result, details);
+ left->compute(result, details, context);
result.negate();
break;
@@ -411,30 +528,31 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
assert(left);
assert(right);
assert(right->kind == O_COL);
- left->compute(result, details);
+ left->compute(result, details, context);
if (result)
- right->left->compute(result, details);
+ right->left->compute(result, details, context);
else
- right->right->compute(result, details);
+ right->right->compute(result, details, context);
break;
}
case O_AND:
assert(left);
assert(right);
- left->compute(result, details);
+ left->compute(result, details, context);
if (result)
- right->compute(result, details);
+ right->compute(result, details, context);
break;
case O_OR:
assert(left);
assert(right);
- left->compute(result, details);
+ left->compute(result, details, context);
if (! result)
- right->compute(result, details);
+ right->compute(result, details, context);
break;
+ case O_NEQ:
case O_EQ:
case O_LT:
case O_LTE:
@@ -443,9 +561,10 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
assert(left);
assert(right);
value_t temp;
- left->compute(temp, details);
- right->compute(result, details);
+ left->compute(temp, details, context);
+ right->compute(result, details, context);
switch (kind) {
+ case O_NEQ: result = temp != result; break;
case O_EQ: result = temp == result; break;
case O_LT: result = temp < result; break;
case O_LTE: result = temp <= result; break;
@@ -456,6 +575,12 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
break;
}
+ case O_NEG:
+ assert(left);
+ left->compute(result, details, context);
+ result.negate();
+ break;
+
case O_ADD:
case O_SUB:
case O_MUL:
@@ -463,8 +588,8 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
assert(left);
assert(right);
value_t temp;
- right->compute(temp, details);
- left->compute(result, details);
+ right->compute(temp, details, context);
+ left->compute(result, details, context);
switch (kind) {
case O_ADD: result += temp; break;
case O_SUB: result -= temp; break;
@@ -475,6 +600,15 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
break;
}
+ case O_PERC: {
+ assert(left);
+ result = "100.0%";
+ value_t temp;
+ left->compute(temp, details, context);
+ result *= temp;
+ break;
+ }
+
case LAST:
default:
assert(0);
@@ -497,107 +631,171 @@ static inline void unexpected(char c, char wanted = '\0') {
}
}
-value_expr_t * parse_value_term(std::istream& in);
+value_expr_t * parse_value_term(std::istream& in, scope_t * scope);
-inline value_expr_t * parse_value_term(const char * p) {
+inline value_expr_t * parse_value_term(const char * p, scope_t * scope) {
std::istringstream stream(p);
- return parse_value_term(stream);
+ return parse_value_term(stream, scope);
}
-value_expr_t * parse_value_term(std::istream& in)
+value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
{
- std::auto_ptr<value_expr_t> node;
+ value_auto_ptr node;
char buf[256];
char c = peek_next_nonws(in);
- if (std::isdigit(c)) {
- READ_INTO(in, buf, 255, c, std::isdigit(c));
+ if (std::isdigit(c) || c == '.') {
+ READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.');
- node.reset(new value_expr_t(value_expr_t::CONSTANT_I));
- node->constant_i = std::atol(buf);
- return node.release();
+ if (std::strchr(buf, '.')) {
+ node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
+ node->constant_a = new amount_t(buf);
+ } else {
+ node.reset(new value_expr_t(value_expr_t::CONSTANT_I));
+ node->constant_i = std::atol(buf);
+ }
+ goto parsed;
}
- else if (c == '{') {
- in.get(c);
- READ_INTO(in, buf, 255, c, c != '}');
- if (c == '}')
+ else if (std::isalnum(c) || c == '_') {
+ bool have_args = false;
+ istream_pos_type beg;
+
+ READ_INTO(in, buf, 255, c, std::isalnum(c) || c == '_');
+ c = peek_next_nonws(in);
+ if (c == '(') {
in.get(c);
- else
- unexpected(c, '}');
+ beg = in.tellg();
- node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
- node->constant_a.parse(buf);
- return node.release();
- }
+ int paren_depth = 0;
+ while (! in.eof()) {
+ in.get(c);
+ if (c == '(' || c == '{' || c == '[')
+ paren_depth++;
+ else if (c == ')' || c == '}' || c == ']') {
+ if (paren_depth == 0)
+ break;
+ paren_depth--;
+ }
+ }
+ if (c != ')')
+ unexpected(c, ')');
- in.get(c);
- switch (c) {
- // Basic terms
- case 'm': node.reset(new value_expr_t(value_expr_t::F_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 '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;
+ have_args = true;
+ c = peek_next_nonws(in);
+ }
- // 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;
+ if (c == '=') {
+ in.get(c);
+ if (peek_next_nonws(in) == '=') {
+ in.unget();
+ c = '\0';
+ goto parsed; // parse this as == operator
+ }
- // 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;
+ std::auto_ptr<scope_t> params(new scope_t(scope));
- // Functions
- case '^':
- node.reset(new value_expr_t(value_expr_t::F_PARENT));
- node->left = parse_value_term(in);
- break;
+ int index = 0;
+ if (have_args) {
+ bool done = false;
- case '-':
- node.reset(new value_expr_t(value_expr_t::F_NEG));
- node->left = parse_value_term(in);
- break;
+ in.clear();
+ in.seekg(beg, std::ios::beg);
+ while (! done && ! in.eof()) {
+ char ident[32];
+ READ_INTO(in, ident, 31, c, std::isalnum(c) || c == '_');
- case 'U':
- node.reset(new value_expr_t(value_expr_t::F_ABS));
- node->left = parse_value_term(in);
- break;
+ c = peek_next_nonws(in);
+ in.get(c);
+ if (c != ',' && c != ')')
+ unexpected(c, ')');
+ else if (c == ')')
+ done = true;
+
+ // 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++;
+ params->define(ident, node.release());
+ }
+
+ if (peek_next_nonws(in) != '=') {
+ in.get(c);
+ unexpected(c, '=');
+ }
+ in.get(c);
+ }
- case 'S':
- node.reset(new value_expr_t(value_expr_t::F_STRIP));
- node->left = parse_value_term(in);
- break;
+ // Define the value associated with the defined identifier
+ value_auto_ptr def(parse_boolean_expr(in, params.get()));
+ if (def.get() == NULL)
+ throw 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_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;
+ } else {
+ assert(scope);
+ value_expr_t * def = scope->lookup(buf);
+ if (def == NULL) {
+ if (buf[1] == '\0' &&
+ (buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' ||
+ buf[0] == 'w' || buf[0] == 'W' || buf[0] == 'e'))
+ goto find_term;
+ throw 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));
+ node->set_left(def->right);
+
+ int count = 0;
+ if (have_args) {
+ in.clear();
+ in.seekg(beg, std::ios::beg);
+ value_auto_ptr args(parse_value_expr(in, scope, true));
+
+ if (peek_next_nonws(in) != ')') {
+ in.get(c);
+ unexpected(c, ')');
+ }
+ in.get(c);
- case 'A':
- node.reset(new value_expr_t(value_expr_t::F_ARITH_MEAN));
- node->left = parse_value_term(in);
- break;
+ if (args.get() != NULL) {
+ count = count_leaves(args.get());
+ node->set_right(args.release());
+ }
+ }
- 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);
+ if (count != def->left->constant_i) {
+ std::string msg;
+ std::ostringstream errmsg(msg);
+ errmsg << "Wrong number of arguments to '" << buf
+ << "': saw " << count
+ << ", wanted " << def->left->constant_i;
+ throw value_expr_error(errmsg.str());
+ }
+ }
+ else {
+ node.reset(def);
}
- in.get(c);
- if (c != ')')
- unexpected(c, ')');
- } else {
- node->left = parse_value_term(in);
}
+ goto parsed;
+ }
+
+ find_term:
+ in.get(c);
+ switch (c) {
+ // Functions
+ case '^':
+ node.reset(new value_expr_t(value_expr_t::F_PARENT));
+ node->set_left(parse_value_term(in, scope));
break;
// Other
@@ -657,39 +855,39 @@ value_expr_t * parse_value_term(std::istream& in)
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) == ')') {
+ case '{': {
+ int paren_depth = 0;
+ int i = 0;
+ while (i < 255 && ! in.eof()) {
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 == '{') {
+ paren_depth++;
}
- if (c != ')')
- unexpected(c, ')');
+ else if (c == '}') {
+ if (paren_depth == 0)
+ break;
+ paren_depth--;
+ }
+ buf[i++] = c;
}
+ buf[i] = '\0';
+
+ if (c != '}')
+ unexpected(c, '}');
+
+ node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
+ node->constant_a = new amount_t(buf);
break;
}
- case '(':
- node.reset(parse_value_expr(in, true));
+ case '(': {
+ std::auto_ptr<scope_t> locals(new scope_t(scope));
+ node.reset(parse_value_expr(in, locals.get(), true));
in.get(c);
if (c != ')')
unexpected(c, ')');
break;
+ }
case '[': {
READ_INTO(in, buf, 255, c, c != ']');
@@ -709,12 +907,23 @@ value_expr_t * parse_value_term(std::istream& in)
break;
}
+ parsed:
return node.release();
}
-value_expr_t * parse_mul_expr(std::istream& in)
+value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope)
{
- std::auto_ptr<value_expr_t> node(parse_value_term(in));
+ value_auto_ptr 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));
+ return node.release();
+ }
+
+ node.reset(parse_value_term(in, scope));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
@@ -722,18 +931,18 @@ value_expr_t * parse_mul_expr(std::istream& in)
in.get(c);
switch (c) {
case '*': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_MUL));
- node->left = prev.release();
- node->right = parse_value_term(in);
+ node->set_left(prev.release());
+ node->set_right(parse_value_term(in, scope));
break;
}
case '/': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_DIV));
- node->left = prev.release();
- node->right = parse_value_term(in);
+ node->set_left(prev.release());
+ node->set_right(parse_value_term(in, scope));
break;
}
}
@@ -744,9 +953,28 @@ value_expr_t * parse_mul_expr(std::istream& in)
return node.release();
}
-value_expr_t * parse_add_expr(std::istream& in)
+value_expr_t * parse_add_expr(std::istream& in, scope_t * scope)
{
- std::auto_ptr<value_expr_t> node(parse_mul_expr(in));
+ value_auto_ptr 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();
+ return expr.release();
+ }
+ node.reset(new value_expr_t(value_expr_t::O_NEG));
+ node->set_left(expr.release());
+ return node.release();
+ }
+
+ node.reset(parse_mul_expr(in, scope));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
@@ -754,18 +982,18 @@ value_expr_t * parse_add_expr(std::istream& in)
in.get(c);
switch (c) {
case '+': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_ADD));
- node->left = prev.release();
- node->right = parse_mul_expr(in);
+ node->set_left(prev.release());
+ node->set_right(parse_mul_expr(in, scope));
break;
}
case '-': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_SUB));
- node->left = prev.release();
- node->right = parse_mul_expr(in);
+ node->set_left(prev.release());
+ node->set_right(parse_mul_expr(in, scope));
break;
}
}
@@ -776,54 +1004,61 @@ value_expr_t * parse_add_expr(std::istream& in)
return node.release();
}
-value_expr_t * parse_logic_expr(std::istream& in)
+value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope)
{
- std::auto_ptr<value_expr_t> node;
+ value_auto_ptr 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);
+ node->set_left(parse_add_expr(in, scope));
return node.release();
}
- node.reset(parse_add_expr(in));
+ node.reset(parse_add_expr(in, scope));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
- if (c == '=' || c == '<' || c == '>') {
+ if (c == '!' || c == '=' || c == '<' || c == '>') {
in.get(c);
switch (c) {
+ case '!':
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);
+ bool negate = c == '!';
+ if ((c = peek_next_nonws(in)) == '=')
+ in.get(c);
+ else
+ unexpected(c, '=');
+ value_auto_ptr 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));
break;
}
case '<': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr 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);
+ node->set_left(prev.release());
+ node->set_right(parse_add_expr(in, scope));
break;
}
case '>': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr 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);
+ node->set_left(prev.release());
+ node->set_right(parse_add_expr(in, scope));
break;
}
@@ -838,9 +1073,9 @@ value_expr_t * parse_logic_expr(std::istream& in)
return node.release();
}
-value_expr_t * parse_value_expr(std::istream& in, const bool partial)
+value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope)
{
- std::auto_ptr<value_expr_t> node(parse_logic_expr(in));
+ value_auto_ptr node(parse_logic_expr(in, scope));
if (node.get() && ! in.eof()) {
char c = peek_next_nonws(in);
@@ -848,33 +1083,208 @@ value_expr_t * parse_value_expr(std::istream& in, const bool partial)
in.get(c);
switch (c) {
case '&': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_AND));
- node->left = prev.release();
- node->right = parse_logic_expr(in);
+ node->set_left(prev.release());
+ node->set_right(parse_logic_expr(in, scope));
break;
}
case '|': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr prev(node.release());
node.reset(new value_expr_t(value_expr_t::O_OR));
- node->left = prev.release();
- node->right = parse_logic_expr(in);
+ node->set_left(prev.release());
+ node->set_right(parse_logic_expr(in, scope));
break;
}
case '?': {
- std::auto_ptr<value_expr_t> prev(node.release());
+ value_auto_ptr 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);
+ 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));
c = peek_next_nonws(in);
if (c != ':')
unexpected(c, ':');
in.get(c);
- choices->right = parse_logic_expr(in);
+ node->right->set_right(parse_logic_expr(in, scope));
+ break;
+ }
+
+ default:
+ if (! in.eof())
+ unexpected(c);
+ break;
+ }
+ c = peek_next_nonws(in);
+ }
+ }
+
+ return node.release();
+}
+
+void init_value_expr()
+{
+ global_scope.reset(new scope_t());
+ scope_t * globals = global_scope.get();
+
+ value_expr_t * node;
+
+ // Basic terms
+ node = new value_expr_t(value_expr_t::F_NOW);
+ globals->define("m", node);
+ globals->define("now", node);
+
+ node = new value_expr_t(value_expr_t::AMOUNT);
+ globals->define("a", node);
+ globals->define("amount", node);
+
+ node = new value_expr_t(value_expr_t::COST);
+ globals->define("b", node);
+ globals->define("cost", node);
+
+ node = new value_expr_t(value_expr_t::PRICE);
+ globals->define("i", node);
+ globals->define("price", node);
+
+ node = new value_expr_t(value_expr_t::DATE);
+ globals->define("d", node);
+ globals->define("date", node);
+
+ node = new value_expr_t(value_expr_t::CLEARED);
+ globals->define("X", node);
+ globals->define("cleared", node);
+
+ node = new value_expr_t(value_expr_t::PENDING);
+ globals->define("Y", node);
+ globals->define("pending", node);
+
+ node = new value_expr_t(value_expr_t::REAL);
+ globals->define("R", node);
+ globals->define("real", node);
+
+ node = new value_expr_t(value_expr_t::ACTUAL);
+ globals->define("L", node);
+ globals->define("actual", node);
+
+ node = new value_expr_t(value_expr_t::INDEX);
+ globals->define("n", node);
+ globals->define("index", node);
+
+ node = new value_expr_t(value_expr_t::COUNT);
+ globals->define("N", node);
+ globals->define("count", node);
+
+ node = new value_expr_t(value_expr_t::DEPTH);
+ globals->define("l", node);
+ globals->define("depth", node);
+
+ node = new value_expr_t(value_expr_t::TOTAL);
+ globals->define("O", node);
+ globals->define("total", node);
+
+ node = new value_expr_t(value_expr_t::COST_TOTAL);
+ globals->define("B", node);
+ globals->define("cost_total", node);
+
+ node = new value_expr_t(value_expr_t::PRICE_TOTAL);
+ globals->define("I", node);
+ globals->define("price_total", node);
+
+ // Relating to format_t
+ globals->define("t", new value_expr_t(value_expr_t::VALUE_EXPR));
+ globals->define("T", new value_expr_t(value_expr_t::TOTAL_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_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_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_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_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_right(new value_expr_t(value_expr_t::F_ARITH_MEAN));
+ globals->define("A", node);
+ globals->define("avg", node);
+ globals->define("mean", node);
+ 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_right(new value_expr_t(value_expr_t::F_VALUE));
+ globals->define("P", node);
+ globals->define("val", node);
+ globals->define("value", node);
+
+ // Macros
+ node = parse_value_expr("P(a,d)");
+ globals->define("v", node);
+ globals->define("market", node);
+
+ node = parse_value_expr("P(O,d)");
+ globals->define("V", node);
+ globals->define("market_total", node);
+
+ node = parse_value_expr("v-b");
+ globals->define("g", node);
+ globals->define("gain", node);
+
+ node = parse_value_expr("V-B");
+ globals->define("G", node);
+ globals->define("gain_total", node);
+
+ parse_boolean_expr("min(x,y)=x<y?x:y", globals);
+ parse_boolean_expr("max(x,y)=x>y?x:y", globals);
+}
+
+value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,
+ const bool partial)
+{
+ 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()));
+
+ if (node.get() && ! in.eof()) {
+ char c = peek_next_nonws(in);
+ while (c == ',') {
+ in.get(c);
+ switch (c) {
+ case ',': {
+ value_auto_ptr 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()));
break;
}
@@ -905,185 +1315,103 @@ value_expr_t * parse_value_expr(std::istream& in, const bool partial)
return node.release();
}
-#ifdef DEBUG_ENABLED
-
-void dump_value_expr(std::ostream& out, const value_expr_t * node)
+void dump_value_expr(std::ostream& out, const value_expr_t * node,
+ const int depth)
{
+ out.setf(std::ios::left);
+ out.width(10);
+ out << node << " ";
+
+ for (int i = 0; i < depth; i++)
+ out << " ";
+
switch (node->kind) {
case value_expr_t::CONSTANT_I:
- out << "UINT[" << node->constant_i << ']';
+ out << "CONSTANT_I - " << node->constant_i;
break;
case value_expr_t::CONSTANT_T:
- out << "DATE/TIME[" << node->constant_t << ']';
+ out << "CONSTANT_T - [" << 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;
-
+ out << "CONSTANT_A - {" << *(node->constant_a) << '}';
+ break;
+ case value_expr_t::CONSTANT_V:
+ out << "CONSTANT_V - {" << *(node->constant_v) << '}';
+ break;
+
+ case value_expr_t::AMOUNT: out << "AMOUNT"; break;
+ case value_expr_t::COST: out << "COST"; break;
+ case value_expr_t::PRICE: out << "PRICE"; 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::PRICE_TOTAL: out << "PRICE_TOTAL"; break;
+
+ case value_expr_t::F_NOW: out << "F_NOW"; break;
+ case value_expr_t::F_ARITH_MEAN: out << "F_ARITH_MEAN"; break;
+ case value_expr_t::F_ABS: out << "F_ABS"; break;
+ case value_expr_t::F_QUANTITY: out << "F_QUANTITY"; break;
+ case value_expr_t::F_COMMODITY: out << "F_COMMODITY"; break;
+ case value_expr_t::F_SET_COMMODITY: out << "F_SET_COMMODITY"; break;
+ case value_expr_t::F_CODE_MASK: out << "F_CODE_MASK"; break;
+ case value_expr_t::F_PAYEE_MASK: out << "F_PAYEE_MASK"; break;
+ case value_expr_t::F_NOTE_MASK: out << "F_NOTE_MASK"; break;
case value_expr_t::F_ACCOUNT_MASK:
- assert(node->mask);
- out << "M_ACCT(" << node->mask->pattern << ')';
- break;
-
+ out << "F_ACCOUNT_MASK"; break;
case value_expr_t::F_SHORT_ACCOUNT_MASK:
- assert(node->mask);
- out << "M_SACCT(" << node->mask->pattern << ')';
- break;
-
+ out << "F_SHORT_ACCOUNT_MASK"; 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;
+ out << "F_COMMODITY_MASK"; break;
+ case value_expr_t::F_VALUE: out << "F_VALUE"; break;
+
+ case value_expr_t::O_NOT: out << "O_NOT"; break;
+ case value_expr_t::O_ARG: out << "O_ARG"; break;
+ case value_expr_t::O_DEF: out << "O_DEF"; break;
+ case value_expr_t::O_REF: out << "O_REF"; break;
+ case value_expr_t::O_COM: out << "O_COM"; break;
+ case value_expr_t::O_QUES: out << "O_QUES"; break;
+ case value_expr_t::O_COL: out << "O_COL"; break;
+ case value_expr_t::O_AND: out << "O_AND"; break;
+ case value_expr_t::O_OR: out << "O_OR"; break;
+ case value_expr_t::O_NEQ: out << "O_NEQ"; break;
+ case value_expr_t::O_EQ: out << "O_EQ"; break;
+ case value_expr_t::O_LT: out << "O_LT"; break;
+ case value_expr_t::O_LTE: out << "O_LTE"; break;
+ case value_expr_t::O_GT: out << "O_GT"; break;
+ case value_expr_t::O_GTE: out << "O_GTE"; break;
+ case value_expr_t::O_NEG: out << "O_NEG"; break;
+ case value_expr_t::O_ADD: out << "O_ADD"; break;
+ case value_expr_t::O_SUB: out << "O_SUB"; break;
+ case value_expr_t::O_MUL: out << "O_MUL"; break;
+ case value_expr_t::O_DIV: out << "O_DIV"; break;
+ case value_expr_t::O_PERC: out << "O_PERC"; break;
case value_expr_t::LAST:
default:
assert(0);
break;
}
-}
-#endif // DEBUG_ENABLED
+ out << " (" << node->refc << ')' << std::endl;
-} // 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;
+ if (node->kind > value_expr_t::TERMINALS) {
+ if (node->left) {
+ dump_value_expr(out, node->left, depth + 1);
+ if (node->right)
+ dump_value_expr(out, node->right, depth + 1);
+ } else {
+ assert(node->right == NULL);
+ }
+ } else {
+ assert(node->left == NULL);
+ }
}
-#endif // TEST
+} // namespace ledger