ledger/parsexp.cc
John Wiegley d86a91d45b The new XPath parser has been integrated, although I have removed the
XML-related bits -- I just wanted the better infrastructure that had been
created during the rewrite.  It doesn't work, but it compiles and links now.
This means that all of the previous 3.0 code has been moved over, although
there are still snippets of code in pending/old that need to be restored.
2008-07-20 23:12:04 -04:00

2142 lines
50 KiB
C++

/*
* 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 "parsexp.h"
namespace ledger {
namespace expr {
void parser_t::token_t::parse_ident(std::istream& in)
{
if (in.eof()) {
kind = TOK_EOF;
return;
}
assert(in.good());
char c = peek_next_nonws(in);
if (in.eof()) {
kind = TOK_EOF;
return;
}
assert(in.good());
kind = IDENT;
length = 0;
char buf[256];
READ_INTO_(in, buf, 255, c, length,
std::isalnum(c) || c == '_' || c == '.' || c == '-');
switch (buf[0]) {
case 'a':
if (std::strcmp(buf, "and") == 0)
kind = KW_AND;
break;
case 'd':
if (std::strcmp(buf, "div") == 0)
kind = KW_DIV;
break;
case 'e':
if (std::strcmp(buf, "eq") == 0)
kind = EQUAL;
break;
case 'f':
if (std::strcmp(buf, "false") == 0) {
kind = VALUE;
value = false;
}
break;
case 'g':
if (std::strcmp(buf, "gt") == 0)
kind = GREATER;
else if (std::strcmp(buf, "ge") == 0)
kind = GREATEREQ;
break;
case 'i':
if (std::strcmp(buf, "is") == 0)
kind = EQUAL;
break;
case 'l':
if (std::strcmp(buf, "lt") == 0)
kind = LESS;
else if (std::strcmp(buf, "le") == 0)
kind = LESSEQ;
break;
case 'm':
if (std::strcmp(buf, "mod") == 0)
kind = KW_MOD;
break;
case 'n':
if (std::strcmp(buf, "ne") == 0)
kind = NEQUAL;
break;
case 'o':
if (std::strcmp(buf, "or") == 0)
kind = KW_OR;
break;
case 't':
if (std::strcmp(buf, "true") == 0) {
kind = VALUE;
value = true;
}
break;
#if 0
case 'u':
if (std::strcmp(buf, "union") == 0)
kind = KW_UNION;
break;
#endif
}
if (kind == IDENT)
value.set_string(buf);
}
void parser_t::token_t::next(std::istream& in, const flags_t flags)
{
if (in.eof()) {
kind = TOK_EOF;
return;
}
assert(in.good());
char c = peek_next_nonws(in);
if (in.eof()) {
kind = TOK_EOF;
return;
}
assert(in.good());
symbol[0] = c;
symbol[1] = '\0';
length = 1;
if (! (flags & EXPR_PARSE_RELAXED) &&
(std::isalpha(c) || c == '_')) {
parse_ident(in);
return;
}
switch (c) {
case '@':
in.get(c);
kind = AT_SYM;
break;
case '$':
in.get(c);
kind = DOLLAR;
break;
case '(':
in.get(c);
kind = LPAREN;
break;
case ')':
in.get(c);
kind = RPAREN;
break;
case '[': {
in.get(c);
if (flags & EXPR_PARSE_ALLOW_DATE) {
char buf[256];
READ_INTO_(in, buf, 255, c, length, c != ']');
if (c != ']')
unexpected(c, ']');
in.get(c);
length++;
interval_t timespan(buf);
kind = VALUE;
value = timespan.first();
} else {
kind = LBRACKET;
}
break;
}
case ']': {
in.get(c);
kind = RBRACKET;
break;
}
case '\'':
case '"': {
char delim;
in.get(delim);
char buf[4096];
READ_INTO_(in, buf, 4095, c, length, c != delim);
if (c != delim)
unexpected(c, delim);
in.get(c);
length++;
kind = VALUE;
value.set_string(buf);
break;
}
case '{': {
in.get(c);
amount_t temp;
temp.parse(in, AMOUNT_PARSE_NO_MIGRATE);
in.get(c);
if (c != '}')
unexpected(c, '}');
length++;
kind = VALUE;
value = temp;
break;
}
case '!':
in.get(c);
c = in.peek();
if (c == '=') {
in.get(c);
symbol[1] = c;
symbol[2] = '\0';
kind = NEQUAL;
length = 2;
break;
}
kind = EXCLAM;
break;
case '-':
in.get(c);
kind = MINUS;
break;
case '+':
in.get(c);
kind = PLUS;
break;
case '*':
in.get(c);
kind = STAR;
break;
case '/':
in.get(c);
kind = SLASH;
break;
case '=':
in.get(c);
kind = EQUAL;
break;
case '<':
in.get(c);
if (in.peek() == '=') {
in.get(c);
symbol[1] = c;
symbol[2] = '\0';
kind = LESSEQ;
length = 2;
break;
}
kind = LESS;
break;
case '>':
in.get(c);
if (in.peek() == '=') {
in.get(c);
symbol[1] = c;
symbol[2] = '\0';
kind = GREATEREQ;
length = 2;
break;
}
kind = GREATER;
break;
#if 0
case '|':
in.get(c);
kind = PIPE;
break;
#endif
case ',':
in.get(c);
kind = COMMA;
break;
case '.':
in.get(c);
c = in.peek();
if (c == '.') {
in.get(c);
length++;
kind = DOTDOT;
break;
}
else if (! std::isdigit(c)) {
kind = DOT;
break;
}
in.unget(); // put the first '.' back
// fall through...
default:
if (! (flags & EXPR_PARSE_RELAXED)) {
kind = UNKNOWN;
} else {
amount_t temp;
unsigned long pos = 0;
// When in relaxed parsing mode, we want to migrate commodity
// flags so that any precision specified by the user updates the
// current maximum displayed precision.
try {
pos = (long)in.tellg();
unsigned char parse_flags = 0;
if (flags & EXPR_PARSE_NO_MIGRATE)
parse_flags |= AMOUNT_PARSE_NO_MIGRATE;
if (flags & EXPR_PARSE_NO_REDUCE)
parse_flags |= AMOUNT_PARSE_NO_REDUCE;
temp.parse(in, parse_flags);
kind = VALUE;
value = temp;
}
catch (amount_error& err) {
// If the amount had no commodity, it must be an unambiguous
// variable reference
// jww (2007-04-19): There must be a more efficient way to do this!
if (std::strcmp(err.what(), "No quantity specified for amount") == 0) {
in.clear();
in.seekg(pos, std::ios::beg);
c = in.peek();
assert(! (std::isdigit(c) || c == '.'));
parse_ident(in);
} else {
throw;
}
}
}
break;
}
}
void parser_t::token_t::rewind(std::istream& in)
{
for (unsigned int i = 0; i < length; i++)
in.unget();
}
void parser_t::token_t::unexpected()
{
switch (kind) {
case TOK_EOF:
throw_(parse_error, "Unexpected end of expression");
case IDENT:
throw_(parse_error, "Unexpected symbol '" << value << "'");
case VALUE:
throw_(parse_error, "Unexpected value '" << value << "'");
default:
throw_(parse_error, "Unexpected operator '" << symbol << "'");
}
}
void parser_t::token_t::unexpected(char c, char wanted)
{
if ((unsigned char) c == 0xff) {
if (wanted)
throw_(parse_error, "Missing '" << wanted << "'");
else
throw_(parse_error, "Unexpected end");
} else {
if (wanted)
throw_(parse_error, "Invalid char '" << c <<
"' (wanted '" << wanted << "')");
else
throw_(parse_error, "Invalid char '" << c << "'");
}
}
ptr_op_t
parser_t::parse_value_term(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node;
token_t& tok = next_token(in, tflags);
switch (tok.kind) {
case token_t::VALUE:
node = new op_t(op_t::VALUE);
node->set_value(tok.value);
break;
case token_t::IDENT: {
#if 0
#ifdef USE_BOOST_PYTHON
if (tok.value->as_string() == "lambda") // special
try {
char c, buf[4096];
std::strcpy(buf, "lambda ");
READ_INTO(in, &buf[7], 4000, c, true);
ptr_op_t eval = new op_t(op_t::O_EVAL);
ptr_op_t lambda = new op_t(op_t::FUNCTION);
lambda->functor = new python_functor_t(python_eval(buf));
eval->set_left(lambda);
ptr_op_t sym = new op_t(op_t::SYMBOL);
sym->name = new string("__ptr");
eval->set_right(sym);
node = eval;
goto done;
}
catch(const boost::python::error_already_set&) {
throw_(parse_error, "Error parsing lambda expression");
}
#endif /* USE_BOOST_PYTHON */
#endif
string ident = tok.value.as_string();
// An identifier followed by ( represents a function call
tok = next_token(in, tflags);
#if 0
if (tok.kind == token_t::LPAREN) {
node = new op_t(op_t::FUNC_NAME);
node->set_string(ident);
ptr_op_t call_node(new op_t(op_t::O_CALL));
call_node->set_left(node);
call_node->set_right(parse_value_expr(in, scope, tflags | EXPR_PARSE_PARTIAL));
tok = next_token(in, tflags);
if (tok.kind != token_t::RPAREN)
tok.unexpected(0xff, ')');
node = call_node;
} else {
if (std::isdigit(ident[0])) {
node = new op_t(op_t::ARG_INDEX);
node->set_long(lexical_cast<unsigned int>(ident.c_str()));
}
else if (optional<node_t::nameid_t> id =
document_t::lookup_builtin_id(ident)) {
node = new op_t(op_t::NODE_ID);
node->set_name(*id);
}
else {
node = new op_t(op_t::NODE_NAME);
node->set_string(ident);
}
push_token(tok);
}
#endif
break;
}
case token_t::AT_SYM: {
tok = next_token(in, tflags);
if (tok.kind != token_t::IDENT)
throw_(parse_error, "@ symbol must be followed by attribute name");
#if 0
string ident = tok.value.as_string();
if (optional<node_t::nameid_t> id = document_t::lookup_builtin_id(ident)) {
node = new op_t(op_t::ATTR_ID);
node->set_name(*id);
}
else {
node = new op_t(op_t::ATTR_NAME);
node->set_string(ident);
}
#endif
break;
}
case token_t::DOLLAR:
tok = next_token(in, tflags);
if (tok.kind != token_t::IDENT)
throw parse_error("$ symbol must be followed by variable name");
#if 0
node = new op_t(op_t::VAR_NAME);
node->set_string(tok.value.as_string());
#endif
break;
#if 0
case token_t::DOT:
node = new op_t(op_t::NODE_ID);
node->set_name(document_t::CURRENT);
break;
case token_t::DOTDOT:
node = new op_t(op_t::NODE_ID);
node->set_name(document_t::PARENT);
break;
case token_t::SLASH:
node = new op_t(op_t::NODE_ID);
node->set_name(document_t::ROOT);
push_token();
break;
case token_t::STAR:
node = new op_t(op_t::NODE_ID);
node->set_name(document_t::ALL);
break;
#endif
case token_t::LPAREN:
node = new op_t(op_t::O_COMMA);
node->set_left(parse_value_expr(in, scope, tflags | EXPR_PARSE_PARTIAL));
if (! node->left())
throw_(parse_error, tok.symbol << " operator not followed by argument");
tok = next_token(in, tflags);
if (tok.kind != token_t::RPAREN)
tok.unexpected(0xff, ')');
break;
default:
push_token(tok);
break;
}
#if 0
#ifdef USE_BOOST_PYTHON
done:
#endif
#endif
return node;
}
ptr_op_t
parser_t::parse_unary_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node;
token_t& tok = next_token(in, tflags);
switch (tok.kind) {
case token_t::EXCLAM: {
ptr_op_t term(parse_value_term(in, scope, tflags));
if (! term)
throw_(parse_error,
tok.symbol << " operator not followed by argument");
// A very quick optimization
if (term->kind == op_t::VALUE) {
term->as_value().in_place_negate();
node = term;
} else {
node = new op_t(op_t::O_NOT);
node->set_left(term);
}
break;
}
case token_t::MINUS: {
ptr_op_t term(parse_value_term(in, scope, tflags));
if (! term)
throw_(parse_error,
tok.symbol << " operator not followed by argument");
// A very quick optimization
if (term->kind == op_t::VALUE) {
term->as_value().in_place_negate();
node = term;
} else {
node = new op_t(op_t::O_NEG);
node->set_left(term);
}
break;
}
default:
push_token(tok);
node = parse_value_term(in, scope, tflags);
break;
}
return node;
}
#if 0
ptr_op_t
parser_t::parse_union_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node(parse_unary_expr(in, scope, tflags));
if (node) {
token_t& tok = next_token(in, tflags);
if (tok.kind == token_t::PIPE || tok.kind == token_t::KW_UNION) {
ptr_op_t prev(node);
node = new op_t(op_t::O_UNION);
node->set_left(prev);
node->set_right(parse_union_expr(in, scope, tflags));
if (! node->right())
throw_(parse_error,
tok.symbol << " operator not followed by argument");
} else {
push_token(tok);
}
}
return node;
}
#endif
ptr_op_t
parser_t::parse_mul_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node(parse_unary_expr(in, scope, tflags));
if (node) {
token_t& tok = next_token(in, tflags);
if (tok.kind == token_t::STAR || tok.kind == token_t::KW_DIV) {
ptr_op_t prev(node);
node = new op_t(tok.kind == token_t::STAR ?
op_t::O_MUL : op_t::O_DIV);
node->set_left(prev);
node->set_right(parse_mul_expr(in, scope, tflags));
if (! node->right())
throw_(parse_error,
tok.symbol << " operator not followed by argument");
tok = next_token(in, tflags);
}
push_token(tok);
}
return node;
}
ptr_op_t
parser_t::parse_add_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node(parse_mul_expr(in, scope, tflags));
if (node) {
token_t& tok = next_token(in, tflags);
if (tok.kind == token_t::PLUS ||
tok.kind == token_t::MINUS) {
ptr_op_t prev(node);
node = new op_t(tok.kind == token_t::PLUS ?
op_t::O_ADD : op_t::O_SUB);
node->set_left(prev);
node->set_right(parse_add_expr(in, scope, tflags));
if (! node->right())
throw_(parse_error,
tok.symbol << " operator not followed by argument");
tok = next_token(in, tflags);
}
push_token(tok);
}
return node;
}
ptr_op_t
parser_t::parse_logic_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node(parse_add_expr(in, scope, tflags));
if (node) {
op_t::kind_t kind = op_t::LAST;
flags_t _flags = tflags;
token_t& tok = next_token(in, tflags);
switch (tok.kind) {
case token_t::EQUAL:
kind = op_t::O_EQ;
break;
case token_t::NEQUAL:
kind = op_t::O_NEQ;
break;
case token_t::LESS:
kind = op_t::O_LT;
break;
case token_t::LESSEQ:
kind = op_t::O_LTE;
break;
case token_t::GREATER:
kind = op_t::O_GT;
break;
case token_t::GREATEREQ:
kind = op_t::O_GTE;
break;
default:
push_token(tok);
break;
}
if (kind != op_t::LAST) {
ptr_op_t prev(node);
node = new op_t(kind);
node->set_left(prev);
node->set_right(parse_add_expr(in, scope, _flags));
if (! node->right()) {
if (tok.kind == token_t::PLUS)
throw_(parse_error,
tok.symbol << " operator not followed by argument");
else
throw_(parse_error,
tok.symbol << " operator not followed by argument");
}
}
}
return node;
}
ptr_op_t
parser_t::parse_and_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node(parse_logic_expr(in, scope, tflags));
if (node) {
token_t& tok = next_token(in, tflags);
if (tok.kind == token_t::KW_AND) {
ptr_op_t prev(node);
node = new op_t(op_t::O_AND);
node->set_left(prev);
node->set_right(parse_and_expr(in, scope, tflags));
if (! node->right())
throw_(parse_error,
tok.symbol << " operator not followed by argument");
} else {
push_token(tok);
}
}
return node;
}
ptr_op_t
parser_t::parse_or_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node(parse_and_expr(in, scope, tflags));
if (node) {
token_t& tok = next_token(in, tflags);
if (tok.kind == token_t::KW_OR) {
ptr_op_t prev(node);
node = new op_t(op_t::O_OR);
node->set_left(prev);
node->set_right(parse_or_expr(in, scope, tflags));
if (! node->right())
throw_(parse_error,
tok.symbol << " operator not followed by argument");
} else {
push_token(tok);
}
}
return node;
}
ptr_op_t
parser_t::parse_value_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
{
ptr_op_t node(parse_or_expr(in, scope, tflags));
if (node) {
token_t& tok = next_token(in, tflags);
if (tok.kind == token_t::COMMA) {
ptr_op_t prev(node);
node = new op_t(op_t::O_COMMA);
node->set_left(prev);
node->set_right(parse_value_expr(in, scope, tflags));
if (! node->right())
throw_(parse_error,
tok.symbol << " operator not followed by argument");
tok = next_token(in, tflags);
}
if (tok.kind != token_t::TOK_EOF) {
if (tflags & EXPR_PARSE_PARTIAL)
push_token(tok);
else
tok.unexpected();
}
}
else if (! (tflags & EXPR_PARSE_PARTIAL)) {
throw_(parse_error, "Failed to parse value expression");
}
return node;
}
#if 0
bool print(std::ostream& out, op_t::print_context_t& context) const
{
if (ptr)
ptr->print(out, context);
return true;
}
#endif
bool 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 = (long)out.tellp() - 1;
found = true;
}
string symbol;
switch (kind) {
case VALUE: {
const value_t& value(as_value());
switch (value.type()) {
case value_t::VOID:
out << "<VOID>";
break;
case value_t::BOOLEAN:
if (value)
out << "1";
else
out << "0";
break;
case value_t::INTEGER:
out << value;
break;
case value_t::AMOUNT:
if (! context.relaxed)
out << '{';
out << value;
if (! context.relaxed)
out << '}';
break;
case value_t::BALANCE:
case value_t::BALANCE_PAIR:
assert(false);
break;
case value_t::DATETIME:
out << '[' << value << ']';
break;
case value_t::STRING:
out << '"' << value << '"';
break;
#if 0
case value_t::XML_NODE:
out << '<' << value << '>';
break;
#endif
case value_t::POINTER:
out << '&' << value;
break;
case value_t::SEQUENCE:
out << '~' << value << '~';
break;
}
break;
}
#if 0
case ATTR_ID:
out << '@';
// fall through...
case NODE_ID: {
context_scope_t& node_scope(CONTEXT_SCOPE(context.scope));
if (optional<const char *> name =
node_scope.xml_node().document().lookup_name(as_name()))
out << *name;
else
out << '#' << as_name();
break;
}
#endif
#if 0
case NODE_NAME:
case FUNC_NAME:
out << as_string();
break;
case ATTR_NAME:
out << '@' << as_string();
break;
case VAR_NAME:
out << '$' << as_string();
break;
#endif
case FUNCTION:
out << "<FUNCTION>";
break;
case ARG_INDEX:
out << '@' << as_long();
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;
#if 0
case O_UNION:
if (left() && left()->print(out, context))
found = true;
out << " | ";
if (right() && right()->print(out, context))
found = true;
break;
#endif
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;
#if 0
case O_CALL:
if (left() && left()->print(out, context))
found = true;
out << "(";
if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_FIND:
if (left() && left()->print(out, context))
found = true;
out << "/";
if (right() && right()->print(out, context))
found = true;
break;
case O_RFIND:
if (left() && left()->print(out, context))
found = true;
out << "//";
if (right() && right()->print(out, context))
found = true;
break;
case O_PRED:
if (left() && left()->print(out, context))
found = true;
out << "[";
if (right() && right()->print(out, context))
found = true;
out << "]";
break;
#endif
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 = (long)out.tellp() - 1;
return found;
}
void 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;
#if 0
case NODE_NAME:
out << "NODE_NAME - " << as_string();
break;
case NODE_ID:
out << "NODE_ID - " << as_name();
break;
case ATTR_NAME:
out << "ATTR_NAME - " << as_string();
break;
case ATTR_ID:
out << "ATTR_ID - " << as_name();
break;
case FUNC_NAME:
out << "FUNC_NAME - " << as_string();
break;
case VAR_NAME:
out << "VAR_NAME - " << as_string();
break;
#endif
case ARG_INDEX:
out << "ARG_INDEX - " << as_long();
break;
case FUNCTION:
out << "FUNCTION";
break;
#if 0
case O_CALL: out << "O_CALL"; break;
#endif
case O_NOT: out << "O_NOT"; break;
case O_NEG: out << "O_NEG"; break;
#if 0
case O_UNION: out << "O_UNION"; break;
#endif
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;
#if 0
case O_FIND: out << "O_FIND"; break;
case O_RFIND: out << "O_RFIND"; break;
case O_PRED: out << "O_PRED"; break;
#endif
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());
}
}
}
#if 0
ptr_op_t parse_value_term(std::istream& in, scope_t * scope,
const short flags)
{
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 op_t(op_t::VALUE));
node->set_value(temp);
goto parsed;
}
}
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 op_t(op_t::VALUE));
node->set_value(temp);
goto parsed;
}
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 (definition) {
std::auto_ptr<call_scope_t> params(new call_scope_t(*scope));
long 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 op_t(op_t::O_ARG));
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(arg_index++);
params->define(ident, node.release());
}
if (peek_next_nonws(in) != '=') {
in.get(c);
unexpected(c, '=');
}
in.get(c);
}
#if 0
// Define the value associated with the defined identifier
value_expr def(parse_boolean_expr(in, scope, params.get(), flags));
if (! def.get())
throw new value_expr_error(string("Definition failed for '") + buf + "'");
node.reset(new op_t(op_t::O_DEF));
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(arg_index);
node->set_right(def.release());
#endif
scope->define(buf, node.get());
} else {
assert(scope);
ptr_op_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(string("Unknown identifier '") +
buf + "'");
}
else if (def->kind == op_t::O_DEF) {
node.reset(new op_t(op_t::O_REF));
node->set_left(def->right());
unsigned 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());
}
}
if (count != def->left()->as_long()) {
std::ostringstream errmsg;
errmsg << "Wrong number of arguments to '" << buf
<< "': saw " << count
<< ", wanted " << def->left()->as_long();
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 op_t(op_t::F_PARENT));
node->set_left(parse_value_term(in, scope, flags));
break;
// Other
#if 0
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;
}
}
} else {
in.get(c);
}
// Read in the regexp
READ_INTO(in, buf, 255, c, c != '/');
if (c != '/')
unexpected(c, '/');
op_t::kind_t kind;
if (short_account_mask)
kind = op_t::F_SHORT_ACCOUNT_MASK;
else if (code_mask)
kind = op_t::F_CODE_MASK;
else if (commodity_mask)
kind = op_t::F_COMMODITY_MASK;
else if (payee_mask)
kind = op_t::F_PAYEE_MASK;
else if (note_mask)
kind = op_t::F_NOTE_MASK;
else
kind = op_t::F_ACCOUNT_MASK;
in.get(c);
node.reset(new op_t(kind));
node->mask = new mask_t(buf);
break;
}
#endif
case '{': {
amount_t temp;
temp.parse(in, AMOUNT_PARSE_NO_MIGRATE);
in.get(c);
if (c != '}')
unexpected(c, '}');
node.reset(new op_t(op_t::VALUE));
node->set_value(temp);
break;
}
case '(': {
std::auto_ptr<symbol_scope_t> locals(new symbol_scope_t(*scope));
node.reset(parse_value_expr(in, locals.get(),
flags | PARSE_VALEXPR_PARTIAL));
in.get(c);
if (c != ')')
unexpected(c, ')');
break;
}
case '[': {
READ_INTO(in, buf, 255, c, c != ']');
if (c != ']')
unexpected(c, ']');
in.get(c);
interval_t timespan(buf);
node.reset(new op_t(op_t::VALUE));
node->set_value(timespan.first());
break;
}
default:
in.unget();
break;
}
parsed:
return node.release();
}
void init_value_expr()
{
global_scope.reset(new symbol_scope_t());
symbol_scope_t * globals = global_scope.get();
ptr_op_t node;
// Basic terms
node = new op_t(op_t::F_NOW);
globals->define("m", node);
globals->define("now", node);
globals->define("today", node);
node = new op_t(op_t::AMOUNT);
globals->define("a", node);
globals->define("amount", node);
node = new op_t(op_t::PRICE);
globals->define("i", node);
globals->define("price", node);
node = new op_t(op_t::COST);
globals->define("b", node);
globals->define("cost", node);
node = new op_t(op_t::DATE);
globals->define("d", node);
globals->define("date", node);
node = new op_t(op_t::ACT_DATE);
globals->define("act_date", node);
globals->define("actual_date", node);
node = new op_t(op_t::EFF_DATE);
globals->define("eff_date", node);
globals->define("effective_date", node);
node = new op_t(op_t::CLEARED);
globals->define("X", node);
globals->define("cleared", node);
node = new op_t(op_t::PENDING);
globals->define("Y", node);
globals->define("pending", node);
node = new op_t(op_t::REAL);
globals->define("R", node);
globals->define("real", node);
node = new op_t(op_t::ACTUAL);
globals->define("L", node);
globals->define("actual", node);
node = new op_t(op_t::INDEX);
globals->define("n", node);
globals->define("index", node);
node = new op_t(op_t::COUNT);
globals->define("N", node);
globals->define("count", node);
node = new op_t(op_t::DEPTH);
globals->define("l", node);
globals->define("depth", node);
node = new op_t(op_t::TOTAL);
globals->define("O", node);
globals->define("total", node);
node = new op_t(op_t::PRICE_TOTAL);
globals->define("I", node);
globals->define("total_price", node);
node = new op_t(op_t::COST_TOTAL);
globals->define("B", node);
globals->define("total_cost", node);
// Relating to format_t
globals->define("t", ptr_op_t(new op_t(op_t::VALUE_EXPR)));
globals->define("T", ptr_op_t(new op_t(op_t::TOTAL_EXPR)));
// Functions
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_ABS));
globals->define("U", node);
globals->define("abs", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_ROUND));
globals->define("round", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_QUANTITY));
globals->define("S", node);
globals->define("quant", node);
globals->define("quantity", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_COMMODITY));
globals->define("comm", node);
globals->define("commodity", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(2);
node->set_right(new op_t(op_t::F_SET_COMMODITY));
globals->define("setcomm", node);
globals->define("set_commodity", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_ARITH_MEAN));
globals->define("A", node);
globals->define("avg", node);
globals->define("mean", node);
globals->define("average", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(2);
node->set_right(new op_t(op_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 op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_PRICE));
globals->define("priceof", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_DATE));
globals->define("dateof", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(2);
node->set_right(new op_t(op_t::F_DATECMP));
globals->define("datecmp", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_YEAR));
globals->define("yearof", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_t::F_MONTH));
globals->define("monthof", node);
node = new op_t(op_t::O_DEF);
node->set_left(new op_t(op_t::ARG_INDEX));
node->left()->set_long(1);
node->set_right(new op_t(op_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);
}
bool print_value_expr(std::ostream& out,
const ptr_op_t node,
const bool relaxed,
const ptr_op_t op_to_find,
unsigned long * start_pos,
unsigned long * end_pos)
{
bool found = false;
if (start_pos && node == op_to_find) {
*start_pos = (long)out.tellp() - 1;
found = true;
}
string symbol;
switch (node->kind) {
case op_t::ARG_INDEX:
out << node->as_long();
break;
case op_t::VALUE:
switch (node->as_value().type()) {
case value_t::BOOLEAN:
assert(false);
break;
case value_t::DATETIME:
out << '[' << node->as_value().as_datetime() << ']';
break;
case value_t::INTEGER:
case value_t::AMOUNT:
if (! relaxed)
out << '{';
out << node->as_value();
if (! relaxed)
out << '}';
break;
//case value_t::BALANCE:
//case value_t::BALANCE_PAIR:
default:
assert(false);
break;
}
break;
case op_t::AMOUNT:
symbol = "amount"; break;
case op_t::PRICE:
symbol = "price"; break;
case op_t::COST:
symbol = "cost"; break;
case op_t::DATE:
symbol = "date"; break;
case op_t::ACT_DATE:
symbol = "actual_date"; break;
case op_t::EFF_DATE:
symbol = "effective_date"; break;
case op_t::CLEARED:
symbol = "cleared"; break;
case op_t::PENDING:
symbol = "pending"; break;
case op_t::REAL:
symbol = "real"; break;
case op_t::ACTUAL:
symbol = "actual"; break;
case op_t::INDEX:
symbol = "index"; break;
case op_t::COUNT:
symbol = "count"; break;
case op_t::DEPTH:
symbol = "depth"; break;
case op_t::TOTAL:
symbol = "total"; break;
case op_t::PRICE_TOTAL:
symbol = "total_price"; break;
case op_t::COST_TOTAL:
symbol = "total_cost"; break;
case op_t::F_NOW:
symbol = "now"; break;
case op_t::VALUE_EXPR:
if (print_value_expr(out, amount_expr.get(), relaxed,
op_to_find, start_pos, end_pos))
found = true;
break;
case op_t::TOTAL_EXPR:
if (print_value_expr(out, total_expr.get(), relaxed,
op_to_find, start_pos, end_pos))
found = true;
break;
case op_t::F_ARITH_MEAN:
symbol = "average"; break;
case op_t::F_ABS:
symbol = "abs"; break;
case op_t::F_QUANTITY:
symbol = "quantity"; break;
case op_t::F_COMMODITY:
symbol = "commodity"; break;
case op_t::F_SET_COMMODITY:
symbol = "set_commodity"; break;
case op_t::F_VALUE:
symbol = "valueof"; break;
case op_t::F_PRICE:
symbol = "priceof"; break;
case op_t::F_DATE:
symbol = "dateof"; break;
case op_t::F_DATECMP:
symbol = "datecmp"; break;
case op_t::F_YEAR:
symbol = "yearof"; break;
case op_t::F_MONTH:
symbol = "monthof"; break;
case op_t::F_DAY:
symbol = "dayof"; break;
#if 0
case op_t::F_CODE_MASK:
out << "c/" << node->mask->expr.str() << "/";
break;
case op_t::F_PAYEE_MASK:
out << "p/" << node->mask->expr.str() << "/";
break;
case op_t::F_NOTE_MASK:
out << "e/" << node->mask->expr.str() << "/";
break;
case op_t::F_ACCOUNT_MASK:
out << "W/" << node->mask->expr.str() << "/";
break;
case op_t::F_SHORT_ACCOUNT_MASK:
out << "w/" << node->mask->expr.str() << "/";
break;
case op_t::F_COMMODITY_MASK:
out << "C/" << node->mask->expr.str() << "/";
break;
#endif
case op_t::O_NOT:
out << "!";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
case op_t::O_NEG:
out << "-";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
case op_t::O_PERC:
out << "%";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
case op_t::O_ARG:
out << "@arg" << node->as_long();
break;
case op_t::O_DEF:
out << "<def args=\"";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << "\" value=\"";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << "\">";
break;
case op_t::O_REF:
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
if (node->right()) {
out << "(";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
}
break;
case op_t::O_COM:
if (node->left() &&
print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ", ";
if (node->right() &&
print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
case op_t::O_QUES:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " ? ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_COL:
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " : ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
break;
case op_t::O_AND:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " & ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_OR:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " | ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_NEQ:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " != ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_EQ:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " == ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_LT:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " < ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_LTE:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " <= ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_GT:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " > ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_GTE:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " >= ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_ADD:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " + ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_SUB:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " - ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_MUL:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " * ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::O_DIV:
out << "(";
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << " / ";
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
found = true;
out << ")";
break;
case op_t::LAST:
default:
assert(false);
break;
}
if (! symbol.empty()) {
if (amount_t::current_pool->find(symbol))
out << '@';
out << symbol;
}
if (end_pos && node == op_to_find)
*end_pos = (long)out.tellp() - 1;
return found;
}
void dump_value_expr(std::ostream& out, const ptr_op_t node,
const int depth)
{
out.setf(std::ios::left);
out.width(10);
out << node << " ";
for (int i = 0; i < depth; i++)
out << " ";
switch (node->kind) {
case op_t::ARG_INDEX:
out << "ARG_INDEX - " << node->as_long();
break;
case op_t::VALUE:
out << "VALUE - " << node->as_value();
break;
case op_t::AMOUNT: out << "AMOUNT"; break;
case op_t::PRICE: out << "PRICE"; break;
case op_t::COST: out << "COST"; break;
case op_t::DATE: out << "DATE"; break;
case op_t::ACT_DATE: out << "ACT_DATE"; break;
case op_t::EFF_DATE: out << "EFF_DATE"; break;
case op_t::CLEARED: out << "CLEARED"; break;
case op_t::PENDING: out << "PENDING"; break;
case op_t::REAL: out << "REAL"; break;
case op_t::ACTUAL: out << "ACTUAL"; break;
case op_t::INDEX: out << "INDEX"; break;
case op_t::COUNT: out << "COUNT"; break;
case op_t::DEPTH: out << "DEPTH"; break;
case op_t::TOTAL: out << "TOTAL"; break;
case op_t::PRICE_TOTAL: out << "PRICE_TOTAL"; break;
case op_t::COST_TOTAL: out << "COST_TOTAL"; break;
case op_t::VALUE_EXPR: out << "VALUE_EXPR"; break;
case op_t::TOTAL_EXPR: out << "TOTAL_EXPR"; break;
case op_t::F_NOW: out << "F_NOW"; break;
case op_t::F_ARITH_MEAN: out << "F_ARITH_MEAN"; break;
case op_t::F_ABS: out << "F_ABS"; break;
case op_t::F_QUANTITY: out << "F_QUANTITY"; break;
case op_t::F_COMMODITY: out << "F_COMMODITY"; break;
case op_t::F_SET_COMMODITY: out << "F_SET_COMMODITY"; break;
case op_t::F_CODE_MASK: out << "F_CODE_MASK"; break;
case op_t::F_PAYEE_MASK: out << "F_PAYEE_MASK"; break;
case op_t::F_NOTE_MASK: out << "F_NOTE_MASK"; break;
case op_t::F_ACCOUNT_MASK:
out << "F_ACCOUNT_MASK"; break;
case op_t::F_SHORT_ACCOUNT_MASK:
out << "F_SHORT_ACCOUNT_MASK"; break;
case op_t::F_COMMODITY_MASK:
out << "F_COMMODITY_MASK"; break;
case op_t::F_VALUE: out << "F_VALUE"; break;
case op_t::F_PRICE: out << "F_PRICE"; break;
case op_t::F_DATE: out << "F_DATE"; break;
case op_t::F_DATECMP: out << "F_DATECMP"; break;
case op_t::F_YEAR: out << "F_YEAR"; break;
case op_t::F_MONTH: out << "F_MONTH"; break;
case op_t::F_DAY: out << "F_DAY"; break;
case op_t::O_NOT: out << "O_NOT"; break;
case op_t::O_ARG: out << "O_ARG"; break;
case op_t::O_DEF: out << "O_DEF"; break;
case op_t::O_REF: out << "O_REF"; break;
case op_t::O_COM: out << "O_COM"; break;
case op_t::O_QUES: out << "O_QUES"; break;
case op_t::O_COL: out << "O_COL"; break;
case op_t::O_AND: out << "O_AND"; break;
case op_t::O_OR: out << "O_OR"; break;
case op_t::O_NEQ: out << "O_NEQ"; break;
case op_t::O_EQ: out << "O_EQ"; break;
case op_t::O_LT: out << "O_LT"; break;
case op_t::O_LTE: out << "O_LTE"; break;
case op_t::O_GT: out << "O_GT"; break;
case op_t::O_GTE: out << "O_GTE"; break;
case op_t::O_NEG: out << "O_NEG"; break;
case op_t::O_ADD: out << "O_ADD"; break;
case op_t::O_SUB: out << "O_SUB"; break;
case op_t::O_MUL: out << "O_MUL"; break;
case op_t::O_DIV: out << "O_DIV"; break;
case op_t::O_PERC: out << "O_PERC"; break;
case op_t::LAST:
default:
assert(false);
break;
}
out << " (" << node->refc << ')' << std::endl;
if (node->kind > op_t::TERMINALS) {
dump_value_expr(out, node->left(), depth + 1);
if (node->right())
dump_value_expr(out, node->right(), depth + 1);
}
}
#endif
} // namespace expr
} // namespace ledger