ledger/op.cc
John Wiegley 9a9e06554e Formatting now relies exclusively on value expressions.
What this means is that the utility code, basic math, value expressions,
string formatting and option handling are now entirely decoupled from the rest
of the code.  This decoupling not only greatly simplifies the more basic parts
of Ledger, but makes it much easier to test and verify its completeness.

For example, when the formatting code %X is seen by the format parser, it
turns into a call to the expression function fmt_X, which must be defined when
the format string is first compiled against an object.  If that object is a
transaction, the transaction's scope will be the first to have a chance at
providing a definition.  If an account is being reported, it will.  If neither
does, the next scope in sequence -- soon to be the current report -- will, and
then the session object that "owns" the current Ledger session.

In 2.6, the formatting code new everything about transaction and accounts, and
relied on flags to communicate special details between them.  Now the
transaction will offer the details for its own reporting, while the formatter
worries only about strings and how to output them.
2008-08-02 06:42:36 -04:00

1172 lines
27 KiB
C++

/*
* 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 F_CODE_MASK:
if (details.entry && details.entry->code)
result = as_mask().match(*details.entry->code);
else
result = false;
break;
case F_PAYEE_MASK:
if (details.entry)
result = as_mask().match(details.entry->payee);
else
result = false;
break;
case F_NOTE_MASK:
if (details.xact && details.xact->note)
result = as_mask().match(*details.xact->note);
else
result = false;
break;
case F_ACCOUNT_MASK:
if (details.account)
result = as_mask().match(details.account->fullname());
else
result = false;
break;
case F_SHORT_ACCOUNT_MASK:
if (details.account)
result = as_mask().match(details.account->name);
else
result = false;
break;
case F_COMMODITY_MASK:
if (details.xact)
result = as_mask().match(details.xact->amount.commodity().base_symbol());
else
result = false;
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(left()->is_mask());
return left()->as_mask().match(right()->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