diff options
Diffstat (limited to 'valexpr.cc')
-rw-r--r-- | valexpr.cc | 1164 |
1 files changed, 746 insertions, 418 deletions
@@ -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 |