summaryrefslogtreecommitdiff
path: root/src/op.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2008-08-05 13:17:04 -0400
committerJohn Wiegley <johnw@newartisans.com>2008-08-05 18:05:49 -0400
commitf6f4a46cf5b14f9a2170cd6475958efbf320caec (patch)
tree05bc1defcdebc201de3dd10477483d906a842821 /src/op.cc
parentb7970b29855563e4c67f85af8b31233eda80c22a (diff)
downloadfork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.gz
fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.bz2
fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.zip
Moved around most of the files so that source code is in src/, documentation
is in doc/, etc.
Diffstat (limited to 'src/op.cc')
-rw-r--r--src/op.cc1130
1 files changed, 1130 insertions, 0 deletions
diff --git a/src/op.cc b/src/op.cc
new file mode 100644
index 00000000..b412c47f
--- /dev/null
+++ b/src/op.cc
@@ -0,0 +1,1130 @@
+/*
+ * Copyright (c) 2003-2008, 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 "op.h"
+#include "scope.h"
+#include "binary.h"
+
+namespace ledger {
+
+#if 0
+void expr_t::op_t::compute(value_t& result,
+ const details_t& details,
+ ptr_op_t context) const
+{
+ try {
+ switch (kind) {
+ case INDEX:
+ throw compute_error("Cannot directly compute an argument index");
+
+ case VALUE:
+ result = as_value();
+ break;
+
+ case F_NOW:
+ result = terminus;
+ break;
+
+ case AMOUNT:
+ if (details.xact) {
+ if (xact_has_xdata(*details.xact) &&
+ xact_xdata_(*details.xact).dflags & XACT_COMPOUND)
+ result = xact_xdata_(*details.xact).value;
+ else
+ result = details.xact->amount;
+ }
+ else if (details.account && account_has_xdata(*details.account)) {
+ result = account_xdata(*details.account).value;
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case PRICE:
+ if (details.xact) {
+ bool set = false;
+ if (xact_has_xdata(*details.xact)) {
+ xact_xdata_t& xdata(xact_xdata_(*details.xact));
+ if (xdata.dflags & XACT_COMPOUND) {
+ result = xdata.value.value();
+ set = true;
+ }
+ }
+ 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.value();
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case COST:
+ if (details.xact) {
+ bool set = false;
+ if (xact_has_xdata(*details.xact)) {
+ xact_xdata_t& xdata(xact_xdata_(*details.xact));
+ if (xdata.dflags & XACT_COMPOUND) {
+ result = xdata.value.cost();
+ set = true;
+ }
+ }
+
+ if (! set) {
+ if (details.xact->cost)
+ result = *details.xact->cost;
+ else
+ result = details.xact->amount;
+ }
+ }
+ else if (details.account && account_has_xdata(*details.account)) {
+ result = account_xdata(*details.account).value.cost();
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case TOTAL:
+ if (details.xact && xact_has_xdata(*details.xact))
+ result = xact_xdata_(*details.xact).total;
+ else if (details.account && account_has_xdata(*details.account))
+ result = account_xdata(*details.account).total;
+ else
+ result = 0L;
+ break;
+ case PRICE_TOTAL:
+ if (details.xact && xact_has_xdata(*details.xact))
+ result = xact_xdata_(*details.xact).total.value();
+ else if (details.account && account_has_xdata(*details.account))
+ result = account_xdata(*details.account).total.value();
+ else
+ result = 0L;
+ break;
+ case COST_TOTAL:
+ if (details.xact && xact_has_xdata(*details.xact))
+ result = xact_xdata_(*details.xact).total.cost();
+ else if (details.account && account_has_xdata(*details.account))
+ result = account_xdata(*details.account).total.cost();
+ else
+ result = 0L;
+ break;
+
+ case VALUE_EXPR:
+ if (value_expr::amount_expr.get())
+ value_expr::amount_expr->compute(result, details, context);
+ else
+ result = 0L;
+ break;
+ case TOTAL_EXPR:
+ if (value_expr::total_expr.get())
+ value_expr::total_expr->compute(result, details, context);
+ else
+ result = 0L;
+ break;
+
+ case DATE:
+ if (details.xact && xact_has_xdata(*details.xact) &&
+ is_valid(xact_xdata_(*details.xact).date))
+ result = xact_xdata_(*details.xact).date;
+ else if (details.xact)
+ result = details.xact->date();
+ else if (details.entry)
+ result = details.entry->date();
+ else
+ result = terminus;
+ break;
+
+ case ACT_DATE:
+ if (details.xact && xact_has_xdata(*details.xact) &&
+ is_valid(xact_xdata_(*details.xact).date))
+ result = xact_xdata_(*details.xact).date;
+ else if (details.xact)
+ result = details.xact->actual_date();
+ else if (details.entry)
+ result = details.entry->actual_date();
+ else
+ result = terminus;
+ break;
+
+ case EFF_DATE:
+ if (details.xact && xact_has_xdata(*details.xact) &&
+ is_valid(xact_xdata_(*details.xact).date))
+ result = xact_xdata_(*details.xact).date;
+ else if (details.xact)
+ result = details.xact->effective_date();
+ else if (details.entry)
+ result = details.entry->effective_date();
+ else
+ result = terminus;
+ break;
+
+ case CLEARED:
+ if (details.xact)
+ result = details.xact->state == xact_t::CLEARED;
+ else
+ result = false;
+ break;
+ case PENDING:
+ if (details.xact)
+ result = details.xact->state == xact_t::PENDING;
+ else
+ result = false;
+ break;
+
+ case REAL:
+ if (details.xact)
+ result = ! (details.xact->has_flags(XACT_VIRTUAL));
+ else
+ result = true;
+ break;
+
+ case ACTUAL:
+ if (details.xact)
+ result = ! (details.xact->has_flags(XACT_AUTO));
+ else
+ result = true;
+ break;
+
+ case INDEX:
+ if (details.xact && xact_has_xdata(*details.xact))
+ result = long(xact_xdata_(*details.xact).index + 1);
+ else if (details.account && account_has_xdata(*details.account))
+ result = long(account_xdata(*details.account).count);
+ else
+ result = 0L;
+ break;
+
+ case COUNT:
+ if (details.xact && xact_has_xdata(*details.xact))
+ result = long(xact_xdata_(*details.xact).index + 1);
+ else if (details.account && account_has_xdata(*details.account))
+ result = long(account_xdata(*details.account).total_count);
+ else
+ result = 0L;
+ break;
+
+ case DEPTH:
+ if (details.account)
+ result = long(details.account->depth);
+ else
+ result = 0L;
+ break;
+
+ case F_PRICE: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ result = result.value();
+ break;
+ }
+
+ case F_DATE: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ result = result.as_datetime();
+ break;
+ }
+
+ case F_DATECMP: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ result = result.as_datetime();
+ if (! result)
+ break;
+
+ arg_index = 0;
+ expr = find_leaf(context, 1, arg_index);
+ value_t moment;
+ expr->compute(moment, details, context);
+ if (moment.is_type(value_t::DATETIME)) {
+ result.cast(value_t::INTEGER);
+ moment.cast(value_t::INTEGER);
+ result -= moment;
+ } else {
+ add_error_context(expr_context(expr));
+ throw compute_error("Invalid date passed to datecmp(value,date)");
+ }
+ break;
+ }
+
+ case F_YEAR:
+ case F_MONTH:
+ case F_DAY: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+
+ if (! result.is_type(value_t::DATETIME)) {
+ add_error_context(expr_context(expr));
+ throw compute_error("Invalid date passed to year|month|day(date)");
+ }
+
+ const date_t& moment(result.as_date());
+ switch (kind) {
+ case F_YEAR:
+ result = (long)moment.year();
+ break;
+ case F_MONTH:
+ result = (long)moment.month();
+ break;
+ case F_DAY:
+ result = (long)moment.day();
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case F_ARITH_MEAN: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ if (details.xact && xact_has_xdata(*details.xact)) {
+ expr->compute(result, details, context);
+ result /= amount_t(long(xact_xdata_(*details.xact).index + 1));
+ }
+ else if (details.account && account_has_xdata(*details.account) &&
+ account_xdata(*details.account).total_count) {
+ 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), context);
+ break;
+
+ case F_ABS: {
+ 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: {
+ 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: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ if (! result.is_type(value_t::AMOUNT)) {
+ add_error_context(expr_context(expr));
+ throw compute_error("Argument to commodity() must be a commoditized amount");
+ }
+ amount_t temp("1");
+ temp.set_commodity(result.as_amount().commodity());
+ result = temp;
+ break;
+ }
+
+ case F_SET_COMMODITY: {
+ 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.is_type(value_t::AMOUNT)) {
+ add_error_context(expr_context(expr));
+ throw compute_error("Second argument to set_commodity() must be a commoditized amount");
+ }
+ amount_t one("1");
+ one.set_commodity(result.as_amount().commodity());
+ result = one;
+
+ result *= temp;
+ break;
+ }
+
+ case F_QUANTITY: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+
+ const balance_t * bal = NULL;
+ switch (result.type()) {
+ case value_t::BALANCE_PAIR:
+ bal = &(result.as_balance_pair().quantity());
+ // fall through...
+
+ case value_t::BALANCE:
+ if (! bal)
+ bal = &result.as_balance();
+
+ if (bal->amounts.size() < 2) {
+ result.cast(value_t::AMOUNT);
+ } else {
+ value_t temp;
+ for (balance_t::amounts_map::value_type pair, bal->amounts) {
+ amount_t x = pair.second;
+ x.clear_commodity();
+ temp += x;
+ }
+ result = temp;
+ assert(temp.is_type(value_t::AMOUNT));
+ }
+ // fall through...
+
+ case value_t::AMOUNT:
+ result.as_amount_lval().clear_commodity();
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ case O_ARG: {
+ long arg_index = 0;
+ assert(left()->kind == INDEX);
+ ptr_op_t expr = find_leaf(context, left()->as_long(), arg_index);
+ if (expr)
+ expr->compute(result, details, context);
+ else
+ result = 0L;
+ break;
+ }
+
+ case O_COMMA:
+ if (! left()) {
+ add_error_context(expr_context(*this));
+ throw compute_error("Comma operator missing left operand");
+ }
+ if (! right()) {
+ add_error_context(expr_context(*this));
+ throw compute_error("Comma operator missing right operand");
+ }
+ right()->compute(result, details, context);
+ break;
+
+ case O_DEF:
+ result = 0L;
+ break;
+
+ case O_REF: {
+ assert(left());
+ if (right()) {
+ value_expr args(reduce_leaves(right(), details, context));
+ left()->compute(result, details, args.get());
+ } else {
+ left()->compute(result, details, context);
+ }
+ break;
+ }
+
+ case F_VALUE: {
+ 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.is_type(value_t::DATETIME)) {
+ add_error_context(expr_context(expr));
+ throw compute_error("Invalid date passed to P(value,date)");
+ }
+ result = result.value(moment.as_datetime());
+ break;
+ }
+
+ case O_NOT:
+ left()->compute(result, details, context);
+ if (result.strip_annotations())
+ result = false;
+ else
+ result = true;
+ break;
+
+ case O_QUES: {
+ assert(left());
+ assert(right());
+ assert(right()->kind == O_COL);
+ left()->compute(result, details, context);
+ if (result.strip_annotations())
+ right()->left()->compute(result, details, context);
+ else
+ right()->right()->compute(result, details, context);
+ break;
+ }
+
+ case O_AND:
+ assert(left());
+ assert(right());
+ left()->compute(result, details, context);
+ result = result.strip_annotations();
+ if (result)
+ right()->compute(result, details, context);
+ break;
+
+ case O_OR:
+ assert(left());
+ assert(right());
+ left()->compute(result, details, context);
+ if (! result.strip_annotations())
+ right()->compute(result, details, context);
+ break;
+
+ case O_NEQ:
+ case O_EQ:
+ case O_LT:
+ case O_LTE:
+ case O_GT:
+ case O_GTE: {
+ assert(left());
+ assert(right());
+ value_t temp;
+ left()->compute(temp, details, 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;
+ case O_GT: result = temp > result; break;
+ case O_GTE: result = temp >= result; break;
+ default: assert(false); break;
+ }
+ break;
+ }
+
+ case O_NEG:
+ assert(left());
+ left()->compute(result, details, context);
+ result.negate();
+ break;
+
+ case O_ADD:
+ case O_SUB:
+ case O_MUL:
+ case O_DIV: {
+ assert(left());
+ assert(right());
+ value_t temp;
+ 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(false); break;
+ }
+ break;
+ }
+
+ case O_PERC: {
+ assert(left());
+ result = "100.0%";
+ value_t temp;
+ left()->compute(temp, details, context);
+ result *= temp;
+ break;
+ }
+
+ case LAST:
+ default:
+ assert(false);
+ break;
+ }
+ }
+ catch (const std::exception& err) {
+ add_error_context(expr_context(*this));
+ throw err;
+ }
+}
+#endif
+
+expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope)
+{
+ switch (kind) {
+ case IDENT:
+ if (ptr_op_t def = scope.lookup(as_ident())) {
+#if 1
+ return def;
+#else
+ // jww (2008-08-02): Aren't definitions compiled when they go in?
+ // Would recompiling here really add any benefit?
+ return def->compile(scope);
+#endif
+ }
+ return this;
+
+ default:
+ break;
+ }
+
+ if (kind < TERMINALS)
+ return this;
+
+ ptr_op_t lhs(left()->compile(scope));
+ ptr_op_t rhs(kind > UNARY_OPERATORS ? right()->compile(scope) : ptr_op_t());
+
+ if (lhs == left() && (! rhs || rhs == right()))
+ return this;
+
+ ptr_op_t intermediate(copy(lhs, rhs));
+
+ if (lhs->is_value() && (! rhs || rhs->is_value()))
+ return wrap_value(intermediate->calc(scope));
+
+ return intermediate;
+}
+
+value_t expr_t::op_t::calc(scope_t& scope)
+{
+ switch (kind) {
+ case VALUE:
+ return as_value();
+
+ case IDENT:
+#if 0
+ if (ptr_op_t reference = compile(scope)) {
+ if (reference != this)
+ return reference->calc(scope);
+ }
+#endif
+ throw_(calc_error, "Unknown identifier '" << as_ident() << "'");
+
+ case FUNCTION: {
+ // Evaluating a FUNCTION is the same as calling it directly; this happens
+ // when certain functions-that-look-like-variables (such as "amount") are
+ // resolved.
+ call_scope_t call_args(scope);
+ return as_function()(call_args);
+ }
+
+ case O_CALL: {
+ call_scope_t call_args(scope);
+
+ if (right())
+ call_args.set_args(right()->calc(scope));
+
+ ptr_op_t func = left();
+ string name;
+
+#if 0
+ // The expression must be compiled beforehand in order to resolve this
+ // into a function.
+ if (func->kind == IDENT) {
+ name = func->as_ident();
+ ptr_op_t def = func->compile(scope);
+ if (def == func)
+ throw_(calc_error,
+ "Calling unknown function '" << name << "'");
+ func = def;
+ }
+#endif
+
+ if (func->kind != FUNCTION)
+ throw_(calc_error, "Calling non-function");
+
+ return func->as_function()(call_args);
+ }
+
+ case O_MATCH:
+ assert(right()->is_mask());
+ return right()->as_mask().match(left()->calc(scope).to_string());
+
+ case INDEX: {
+ const call_scope_t& args(downcast<const call_scope_t>(scope));
+
+ if (as_index() < args.size())
+ return args[as_index()];
+ else
+ throw_(calc_error, "Reference to non-existing argument " << as_index());
+ 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 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 O_NEG:
+ assert(! right());
+ return left()->calc(scope).negate();
+
+ case O_NOT:
+ assert(! right());
+ return ! left()->calc(scope);
+
+ case O_AND:
+ return ! left()->calc(scope) ? value_t(false) : right()->calc(scope);
+
+ case O_OR:
+ if (value_t temp = left()->calc(scope))
+ return temp;
+ else
+ return right()->calc(scope);
+
+ 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;
+ }
+
+ result.push_back(value_op->calc(scope));
+ }
+ return result;
+ }
+
+ case LAST:
+ default:
+ assert(false);
+ break;
+ }
+
+ return NULL_VALUE;
+}
+
+bool expr_t::op_t::print(std::ostream& out, print_context_t& context) const
+{
+ bool found = false;
+
+ if (context.start_pos && this == context.op_to_find) {
+ *context.start_pos = static_cast<unsigned long>(out.tellp()) - 1;
+ found = true;
+ }
+
+ string symbol;
+
+ switch (kind) {
+ case VALUE: {
+ as_value().print(out, context.relaxed);
+ break;
+ }
+
+ case IDENT:
+ out << as_ident();
+ break;
+
+ case FUNCTION:
+ out << "<FUNCTION>";
+ break;
+
+ case INDEX:
+ out << '@' << as_index();
+ break;
+
+ case O_NOT:
+ out << "!";
+ if (left() && left()->print(out, context))
+ found = true;
+ break;
+ case O_NEG:
+ out << "-";
+ if (left() && left()->print(out, context))
+ found = true;
+ break;
+
+ case O_ADD:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " + ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_SUB:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " - ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_MUL:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " * ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_DIV:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " / ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+
+ case O_NEQ:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " != ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_EQ:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " == ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_LT:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " < ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_LTE:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " <= ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_GT:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " > ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_GTE:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " >= ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+
+ case O_AND:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " & ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_OR:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " | ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+
+ case O_COMMA:
+ if (left() && left()->print(out, context))
+ found = true;
+ out << ", ";
+ if (right() && right()->print(out, context))
+ found = true;
+ break;
+
+ case O_CALL:
+ if (left() && left()->print(out, context))
+ found = true;
+ out << "(";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+
+ case O_MATCH:
+ out << '/';
+ if (left() && left()->print(out, context))
+ found = true;
+ out << "/ =~ ";
+ if (right() && right()->print(out, context))
+ found = true;
+ break;
+
+ case LAST:
+ default:
+ assert(false);
+ break;
+ }
+
+ if (! symbol.empty()) {
+ if (amount_t::current_pool->find(symbol))
+ out << '@';
+ out << symbol;
+ }
+
+ if (context.end_pos && this == context.op_to_find)
+ *context.end_pos = static_cast<unsigned long>(out.tellp()) - 1;
+
+ return found;
+}
+
+void expr_t::op_t::dump(std::ostream& out, const int depth) const
+{
+ out.setf(std::ios::left);
+ out.width(10);
+ out << this;
+
+ for (int i = 0; i < depth; i++)
+ out << " ";
+
+ switch (kind) {
+ case VALUE:
+ out << "VALUE - " << as_value();
+ break;
+
+ case IDENT:
+ out << "IDENT - " << as_ident();
+ break;
+
+ case INDEX:
+ out << "INDEX - " << as_index();
+ break;
+
+ case FUNCTION:
+ out << "FUNCTION";
+ break;
+
+ case O_CALL: out << "O_CALL"; break;
+ case O_MATCH: out << "O_MATCH"; break;
+
+ case O_NOT: out << "O_NOT"; break;
+ case O_NEG: out << "O_NEG"; break;
+
+ case O_ADD: out << "O_ADD"; break;
+ case O_SUB: out << "O_SUB"; break;
+ case O_MUL: out << "O_MUL"; break;
+ case O_DIV: out << "O_DIV"; break;
+
+ case O_NEQ: out << "O_NEQ"; break;
+ case O_EQ: out << "O_EQ"; break;
+ case O_LT: out << "O_LT"; break;
+ case O_LTE: out << "O_LTE"; break;
+ case O_GT: out << "O_GT"; break;
+ case O_GTE: out << "O_GTE"; break;
+
+ case O_AND: out << "O_AND"; break;
+ case O_OR: out << "O_OR"; break;
+
+ case O_COMMA: out << "O_COMMA"; break;
+
+ case LAST:
+ default:
+ assert(false);
+ break;
+ }
+
+ out << " (" << refc << ')' << std::endl;
+
+ if (kind > TERMINALS) {
+ if (left()) {
+ left()->dump(out, depth + 1);
+ if (right())
+ right()->dump(out, depth + 1);
+ } else {
+ assert(! right());
+ }
+ }
+}
+
+void expr_t::op_t::read(const char *& data)
+{
+#if 0
+ if (! read_bool(data))
+ return expr_t::ptr_op_t();
+
+ expr_t::op_t::kind_t kind;
+ read_number(data, kind);
+
+ expr_t::ptr_op_t expr = new expr_t::op_t(kind);
+
+ if (kind > expr_t::op_t::TERMINALS)
+ expr->set_left(read_value_expr(data));
+
+ switch (expr->kind) {
+ case expr_t::op_t::INDEX: {
+ long temp;
+ read_long(data, temp);
+ expr->set_index(temp);
+ break;
+ }
+ case expr_t::op_t::VALUE: {
+ value_t temp;
+ read_value(data, temp);
+ expr->set_value(temp);
+ break;
+ }
+
+ case expr_t::op_t::MASK:
+ if (read_bool(data))
+ read_mask(data, expr->as_mask_lval());
+ break;
+
+ default:
+ if (kind > expr_t::op_t::TERMINALS)
+ expr->set_right(read_value_expr(data));
+ break;
+ }
+
+ return expr;
+#endif
+}
+
+void expr_t::op_t::write(std::ostream& out) const
+{
+#if 0
+ if (! expr) {
+ write_bool(out, false);
+ return;
+ }
+
+ write_bool(out, true);
+ write_number(out, expr->kind);
+
+ if (expr->kind > expr_t::op_t::TERMINALS)
+ write_value_expr(out, expr->left());
+
+ switch (expr->kind) {
+ case expr_t::op_t::INDEX:
+ write_long(out, expr->as_index());
+ break;
+ case expr_t::op_t::IDENT:
+ write_long(out, expr->as_ident());
+ break;
+ case expr_t::op_t::VALUE:
+ write_value(out, expr->as_value());
+ break;
+
+ case expr_t::op_t::MASK:
+ if (expr->as_mask()) {
+ write_bool(out, true);
+ write_mask(out, expr->as_mask());
+ } else {
+ write_bool(out, false);
+ }
+ break;
+
+ default:
+ if (expr->kind > expr_t::op_t::TERMINALS)
+ write_value_expr(out, expr->right());
+ break;
+ }
+#endif
+}
+
+#if 0
+class op_predicate : public noncopyable
+{
+ ptr_op_t op;
+
+ op_predicate();
+
+public:
+ explicit op_predicate(ptr_op_t _op) : op(_op) {
+ TRACE_CTOR(op_predicate, "ptr_op_t");
+ }
+ ~op_predicate() throw() {
+ TRACE_DTOR(op_predicate);
+ }
+ bool operator()(scope_t& scope) {
+ return op->calc(scope).to_boolean();
+ }
+};
+
+#endif
+
+} // namespace ledger