/* * 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" #include "parser.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; #if 0 case '$': in.get(c); kind = DOLLAR; break; #endif case '&': in.get(c); kind = KW_AND; break; case '(': in.get(c); kind = LPAREN; break; case ')': in.get(c); kind = RPAREN; break; case '[': { in.get(c); if (! (flags & EXPR_PARSE_NO_DATES)) { 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; #if 0 case '/': in.get(c); kind = SLASH; break; #endif 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'; in.get(c); 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 char buf[256]; READ_INTO_(in, buf, 255, c, length, c != '/'); if (c != '/') unexpected(c, '/'); in.get(c); length++; if (short_account_mask) kind = SHORT_ACCOUNT_MASK; else if (code_mask) kind = CODE_MASK; else if (commodity_mask) kind = COMMODITY_MASK; else if (payee_mask) kind = PAYEE_MASK; else if (note_mask) kind = NOTE_MASK; else kind = ACCOUNT_MASK; value.set_string(buf); 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 (const 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::SHORT_ACCOUNT_MASK: node = new op_t(op_t::F_SHORT_ACCOUNT_MASK); node->set_mask(tok.value.as_string()); break; case token_t::CODE_MASK: node = new op_t(op_t::F_CODE_MASK); node->set_mask(tok.value.as_string()); break; case token_t::COMMODITY_MASK: node = new op_t(op_t::F_COMMODITY_MASK); node->set_mask(tok.value.as_string()); break; case token_t::PAYEE_MASK: node = new op_t(op_t::F_PAYEE_MASK); node->set_mask(tok.value.as_string()); break; case token_t::NOTE_MASK: node = new op_t(op_t::F_NOTE_MASK); node->set_mask(tok.value.as_string()); break; case token_t::ACCOUNT_MASK: node = new op_t(op_t::F_ACCOUNT_MASK); node->set_mask(tok.value.as_string()); 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(ident.c_str())); } else if (optional 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; } #if 0 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"); string ident = tok.value.as_string(); if (optional 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); } 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"); node = new op_t(op_t::VAR_NAME); node->set_string(tok.value.as_string()); break; 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_lval().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_lval().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 << ""; 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 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 << ""; 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 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 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 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 << "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