diff options
Diffstat (limited to 'valexpr.cc')
-rw-r--r-- | valexpr.cc | 1805 |
1 files changed, 455 insertions, 1350 deletions
@@ -1,32 +1,66 @@ +/* + * Copyright (c) 2003-2007, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #include "valexpr.h" +#include "parsexp.h" #include "walk.h" -#include "error.h" -#include "datetime.h" -#include "debug.h" -#include "util.h" +#include "utils.h" namespace ledger { value_expr amount_expr; value_expr total_expr; -std::auto_ptr<scope_t> global_scope; +namespace expr { + +std::auto_ptr<symbol_scope_t> global_scope; datetime_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"); + DEBUG("ledger.memory.ctors", "ctor details_t"); } -bool compute_amount(value_expr_t * expr, amount_t& amt, - const transaction_t * xact, value_expr_t * context) +bool compute_amount(ptr_op_t expr, amount_t& amt, + const transaction_t * xact, ptr_op_t context) { value_t result; try { expr->compute(result, xact ? details_t(*xact) : details_t(), context); - result.cast(value_t::AMOUNT); - amt = *((amount_t *) result.data); + + // Most of the time when computing the amount of a transaction this cast + // will do nothing at all. + result.in_place_cast(value_t::AMOUNT); + amt = result.as_amount(); } catch (error * err) { if (err->context.empty() || @@ -34,7 +68,7 @@ bool compute_amount(value_expr_t * expr, amount_t& amt, err->context.push_back(new valexpr_context(expr)); error_context * last = err->context.back(); if (valexpr_context * ctxt = dynamic_cast<valexpr_context *>(last)) { - ctxt->expr = expr->acquire(); + ctxt->expr = expr; ctxt->desc = "While computing amount expression:"; } throw err; @@ -42,89 +76,87 @@ bool compute_amount(value_expr_t * expr, amount_t& amt, return true; } -value_expr_t::~value_expr_t() -{ - DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr_t " << this); - - 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; +void scope_t::define(const string& name, const value_t& val) { + define(name, op_t::wrap_value(val)); +} - case CONSTANT: - assert(value); - delete value; - break; +value_t scope_t::resolve(const string& name) { + ptr_op_t definition = lookup(name); + if (definition) + return definition->calc(*this); + else + return value_t(); +} - default: - if (kind > TERMINALS && right) - right->release(); - break; +void symbol_scope_t::define(const string& name, ptr_op_t def) +{ + DEBUG("ledger.xpath.syms", "Defining '" << name << "' = " << def); + + std::pair<symbol_map::iterator, bool> result + = symbols.insert(symbol_map::value_type(name, def)); + if (! result.second) { + symbol_map::iterator i = symbols.find(name); + assert(i != symbols.end()); + symbols.erase(i); + + std::pair<symbol_map::iterator, bool> result2 + = symbols.insert(symbol_map::value_type(name, def)); + if (! result2.second) + throw_(compile_error, + "Redefinition of '" << name << "' in same scope"); } } namespace { - int count_leaves(value_expr_t * expr) + int count_leaves(ptr_op_t expr) { int count = 0; - if (expr->kind != value_expr_t::O_COM) { + if (expr->kind != op_t::O_COMMA) { count = 1; } else { - count += count_leaves(expr->left); - count += count_leaves(expr->right); + 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) + ptr_op_t reduce_leaves(ptr_op_t expr, const details_t& details, + ptr_op_t context) { if (! expr) return NULL; value_expr temp; - if (expr->kind != value_expr_t::O_COM) { - if (expr->kind < value_expr_t::TERMINALS) { + if (expr->kind != op_t::O_COMMA) { + if (expr->kind < op_t::TERMINALS) { temp.reset(expr); } else { - temp.reset(new value_expr_t(value_expr_t::CONSTANT)); - temp->value = new value_t; - expr->compute(*(temp->value), details, context); + temp.reset(new op_t(op_t::VALUE)); + temp->set_value(value_t()); + expr->compute(temp->as_value(), 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)); + temp.reset(new op_t(op_t::O_COMMA)); + 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) + ptr_op_t find_leaf(ptr_op_t context, int goal, long& found) { if (! context) return NULL; - if (context->kind != value_expr_t::O_COM) { + if (context->kind != op_t::O_COMMA) { if (goal == found++) return context; } else { - value_expr_t * expr = find_leaf(context->left, goal, found); + ptr_op_t expr = find_leaf(context->left(), goal, found); if (expr) return expr; - expr = find_leaf(context->right, goal, found); + expr = find_leaf(context->right(), goal, found); if (expr) return expr; } @@ -132,17 +164,47 @@ namespace { } } -void value_expr_t::compute(value_t& result, const details_t& details, - value_expr_t * context) const +ptr_op_t symbol_scope_t::lookup(const string& name) +{ + switch (name[0]) { +#if 0 + case 'l': + if (name == "last") + return WRAP_FUNCTOR(bind(xpath_fn_last, _1)); + break; + + case 'p': + if (name == "position") + return WRAP_FUNCTOR(bind(xpath_fn_position, _1)); + break; + + case 't': + if (name == "text") + return WRAP_FUNCTOR(bind(xpath_fn_text, _1)); + else if (name == "type") + return WRAP_FUNCTOR(bind(xpath_fn_type, _1)); +#endif + break; + } + + symbol_map::const_iterator i = symbols.find(name); + if (i != symbols.end()) + return (*i).second; + + return child_scope_t::lookup(name); +} + + +void op_t::compute(value_t& result, const details_t& details, + ptr_op_t context) const { try { switch (kind) { case ARG_INDEX: throw new compute_error("Cannot directly compute an arg_index"); - case CONSTANT: - assert(value); - result = *value; + case VALUE: + result = as_value(); break; case F_NOW: @@ -171,15 +233,20 @@ void value_expr_t::compute(value_t& result, const details_t& details, if (transaction_has_xdata(*details.xact)) { transaction_xdata_t& xdata(transaction_xdata_(*details.xact)); if (xdata.dflags & TRANSACTION_COMPOUND) { - result = xdata.value.price(); + result = xdata.value.value(); set = true; } } - if (! set) - result = details.xact->amount.price(); + if (! set) { + optional<amount_t> value = details.xact->amount.value(); + if (value) + result = *value; + else + result = 0L; + } } else if (details.account && account_has_xdata(*details.account)) { - result = account_xdata(*details.account).value.price(); + result = account_xdata(*details.account).value.value(); } else { result = 0L; @@ -222,9 +289,9 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case PRICE_TOTAL: if (details.xact && transaction_has_xdata(*details.xact)) - result = transaction_xdata_(*details.xact).total.price(); + result = transaction_xdata_(*details.xact).total.value(); else if (details.account && account_has_xdata(*details.account)) - result = account_xdata(*details.account).total.price(); + result = account_xdata(*details.account).total.value(); else result = 0L; break; @@ -252,7 +319,7 @@ void value_expr_t::compute(value_t& result, const details_t& details, case DATE: if (details.xact && transaction_has_xdata(*details.xact) && - transaction_xdata_(*details.xact).date) + is_valid(transaction_xdata_(*details.xact).date)) result = transaction_xdata_(*details.xact).date; else if (details.xact) result = details.xact->date(); @@ -264,7 +331,7 @@ void value_expr_t::compute(value_t& result, const details_t& details, case ACT_DATE: if (details.xact && transaction_has_xdata(*details.xact) && - transaction_xdata_(*details.xact).date) + is_valid(transaction_xdata_(*details.xact).date)) result = transaction_xdata_(*details.xact).date; else if (details.xact) result = details.xact->actual_date(); @@ -276,7 +343,7 @@ void value_expr_t::compute(value_t& result, const details_t& details, case EFF_DATE: if (details.xact && transaction_has_xdata(*details.xact) && - transaction_xdata_(*details.xact).date) + is_valid(transaction_xdata_(*details.xact).date)) result = transaction_xdata_(*details.xact).date; else if (details.xact) result = details.xact->effective_date(); @@ -301,14 +368,14 @@ void value_expr_t::compute(value_t& result, const details_t& details, case REAL: if (details.xact) - result = ! (details.xact->flags & TRANSACTION_VIRTUAL); + result = ! (details.xact->has_flags(TRANSACTION_VIRTUAL)); else result = true; break; case ACTUAL: if (details.xact) - result = ! (details.xact->flags & TRANSACTION_AUTO); + result = ! (details.xact->has_flags(TRANSACTION_AUTO)); else result = true; break; @@ -339,26 +406,26 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case F_PRICE: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - result = result.price(); + result = result.value(); break; } case F_DATE: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - result = result.date(); + result = result.as_datetime_lval(); break; } case F_DATECMP: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - result = result.date(); + result = result.as_datetime_lval(); if (! result) break; @@ -366,7 +433,7 @@ void value_expr_t::compute(value_t& result, const details_t& details, expr = find_leaf(context, 1, arg_index); value_t moment; expr->compute(moment, details, context); - if (moment.type == value_t::DATETIME) { + if (moment.is_type(value_t::DATETIME)) { result.cast(value_t::INTEGER); moment.cast(value_t::INTEGER); result -= moment; @@ -380,32 +447,34 @@ void value_expr_t::compute(value_t& result, const details_t& details, case F_YEAR: case F_MONTH: case F_DAY: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - if (result.type != value_t::DATETIME) + if (! result.is_type(value_t::DATETIME)) throw new compute_error("Invalid date passed to year|month|day(date)", new valexpr_context(expr)); - datetime_t& moment(*((datetime_t *)result.data)); + datetime_t& moment(result.as_datetime_lval()); switch (kind) { case F_YEAR: - result = (long)moment.year(); + result = (long)moment.date().year(); break; case F_MONTH: - result = (long)moment.month(); + result = (long)moment.date().month(); break; case F_DAY: - result = (long)moment.day(); + result = (long)moment.date().day(); + break; + default: break; } break; } case F_ARITH_MEAN: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); if (details.xact && transaction_has_xdata(*details.xact)) { expr->compute(result, details, context); result /= amount_t(long(transaction_xdata_(*details.xact).index + 1)); @@ -423,53 +492,53 @@ void value_expr_t::compute(value_t& result, const details_t& details, case F_PARENT: if (details.account && details.account->parent) - left->compute(result, details_t(*details.account->parent), context); + left()->compute(result, details_t(*details.account->parent), context); break; case F_ABS: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); result.abs(); break; } case F_ROUND: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); result.round(); break; } case F_COMMODITY: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - if (result.type != value_t::AMOUNT) + if (! result.is_type(value_t::AMOUNT)) throw new compute_error("Argument to commodity() must be a commoditized amount", new valexpr_context(expr)); amount_t temp("1"); - temp.set_commodity(((amount_t *) result.data)->commodity()); + temp.set_commodity(result.as_amount_lval().commodity()); result = temp; break; } case F_SET_COMMODITY: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); value_t temp; expr->compute(temp, details, context); arg_index = 0; expr = find_leaf(context, 1, arg_index); expr->compute(result, details, context); - if (result.type != value_t::AMOUNT) + if (! result.is_type(value_t::AMOUNT)) throw new compute_error ("Second argument to set_commodity() must be a commoditized amount", new valexpr_context(expr)); amount_t one("1"); - one.set_commodity(((amount_t *) result.data)->commodity()); + one.set_commodity(result.as_amount_lval().commodity()); result = one; result *= temp; @@ -477,25 +546,25 @@ void value_expr_t::compute(value_t& result, const details_t& details, } case F_QUANTITY: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); balance_t * bal = NULL; - switch (result.type) { + switch (result.type()) { case value_t::BALANCE_PAIR: - bal = &((balance_pair_t *) result.data)->quantity; + bal = &(result.as_balance_pair_lval().quantity()); // fall through... case value_t::BALANCE: if (! bal) - bal = (balance_t *) result.data; + bal = &(result.as_balance_lval()); if (bal->amounts.size() < 2) { result.cast(value_t::AMOUNT); } else { value_t temp; - for (amounts_map::const_iterator i = bal->amounts.begin(); + for (balance_t::amounts_map::const_iterator i = bal->amounts.begin(); i != bal->amounts.end(); i++) { amount_t x = (*i).second; @@ -503,12 +572,12 @@ void value_expr_t::compute(value_t& result, const details_t& details, temp += x; } result = temp; - assert(temp.type == value_t::AMOUNT); + assert(temp.is_type(value_t::AMOUNT)); } // fall through... case value_t::AMOUNT: - ((amount_t *) result.data)->clear_commodity(); + result.as_amount_lval().clear_commodity(); break; default: @@ -517,6 +586,7 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; } +#if 0 case F_CODE_MASK: assert(mask); if (details.entry) @@ -564,12 +634,12 @@ void value_expr_t::compute(value_t& result, const details_t& details, else result = false; break; +#endif case O_ARG: { - int arg_index = 0; - assert(left); - assert(left->kind == ARG_INDEX); - value_expr_t * expr = find_leaf(context, left->arg_index, arg_index); + long arg_index = 0; + assert(left()->kind == ARG_INDEX); + ptr_op_t expr = find_leaf(context, left()->as_long(), arg_index); if (expr) expr->compute(result, details, context); else @@ -577,15 +647,15 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; } - case O_COM: - if (! left) + case O_COMMA: + if (! left()) throw new compute_error("Comma operator missing left operand", - new valexpr_context(this)); - if (! right) + new valexpr_context(const_cast<op_t *>(this))); + if (! right()) throw new compute_error("Comma operator missing right operand", - new valexpr_context(this)); - left->compute(result, details, context); - right->compute(result, details, context); + new valexpr_context(const_cast<op_t *>(this))); + left()->compute(result, details, context); + right()->compute(result, details, context); break; case O_DEF: @@ -593,35 +663,35 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case O_REF: { - assert(left); - if (right) { - value_expr args(reduce_leaves(right, details, context)); - left->compute(result, details, args.get()); + assert(left()); + if (right()) { + value_expr args(reduce_leaves(right(), details, context)); + left()->compute(result, details, args.get()); } else { - left->compute(result, details, context); + left()->compute(result, details, context); } break; } case F_VALUE: { - int arg_index = 0; - value_expr_t * expr = find_leaf(context, 0, arg_index); + long arg_index = 0; + ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); arg_index = 0; expr = find_leaf(context, 1, arg_index); value_t moment; expr->compute(moment, details, context); - if (moment.type != value_t::DATETIME) + if (! moment.is_type(value_t::DATETIME)) throw new compute_error("Invalid date passed to P(value,date)", new valexpr_context(expr)); - result = result.value(*((datetime_t *)moment.data)); + result = result.value(moment.as_datetime_lval()); break; } case O_NOT: - left->compute(result, details, context); + left()->compute(result, details, context); if (result.strip_annotations()) result = false; else @@ -629,32 +699,32 @@ void value_expr_t::compute(value_t& result, const details_t& details, break; case O_QUES: { - assert(left); - assert(right); - assert(right->kind == O_COL); - left->compute(result, details, context); + assert(left()); + assert(right()); + assert(right()->kind == O_COL); + left()->compute(result, details, context); if (result.strip_annotations()) - right->left->compute(result, details, context); + right()->left()->compute(result, details, context); else - right->right->compute(result, details, context); + right()->right()->compute(result, details, context); break; } case O_AND: - assert(left); - assert(right); - left->compute(result, details, context); + assert(left()); + assert(right()); + left()->compute(result, details, context); result = result.strip_annotations(); if (result) - right->compute(result, details, context); + right()->compute(result, details, context); break; case O_OR: - assert(left); - assert(right); - left->compute(result, details, context); + assert(left()); + assert(right()); + left()->compute(result, details, context); if (! result.strip_annotations()) - right->compute(result, details, context); + right()->compute(result, details, context); break; case O_NEQ: @@ -663,11 +733,11 @@ void value_expr_t::compute(value_t& result, const details_t& details, case O_LTE: case O_GT: case O_GTE: { - assert(left); - assert(right); + assert(left()); + assert(right()); value_t temp; - left->compute(temp, details, context); - right->compute(result, details, context); + 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; @@ -675,14 +745,14 @@ void value_expr_t::compute(value_t& result, const details_t& details, 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; + default: assert(false); break; } break; } case O_NEG: - assert(left); - left->compute(result, details, context); + assert(left()); + left()->compute(result, details, context); result.negate(); break; @@ -690,1307 +760,342 @@ void value_expr_t::compute(value_t& result, const details_t& details, case O_SUB: case O_MUL: case O_DIV: { - assert(left); - assert(right); + assert(left()); + assert(right()); value_t temp; - right->compute(temp, details, context); - left->compute(result, details, context); + right()->compute(temp, details, context); + left()->compute(result, details, context); 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; + default: assert(false); break; } break; } case O_PERC: { - assert(left); + assert(left()); result = "100.0%"; value_t temp; - left->compute(temp, details, context); + left()->compute(temp, details, context); result *= temp; break; } case LAST: default: - assert(0); + assert(false); break; } } catch (error * err) { if (err->context.empty() || ! dynamic_cast<valexpr_context *>(err->context.back())) - err->context.push_back(new valexpr_context(this)); + err->context.push_back(new valexpr_context(const_cast<op_t *>(this))); throw err; } } -static inline void unexpected(char c, char wanted = '\0') { - if ((unsigned char) c == 0xff) { - if (wanted) - throw new value_expr_error(std::string("Missing '") + wanted + "'"); - else - throw new value_expr_error("Unexpected end"); - } else { - if (wanted) - throw new value_expr_error(std::string("Invalid char '") + c + - "' (wanted '" + wanted + "')"); - else - throw new value_expr_error(std::string("Invalid char '") + c + "'"); +void valexpr_context::describe(std::ostream& out) const throw() +{ + if (! expr) { + out << "valexpr_context expr not set!" << std::endl; + return; } -} -value_expr_t * parse_value_term(std::istream& in, scope_t * scope, - const short flags); + if (! desc.empty()) + out << desc << std::endl; -inline value_expr_t * parse_value_term(const char * p, scope_t * scope, - const short flags) { - std::istringstream stream(p); - return parse_value_term(stream, scope, flags); + out << " "; +#if 0 + unsigned long start = (long)out.tellp() - 1; + unsigned long begin; + unsigned long end; + bool found = print_value_expr(out, expr, true, error_node, &begin, &end); + out << std::endl; + if (found) { + out << " "; + for (unsigned int i = 0; i < end - start; i++) { + if (i >= begin - start) + out << "^"; + else + out << " "; + } + out << std::endl; + } +#endif } -value_expr_t * parse_value_term(std::istream& in, scope_t * scope, - const short flags) +ptr_op_t op_t::compile(scope_t& scope) { - value_expr node; - - char buf[256]; - char c = peek_next_nonws(in); - - if (flags & PARSE_VALEXPR_RELAXED) { - if (c == '@') { - in.get(c); - c = peek_next_nonws(in); - } - else if (! (c == '(' || c == '[' || c == '{' || c == '/')) { - amount_t temp; - char prev_c = c; - unsigned long pos = 0; - // When in relaxed parsing mode, we do want to migrate commodity - // flags, so that any precision specified by the user updates - // the current maximum precision displayed. - try { - pos = (long)in.tellg(); - - unsigned char parse_flags = 0; - if (flags & PARSE_VALEXPR_NO_MIGRATE) - parse_flags |= AMOUNT_PARSE_NO_MIGRATE; - if (flags & PARSE_VALEXPR_NO_REDUCE) - parse_flags |= AMOUNT_PARSE_NO_REDUCE; - - temp.parse(in, parse_flags); - } - catch (amount_error * err) { - // If the amount had no commodity, it must be an unambiguous - // variable reference - if (std::strcmp(err->what(), "No quantity specified for amount") == 0) { - in.clear(); - in.seekg(pos, std::ios::beg); - c = prev_c; - goto parse_ident; - } else { - throw err; - } - } - node.reset(new value_expr_t(value_expr_t::CONSTANT)); - node->value = new value_t(temp); - goto parsed; + switch (kind) { +#if 0 + case VAR_NAME: + case FUNC_NAME: + if (ptr_op_t def = scope.lookup(as_string())) { +#if 1 + return def; +#else + // Aren't definitions compiled when they go in? Would + // recompiling here really add any benefit? + return def->compile(scope); +#endif } - } + return this; +#endif - parse_ident: - if (std::isdigit(c) || c == '.') { - READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.'); - amount_t temp; - temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE); - node.reset(new value_expr_t(value_expr_t::CONSTANT)); - node->value = new value_t(temp); - goto parsed; + default: + break; } - 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); - beg = in.tellg(); - - 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, ')'); - - have_args = true; - c = peek_next_nonws(in); - } - bool definition = false; - if (c == '=') { - in.get(c); - if (peek_next_nonws(in) == '=') { - in.unget(); - c = '\0'; - } else { - definition = true; - } - } + if (kind < TERMINALS) + return this; - if (definition) { - std::auto_ptr<scope_t> params(new scope_t(scope)); - - int arg_index = 0; - if (have_args) { - bool done = false; - - 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 == '_'); - - 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::ARG_INDEX)); - node->left->arg_index = arg_index++; - params->define(ident, node.release()); - } + ptr_op_t lhs(left()->compile(scope)); + ptr_op_t rhs(right() ? right()->compile(scope) : ptr_op_t()); - if (peek_next_nonws(in) != '=') { - in.get(c); - unexpected(c, '='); - } - in.get(c); - } + if (lhs == left() && (! rhs || rhs == right())) + return this; - // Define the value associated with the defined identifier - value_expr def(parse_boolean_expr(in, params.get(), flags)); - if (! def.get()) - throw new value_expr_error(std::string("Definition failed for '") + buf + "'"); + ptr_op_t intermediate(copy(lhs, rhs)); - node.reset(new value_expr_t(value_expr_t::O_DEF)); - node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); - node->left->arg_index = arg_index; - node->set_right(def.release()); + if (lhs->is_value() && (! rhs || rhs->is_value())) + return wrap_value(intermediate->calc(scope)); - scope->define(buf, node.get()); - } else { - assert(scope); - value_expr_t * def = scope->lookup(buf); - if (! def) { - if (buf[1] == '\0' && - (buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' || - buf[0] == 'w' || buf[0] == 'W' || buf[0] == 'e')) { - in.unget(); - goto find_term; - } - throw new 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_expr args - (parse_value_expr(in, scope, flags | PARSE_VALEXPR_PARTIAL)); - - if (peek_next_nonws(in) != ')') { - in.get(c); - unexpected(c, ')'); - } - in.get(c); - - if (args.get()) { - count = count_leaves(args.get()); - node->set_right(args.release()); - } - } + return intermediate; +} - if (count != def->left->arg_index) { - std::ostringstream errmsg; - errmsg << "Wrong number of arguments to '" << buf - << "': saw " << count << ", wanted " << def->left->arg_index; - throw new value_expr_error(errmsg.str()); - } - } - else { - node.reset(def); - } - } - 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, flags)); - break; +value_t op_t::calc(scope_t& scope) +{ +#if 0 + bool find_all_nodes = false; +#endif - // 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; - } - } + switch (kind) { + case VALUE: + return as_value(); + +#if 0 + case VAR_NAME: + case FUNC_NAME: + if (ptr_op_t reference = compile(scope)) { + return reference->calc(scope); } else { - in.get(c); + throw_(calc_error, "No " << (kind == VAR_NAME ? "variable" : "function") + << " named '" << as_string() << "'"); } - - // 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 '{': { - amount_t temp; - temp.parse(in, AMOUNT_PARSE_NO_MIGRATE); - in.get(c); - if (c != '}') - unexpected(c, '}'); +#endif - node.reset(new value_expr_t(value_expr_t::CONSTANT)); - node->value = new value_t(temp); + case FUNCTION: + // This should never be evaluated directly; it only appears as the + // left node of an O_CALL operator. + assert(false); break; - } - case '(': { - std::auto_ptr<scope_t> locals(new scope_t(scope)); - node.reset(parse_value_expr(in, locals.get(), - flags | PARSE_VALEXPR_PARTIAL)); - in.get(c); - if (c != ')') - unexpected(c, ')'); - break; - } +#if 0 + case O_CALL: { + call_scope_t call_args(scope); - case '[': { - READ_INTO(in, buf, 255, c, c != ']'); - if (c != ']') - unexpected(c, ']'); - in.get(c); + if (right()) + call_args.set_args(right()->calc(scope)); - interval_t timespan(buf); - node.reset(new value_expr_t(value_expr_t::CONSTANT)); - node->value = new value_t(timespan.first()); - break; - } - - default: - in.unget(); - break; - } + ptr_op_t func = left(); + string name; - parsed: - return node.release(); -} - -value_expr_t * parse_mul_expr(std::istream& in, scope_t * scope, - const short flags) -{ - value_expr 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, flags)); - return node.release(); - } - - node.reset(parse_value_term(in, scope, flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - while (c == '*' || c == '/') { - in.get(c); - switch (c) { - case '*': { - value_expr prev(node.release()); - node.reset(new value_expr_t(value_expr_t::O_MUL)); - node->set_left(prev.release()); - node->set_right(parse_value_term(in, scope, flags)); - break; - } - - case '/': { - value_expr prev(node.release()); - node.reset(new value_expr_t(value_expr_t::O_DIV)); - node->set_left(prev.release()); - node->set_right(parse_value_term(in, scope, flags)); - break; - } - } - c = peek_next_nonws(in); + if (func->kind == FUNC_NAME) { + name = func->as_string(); + func = func->compile(scope); } - } - return node.release(); -} + if (func->kind != FUNCTION) + throw_(calc_error, + name.empty() ? string("Attempt to call non-function") : + (string("Attempt to call unknown function '") + name + "'")); -value_expr_t * parse_add_expr(std::istream& in, scope_t * scope, - const short flags) -{ - value_expr node; - - if (peek_next_nonws(in) == '-') { - char c; - in.get(c); - value_expr expr(parse_mul_expr(in, scope, flags)); - if (expr->kind == value_expr_t::CONSTANT) { - expr->value->negate(); - return expr.release(); - } - node.reset(new value_expr_t(value_expr_t::O_NEG)); - node->set_left(expr.release()); - return node.release(); + return func->as_function()(call_args); } +#endif - node.reset(parse_mul_expr(in, scope, flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - while (c == '+' || c == '-') { - in.get(c); - switch (c) { - case '+': { - value_expr prev(node.release()); - node.reset(new value_expr_t(value_expr_t::O_ADD)); - node->set_left(prev.release()); - node->set_right(parse_mul_expr(in, scope, flags)); - break; - } + case ARG_INDEX: { + call_scope_t& args(CALL_SCOPE(scope)); - case '-': { - value_expr prev(node.release()); - node.reset(new value_expr_t(value_expr_t::O_SUB)); - node->set_left(prev.release()); - node->set_right(parse_mul_expr(in, scope, flags)); - break; - } - } - c = peek_next_nonws(in); - } + if (as_long() >= 0 && as_long() < args.size()) + return args[as_long()]; + else + throw_(calc_error, "Reference to non-existing argument"); + break; } - return node.release(); -} +#if 0 + case O_FIND: + case O_RFIND: + return select_nodes(scope, left()->calc(scope), right(), kind == O_RFIND); -value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope, - const short flags) -{ - value_expr node; - - if (peek_next_nonws(in) == '!') { - char c; - in.get(c); - node.reset(new value_expr_t(value_expr_t::O_NOT)); - node->set_left(parse_add_expr(in, scope, flags)); - return node.release(); - } + case O_PRED: { + value_t values = left()->calc(scope); - node.reset(parse_add_expr(in, scope, flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - if (c == '!' || c == '=' || c == '<' || c == '>') { - in.get(c); - switch (c) { - case '!': - case '=': { - bool negate = c == '!'; - if ((c = peek_next_nonws(in)) == '=') - in.get(c); - else - unexpected(c, '='); - value_expr 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, flags)); - break; - } + if (! values.is_null()) { + op_predicate pred(right()); - case '<': { - value_expr 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->set_left(prev.release()); - node->set_right(parse_add_expr(in, scope, flags)); - break; - } + if (! values.is_sequence()) { + context_scope_t value_scope(scope, values, 0, 1); + if (pred(value_scope)) + return values; + return NULL_VALUE; + } else { + std::size_t index = 0; + std::size_t size = values.as_sequence().size(); - case '>': { - value_expr 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->set_left(prev.release()); - node->set_right(parse_add_expr(in, scope, flags)); - break; - } + value_t result; - default: - if (! in.eof()) - unexpected(c); - break; + foreach (const value_t& value, values.as_sequence()) { + context_scope_t value_scope(scope, value, index, size); + if (pred(value_scope)) + result.push_back(value); + index++; + } + return result; } } + break; } - return node.release(); -} + case NODE_ID: + switch (as_name()) { + case document_t::CURRENT: + return current_value(scope); -value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope, - const short flags) -{ - value_expr node(parse_logic_expr(in, scope, flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - while (c == '&' || c == '|' || c == '?') { - in.get(c); - switch (c) { - case '&': { - value_expr prev(node.release()); - node.reset(new value_expr_t(value_expr_t::O_AND)); - node->set_left(prev.release()); - node->set_right(parse_logic_expr(in, scope, flags)); - break; - } + case document_t::PARENT: + if (optional<parent_node_t&> parent = current_xml_node(scope).parent()) + return &*parent; + else + throw_(std::logic_error, "Attempt to access parent of root node"); + break; - case '|': { - value_expr prev(node.release()); - node.reset(new value_expr_t(value_expr_t::O_OR)); - node->set_left(prev.release()); - node->set_right(parse_logic_expr(in, scope, flags)); - break; - } + case document_t::ROOT: + return ¤t_xml_node(scope).document(); - case '?': { - value_expr prev(node.release()); - node.reset(new value_expr_t(value_expr_t::O_QUES)); - 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, flags)); - c = peek_next_nonws(in); - if (c != ':') - unexpected(c, ':'); - in.get(c); - node->right->set_right(parse_logic_expr(in, scope, flags)); - break; - } + case document_t::ALL: + find_all_nodes = true; + break; - default: - if (! in.eof()) - unexpected(c); - break; - } - c = peek_next_nonws(in); + default: + break; // pass down to the NODE_NAME case } - } + // fall through... - return node.release(); -} + case NODE_NAME: { + node_t& current_node(current_xml_node(scope)); -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); - globals->define("today", 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::PRICE); - globals->define("i", node); - globals->define("price", 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::DATE); - globals->define("d", node); - globals->define("date", node); - - node = new value_expr_t(value_expr_t::ACT_DATE); - globals->define("act_date", node); - globals->define("actual_date", node); - - node = new value_expr_t(value_expr_t::EFF_DATE); - globals->define("eff_date", node); - globals->define("effective_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::PRICE_TOTAL); - globals->define("I", node); - globals->define("total_price", node); - - node = new value_expr_t(value_expr_t::COST_TOTAL); - globals->define("B", node); - globals->define("total_cost", 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::ARG_INDEX)); - node->left->arg_index = 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::ARG_INDEX)); - node->left->arg_index = 1; - node->set_right(new value_expr_t(value_expr_t::F_ROUND)); - globals->define("round", node); - - node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); - node->left->arg_index = 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::ARG_INDEX)); - node->left->arg_index = 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::ARG_INDEX)); - node->left->arg_index = 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::ARG_INDEX)); - node->left->arg_index = 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::ARG_INDEX)); - node->left->arg_index = 2; - node->set_right(new value_expr_t(value_expr_t::F_VALUE)); - globals->define("P", node); - - parse_value_definition("@value=@P(@t,@m)", globals); - parse_value_definition("@total_value=@P(@T,@m)", globals); - parse_value_definition("@valueof(x)=@P(@x,@m)", globals); - parse_value_definition("@datedvalueof(x,y)=@P(@x,@y)", globals); - - node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); - node->left->arg_index = 1; - node->set_right(new value_expr_t(value_expr_t::F_PRICE)); - globals->define("priceof", node); - - node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); - node->left->arg_index = 1; - node->set_right(new value_expr_t(value_expr_t::F_DATE)); - globals->define("dateof", node); - - node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); - node->left->arg_index = 2; - node->set_right(new value_expr_t(value_expr_t::F_DATECMP)); - globals->define("datecmp", node); - - node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); - node->left->arg_index = 1; - node->set_right(new value_expr_t(value_expr_t::F_YEAR)); - globals->define("yearof", node); - - node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); - node->left->arg_index = 1; - node->set_right(new value_expr_t(value_expr_t::F_MONTH)); - globals->define("monthof", node); - - node = new value_expr_t(value_expr_t::O_DEF); - node->set_left(new value_expr_t(value_expr_t::ARG_INDEX)); - node->left->arg_index = 1; - node->set_right(new value_expr_t(value_expr_t::F_DAY)); - globals->define("dayof", node); - - parse_value_definition("@year=@yearof(@d)", globals); - parse_value_definition("@month=@monthof(@d)", globals); - parse_value_definition("@day=@dayof(@d)", globals); - - // 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("total_market", 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("total_gain", node); - - parse_value_definition("@min(x,y)=@x<@y?@x:@y", globals); - parse_value_definition("@max(x,y)=@x>@y?@x:@y", globals); -} + if (current_node.is_parent_node()) { + const bool have_name_id = kind == NODE_ID; -value_expr_t * parse_value_expr(std::istream& in, scope_t * scope, - const short flags) -{ - if (! global_scope.get()) - init_value_expr(); - - std::auto_ptr<scope_t> this_scope(new scope_t(scope ? scope : - global_scope.get())); - value_expr node; - node.reset(parse_boolean_expr(in, this_scope.get(), flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - while (c == ',') { - in.get(c); - switch (c) { - case ',': { - value_expr 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(), flags)); - break; - } + parent_node_t& parent(current_node.as_parent_node()); - default: - if (! in.eof()) - unexpected(c); - break; + value_t result; + foreach (node_t * child, parent) { + if (find_all_nodes || + ( have_name_id && as_name() == child->name_id()) || + (! have_name_id && as_string() == child->name())) + result.push_back(child); } - c = peek_next_nonws(in); + return result; } + break; } - char c; - if (! node.get()) { - in.get(c); - if (in.eof()) - throw new value_expr_error(std::string("Failed to parse value expression")); - else - unexpected(c); - } else if (! (flags & PARSE_VALEXPR_PARTIAL)) { - in.get(c); - if (! in.eof()) - unexpected(c); - else - in.unget(); - } - - return node.release(); -} - -valexpr_context::valexpr_context(const ledger::value_expr_t * _expr, - const std::string& desc) throw() - : expr(_expr), error_node(_expr), error_context(desc) -{ - error_node->acquire(); -} - -valexpr_context::~valexpr_context() throw() -{ - if (expr) expr->release(); - if (error_node) error_node->release(); -} - -void valexpr_context::describe(std::ostream& out) const throw() -{ - if (! expr) { - out << "valexpr_context expr not set!" << std::endl; - return; - } - - if (! desc.empty()) - out << desc << std::endl; - - out << " "; - unsigned long start = (long)out.tellp() - 1; - unsigned long begin; - unsigned long end; - bool found = ledger::write_value_expr(out, expr, true, - error_node, &begin, &end); - out << std::endl; - if (found) { - out << " "; - for (int i = 0; i < end - start; i++) { - if (i >= begin - start) - out << "^"; - else - out << " "; - } - out << std::endl; - } -} - -bool write_value_expr(std::ostream& out, - const value_expr_t * node, - const bool relaxed, - const value_expr_t * node_to_find, - unsigned long * start_pos, - unsigned long * end_pos) -{ - int arg_index = 0; - bool found = false; - value_expr_t * expr; - - if (start_pos && node == node_to_find) { - *start_pos = (long)out.tellp() - 1; - found = true; - } - - std::string symbol; + case ATTR_ID: + case ATTR_NAME: + if (optional<value_t&> value = + kind == ATTR_ID ? current_xml_node(scope).get_attr(as_name()) : + current_xml_node(scope).get_attr(as_string())) + return *value; - switch (node->kind) { - case value_expr_t::ARG_INDEX: - out << node->arg_index; break; +#endif - case value_expr_t::CONSTANT: - switch (node->value->type) { - case value_t::BOOLEAN: - assert(0); - break; - case value_t::DATETIME: - out << '[' << *(node->value) << ']'; - break; - case value_t::INTEGER: - case value_t::AMOUNT: - if (! relaxed) - out << '{'; - out << *(node->value); - if (! relaxed) - out << '}'; - break; - case value_t::BALANCE: - case value_t::BALANCE_PAIR: - assert(0); - break; - } - break; + case O_NEQ: + return left()->calc(scope) != right()->calc(scope); + case O_EQ: + return left()->calc(scope) == right()->calc(scope); + case O_LT: + return left()->calc(scope) < right()->calc(scope); + case O_LTE: + return left()->calc(scope) <= right()->calc(scope); + case O_GT: + return left()->calc(scope) > right()->calc(scope); + case O_GTE: + return left()->calc(scope) >= right()->calc(scope); - case value_expr_t::AMOUNT: - symbol = "amount"; break; - case value_expr_t::PRICE: - symbol = "price"; break; - case value_expr_t::COST: - symbol = "cost"; break; - case value_expr_t::DATE: - symbol = "date"; break; - case value_expr_t::ACT_DATE: - symbol = "actual_date"; break; - case value_expr_t::EFF_DATE: - symbol = "effective_date"; break; - case value_expr_t::CLEARED: - symbol = "cleared"; break; - case value_expr_t::PENDING: - symbol = "pending"; break; - case value_expr_t::REAL: - symbol = "real"; break; - case value_expr_t::ACTUAL: - symbol = "actual"; break; - case value_expr_t::INDEX: - symbol = "index"; break; - case value_expr_t::COUNT: - symbol = "count"; break; - case value_expr_t::DEPTH: - symbol = "depth"; break; - case value_expr_t::TOTAL: - symbol = "total"; break; - case value_expr_t::PRICE_TOTAL: - symbol = "total_price"; break; - case value_expr_t::COST_TOTAL: - symbol = "total_cost"; break; - case value_expr_t::F_NOW: - symbol = "now"; break; - - case value_expr_t::VALUE_EXPR: - if (write_value_expr(out, amount_expr.get(), relaxed, - node_to_find, start_pos, end_pos)) - found = true; - break; - case value_expr_t::TOTAL_EXPR: - if (write_value_expr(out, total_expr.get(), relaxed, - node_to_find, start_pos, end_pos)) - found = true; - break; + case O_ADD: + return left()->calc(scope) + right()->calc(scope); + case O_SUB: + return left()->calc(scope) - right()->calc(scope); + case O_MUL: + return left()->calc(scope) * right()->calc(scope); + case O_DIV: + return left()->calc(scope) / right()->calc(scope); - case value_expr_t::F_ARITH_MEAN: - symbol = "average"; break; - case value_expr_t::F_ABS: - symbol = "abs"; break; - case value_expr_t::F_QUANTITY: - symbol = "quantity"; break; - case value_expr_t::F_COMMODITY: - symbol = "commodity"; break; - case value_expr_t::F_SET_COMMODITY: - symbol = "set_commodity"; break; - case value_expr_t::F_VALUE: - symbol = "valueof"; break; - case value_expr_t::F_PRICE: - symbol = "priceof"; break; - case value_expr_t::F_DATE: - symbol = "dateof"; break; - case value_expr_t::F_DATECMP: - symbol = "datecmp"; break; - case value_expr_t::F_YEAR: - symbol = "yearof"; break; - case value_expr_t::F_MONTH: - symbol = "monthof"; break; - case value_expr_t::F_DAY: - symbol = "dayof"; break; - - case value_expr_t::F_CODE_MASK: - out << "c/" << node->mask->pattern << "/"; - break; - case value_expr_t::F_PAYEE_MASK: - out << "p/" << node->mask->pattern << "/"; - break; - case value_expr_t::F_NOTE_MASK: - out << "e/" << node->mask->pattern << "/"; - break; - case value_expr_t::F_ACCOUNT_MASK: - out << "W/" << node->mask->pattern << "/"; - break; - case value_expr_t::F_SHORT_ACCOUNT_MASK: - out << "w/" << node->mask->pattern << "/"; - break; - case value_expr_t::F_COMMODITY_MASK: - out << "C/" << node->mask->pattern << "/"; - break; + case O_NEG: + assert(! right()); + return left()->calc(scope).negate(); - case value_expr_t::O_NOT: - out << "!"; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - break; - case value_expr_t::O_NEG: - out << "-"; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - break; - case value_expr_t::O_PERC: - out << "%"; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - break; + case O_NOT: + assert(! right()); + return ! left()->calc(scope); - case value_expr_t::O_ARG: - out << "@arg" << node->arg_index; - break; - case value_expr_t::O_DEF: - out << "<def args=\""; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << "\" value=\""; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << "\">"; - break; + case O_AND: + return left()->calc(scope) && right()->calc(scope); + case O_OR: + return left()->calc(scope) || right()->calc(scope); + +#if 0 + case O_UNION: +#endif + case O_COMMA: { + value_t result(left()->calc(scope)); + + ptr_op_t next = right(); + while (next) { + ptr_op_t value_op; + if (next->kind == O_COMMA /* || next->kind == O_UNION */) { + value_op = next->left(); + next = next->right(); + } else { + value_op = next; + next = NULL; + } - case value_expr_t::O_REF: - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - if (node->right) { - out << "("; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; + result.push_back(value_op->calc(scope)); } - break; - - case value_expr_t::O_COM: - if (node->left && - write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ", "; - if (node->right && - write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - break; - case value_expr_t::O_QUES: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " ? "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_COL: - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " : "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - break; - - case value_expr_t::O_AND: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " & "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_OR: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " | "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - - case value_expr_t::O_NEQ: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " != "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_EQ: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " == "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_LT: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " < "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_LTE: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " <= "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_GT: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " > "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_GTE: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " >= "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - - case value_expr_t::O_ADD: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " + "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_SUB: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " - "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_MUL: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " * "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; - case value_expr_t::O_DIV: - out << "("; - if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << " / "; - if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos)) - found = true; - out << ")"; - break; + return result; + } - case value_expr_t::LAST: + case LAST: default: - assert(0); + assert(false); break; } - if (! symbol.empty()) { - if (commodity_t::find(symbol)) - out << '@'; - out << symbol; - } + return NULL_VALUE; +} - if (end_pos && node == node_to_find) - *end_pos = (long)out.tellp() - 1; +} // namespace expr - return found; +namespace { + expr::parser_t value_expr_parser; } -void dump_value_expr(std::ostream& out, const value_expr_t * node, - const int depth) +value_expr::value_expr(const string& _expr_str) : expr_str(_expr_str) { - 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::ARG_INDEX: - out << "ARG_INDEX - " << node->arg_index; - break; - case value_expr_t::CONSTANT: - out << "CONSTANT - " << *(node->value); - break; + TRACE_CTOR(value_expr, "const string&"); - case value_expr_t::AMOUNT: out << "AMOUNT"; break; - case value_expr_t::PRICE: out << "PRICE"; break; - case value_expr_t::COST: out << "COST"; break; - case value_expr_t::DATE: out << "DATE"; break; - case value_expr_t::ACT_DATE: out << "ACT_DATE"; break; - case value_expr_t::EFF_DATE: out << "EFF_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::PRICE_TOTAL: out << "PRICE_TOTAL"; break; - case value_expr_t::COST_TOTAL: out << "COST_TOTAL"; break; - - case value_expr_t::VALUE_EXPR: out << "VALUE_EXPR"; break; - case value_expr_t::TOTAL_EXPR: out << "TOTAL_EXPR"; 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: - out << "F_ACCOUNT_MASK"; break; - case value_expr_t::F_SHORT_ACCOUNT_MASK: - out << "F_SHORT_ACCOUNT_MASK"; break; - case value_expr_t::F_COMMODITY_MASK: - out << "F_COMMODITY_MASK"; break; - case value_expr_t::F_VALUE: out << "F_VALUE"; break; - case value_expr_t::F_PRICE: out << "F_PRICE"; break; - case value_expr_t::F_DATE: out << "F_DATE"; break; - case value_expr_t::F_DATECMP: out << "F_DATECMP"; break; - case value_expr_t::F_YEAR: out << "F_YEAR"; break; - case value_expr_t::F_MONTH: out << "F_MONTH"; break; - case value_expr_t::F_DAY: out << "F_DAY"; 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; - } - - out << " (" << node->refc << ')' << 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); - } - } else { - assert(! node->left); - } + if (! _expr_str.empty()) + ptr = value_expr_parser.parse(expr_str).ptr; } } // namespace ledger |