diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | binary.cc | 12 | ||||
-rw-r--r-- | format.cc | 12 | ||||
-rw-r--r-- | ledger.h | 1 | ||||
-rw-r--r-- | parsexp.cc | 2142 | ||||
-rw-r--r-- | parsexp.h | 280 | ||||
-rw-r--r-- | session.cc | 7 | ||||
-rw-r--r-- | textual.cc | 14 | ||||
-rw-r--r-- | times.cc | 5 | ||||
-rw-r--r-- | times.h | 19 | ||||
-rw-r--r-- | utils.cc | 2 | ||||
-rw-r--r-- | utils.h | 2 | ||||
-rw-r--r-- | valexpr.cc | 1420 | ||||
-rw-r--r-- | valexpr.h | 181 |
14 files changed, 2797 insertions, 1302 deletions
diff --git a/Makefile.am b/Makefile.am index 934d0e91..97b6c015 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ libledger_la_SOURCES = \ mask.cc \ option.cc \ parser.cc \ + parsexp.cc \ qif.cc \ reconcile.cc \ report.cc \ @@ -94,6 +95,7 @@ pkginclude_HEADERS = \ mask.h \ option.h \ parser.h \ + parsexp.h \ qif.h \ quotes.h \ reconcile.h \ @@ -354,13 +354,13 @@ inline void read_transaction(const char *& data, transaction_t * xact) } else if (flag == 1) { read_amount(data, xact->amount); - read_string(data, xact->amount_expr.expr); + read_string(data, xact->amount_expr.expr_str); } else { expr::ptr_op_t ptr = read_value_expr(data); assert(ptr.get()); xact->amount_expr.reset(ptr); - read_string(data, xact->amount_expr.expr); + read_string(data, xact->amount_expr.expr_str); } if (read_bool(data)) { @@ -912,12 +912,12 @@ void write_transaction(std::ostream& out, transaction_t * xact, else if (xact->amount_expr) { write_number<unsigned char>(out, 2); write_value_expr(out, xact->amount_expr.get()); - write_string(out, xact->amount_expr.expr); + write_string(out, xact->amount_expr.expr_str); } - else if (! xact->amount_expr.expr.empty()) { + else if (! xact->amount_expr.expr_str.empty()) { write_number<unsigned char>(out, 1); write_amount(out, xact->amount); - write_string(out, xact->amount_expr.expr); + write_string(out, xact->amount_expr.expr_str); } else { write_number<unsigned char>(out, 0); @@ -928,7 +928,7 @@ void write_transaction(std::ostream& out, transaction_t * xact, (! (ignore_calculated && xact->has_flags(TRANSACTION_CALCULATED)))) { write_bool(out, true); write_amount(out, *xact->cost); - write_string(out, xact->cost_expr->expr); + write_string(out, xact->cost_expr->expr_str); } else { write_bool(out, false); } @@ -463,13 +463,13 @@ void format_t::format(std::ostream& out_str, const details_t& details) const if (details.xact->cost && details.xact->amount) { std::ostringstream stream; - if (! details.xact->amount_expr.expr.empty()) - stream << details.xact->amount_expr.expr; + if (! details.xact->amount_expr.expr_str.empty()) + stream << details.xact->amount_expr.expr_str; else stream << details.xact->amount.strip_annotations(); if (details.xact->cost_expr) - stream << details.xact->cost_expr->expr; + stream << details.xact->cost_expr->expr_str; else stream << " @ " << amount_t(*details.xact->cost / details.xact->amount).unround(); @@ -498,8 +498,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const } if (! use_disp) { - if (! details.xact->amount_expr.expr.empty()) - out << details.xact->amount_expr.expr; + if (! details.xact->amount_expr.expr_str.empty()) + out << details.xact->amount_expr.expr_str; else out << details.xact->amount.strip_annotations(); } else { @@ -828,7 +828,7 @@ void print_entry(std::ostream& out, const entry_base_t& entry_base, } else if (const auto_entry_t * entry = dynamic_cast<const auto_entry_t *>(&entry_base)) { - out << "= " << entry->predicate.predicate.expr << '\n'; + out << "= " << entry->predicate.predicate.expr_str << '\n'; print_format = prefix + " %-34A %12o\n"; } else if (const period_entry_t * entry = @@ -52,6 +52,7 @@ #include <csv.h> //#include <quotes.h> #include <valexpr.h> +#include <parsexp.h> #include <walk.h> #include <derive.h> #include <reconcile.h> diff --git a/parsexp.cc b/parsexp.cc new file mode 100644 index 00000000..e076c0b1 --- /dev/null +++ b/parsexp.cc @@ -0,0 +1,2142 @@ +/* + * 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 diff --git a/parsexp.h b/parsexp.h new file mode 100644 index 00000000..0ccf09ed --- /dev/null +++ b/parsexp.h @@ -0,0 +1,280 @@ +/* + * 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. + */ + +#ifndef _PARSEXP_H +#define _PARSEXP_H + +#include "valexpr.h" + +namespace ledger { +namespace expr { + +DECLARE_EXCEPTION(error, parse_error); + +class parser_t +{ +#define EXPR_PARSE_NORMAL 0x00 +#define EXPR_PARSE_PARTIAL 0x01 +#define EXPR_PARSE_RELAXED 0x02 +#define EXPR_PARSE_NO_MIGRATE 0x04 +#define EXPR_PARSE_NO_REDUCE 0x08 +#define EXPR_PARSE_ALLOW_DATE 0x10 + +public: + typedef uint_least8_t flags_t; + +private: + struct token_t + { + enum kind_t { + VALUE, // any kind of literal value + + IDENT, // [A-Za-z_][-A-Za-z0-9_:]* + DOLLAR, // $ + AT_SYM, // @ + + DOT, // . + DOTDOT, // .. + SLASH, // / + + LPAREN, // ( + RPAREN, // ) + LBRACKET, // [ + RBRACKET, // ] + + EQUAL, // = + NEQUAL, // != + LESS, // < + LESSEQ, // <= + GREATER, // > + GREATEREQ, // >= + + MINUS, // - + PLUS, // + + STAR, // * + KW_DIV, + + EXCLAM, // ! + KW_AND, + KW_OR, + KW_MOD, + +#if 0 + PIPE, // | + KW_UNION, +#endif + + COMMA, // , + + TOK_EOF, + UNKNOWN + } kind; + + char symbol[3]; + value_t value; + std::size_t length; + + explicit token_t() : kind(UNKNOWN), length(0) { + TRACE_CTOR(token_t, ""); + } + token_t(const token_t& other) { + assert(false); + TRACE_CTOR(token_t, "copy"); + *this = other; + } + ~token_t() { + TRACE_DTOR(token_t); + } + + token_t& operator=(const token_t& other) { + if (&other == this) + return *this; + assert(false); + return *this; + } + + void clear() { + kind = UNKNOWN; + length = 0; + value = NULL_VALUE; + + symbol[0] = '\0'; + symbol[1] = '\0'; + symbol[2] = '\0'; + } + + void parse_ident(std::istream& in); + void next(std::istream& in, flags_t flags); + void rewind(std::istream& in); + void unexpected(); + + static void unexpected(char c, char wanted = '\0'); + }; + + mutable token_t lookahead; + mutable bool use_lookahead; + + token_t& next_token(std::istream& in, flags_t tflags) const + { + if (use_lookahead) + use_lookahead = false; + else + lookahead.next(in, tflags); + return lookahead; + } + + void push_token(const token_t& tok) const + { + assert(&tok == &lookahead); + use_lookahead = true; + } + void push_token() const + { + use_lookahead = true; + } + +public: + value_expr expr; + +private: + ptr_op_t parse_value_term(std::istream& in, scope_t& scope, + const flags_t flags) const; +#if 0 + ptr_op_t parse_predicate_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; + ptr_op_t parse_path_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; +#endif + ptr_op_t parse_unary_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; +#if 0 + ptr_op_t parse_union_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; +#endif + ptr_op_t parse_mul_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; + ptr_op_t parse_add_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; + ptr_op_t parse_logic_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; + ptr_op_t parse_and_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; + ptr_op_t parse_or_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; + ptr_op_t parse_querycolon_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; + ptr_op_t parse_value_expr(std::istream& in, scope_t& scope, + const flags_t flags) const; + + void parse_expr(std::istream& in, string& str, + scope_t& scope, const flags_t flags) { + try { + ptr_op_t top_node = parse_value_expr(in, scope, flags); + expr = value_expr(top_node, str); + +#if 0 + // jww (2008-07-20): This code should no longer be needed, since we + // can't re-use parser_t anymore. + if (use_lookahead) { + use_lookahead = false; +#ifdef THREADSAFE + lookahead.rewind(in); +#else + lookahead->rewind(in); +#endif + } +#ifdef THREADSAFE + lookahead.clear(); +#else + lookahead->clear(); +#endif +#endif + } + catch (error * err) { + err->context.push_back + (new line_context(str, (long)in.tellg() - 1, + "While parsing value expression:")); + throw err; + } + } + +public: + parser_t(std::istream& in, const flags_t flags = EXPR_PARSE_RELAXED) + : use_lookahead(false) + { + TRACE_CTOR(parser_t, "std::istream&, const flags_t"); + parse_expr(in, empty_string, *global_scope, flags); + } + parser_t(std::istream& in, const flags_t flags = EXPR_PARSE_RELAXED, scope_t& scope) + : use_lookahead(false) + { + TRACE_CTOR(parser_t, "std::istream&, const flags_t, scope_t&"); + parse_expr(in, empty_string, scope, flags); + } + parser_t(string& str, const flags_t flags = EXPR_PARSE_RELAXED) + : use_lookahead(false) + { + TRACE_CTOR(parser_t, "string&, const flags_t"); + + std::istringstream stream(str); + parse_expr(stream, str, *global_scope, flags); + } + parser_t(string& str, const flags_t flags = EXPR_PARSE_RELAXED, scope_t& scope) + : use_lookahead(false) + { + TRACE_CTOR(parser_t, "string&, const flags_t, scope_t&"); + + std::istringstream stream(str); + parse_expr(stream, str, scope, flags); + } + + ~parser_t() throw() { + TRACE_DTOR(parser_t); + } + + operator value_expr() const { + return expr; + } +}; + +void dump(std::ostream& out, const ptr_op_t node, const int depth = 0); + +bool print(std::ostream& out, + const ptr_op_t node, + const bool relaxed = true, + const ptr_op_t node_to_find = NULL, + unsigned long * start_pos = NULL, + unsigned long * end_pos = NULL); + +} // namespace expr +} // namespace ledger + +#endif // _PARESXP_H @@ -30,6 +30,7 @@ */ #include "session.h" +#include "parsexp.h" namespace ledger { @@ -306,16 +307,10 @@ static void initialize() { amount_t::initialize(); value_t::initialize(); -#if 0 - expr::initialize(); -#endif } static void shutdown() { -#if 0 - expr::shutdown(); -#endif value_t::shutdown(); amount_t::shutdown(); } @@ -45,8 +45,8 @@ static value_expr parse_amount_expr(std::istream& in, amount_t& amount, transaction_t * xact, unsigned short flags = 0) { - value_expr expr(expr::parse_value_expr(in, NULL, flags | PARSE_VALEXPR_RELAXED | - PARSE_VALEXPR_PARTIAL)); + value_expr expr(expr::parser_t(in, flags | EXPR_PARSE_RELAXED | + EXPR_PARSE_PARTIAL)); DEBUG("ledger.textual.parse", "line " << linenum << ": " << "Parsed an amount expression"); @@ -172,7 +172,7 @@ transaction_t * parse_transaction(char * line, account_t * account, xact->amount_expr = parse_amount_expr(in, xact->amount, xact.get(), - PARSE_VALEXPR_NO_REDUCE); + EXPR_PARSE_NO_REDUCE); saw_amount = true; xact->amount.reduce(); @@ -180,7 +180,7 @@ transaction_t * parse_transaction(char * line, account_t * account, "Reduced amount is " << xact->amount); unsigned long end = (long)in.tellg(); - xact->amount_expr.expr = string(line, beg, end - beg); + xact->amount_expr.expr_str = string(line, beg, end - beg); } catch (error * err) { err_desc = "While parsing transaction amount:"; @@ -215,7 +215,7 @@ transaction_t * parse_transaction(char * line, account_t * account, unsigned long beg = (long)in.tellg(); if (parse_amount_expr(in, *xact->cost, xact.get(), - PARSE_VALEXPR_NO_MIGRATE)) + EXPR_PARSE_NO_MIGRATE)) throw new parse_error ("A transaction's cost must evaluate to a constant value"); @@ -845,8 +845,8 @@ unsigned int textual_parser_t::parse(std::istream& in, #if 0 if (! expr::global_scope.get()) init_value_expr(); -#endif parse_value_definition(p); +#endif } break; } @@ -984,7 +984,7 @@ void write_textual_journal(journal_t& journal, path pathname, base = *el++; } else if (al != journal.auto_entries.end() && pos == (*al)->beg_pos) { - out << "= " << (*al)->predicate.predicate.expr << '\n'; + out << "= " << (*al)->predicate.predicate.expr_str << '\n'; base = *al++; } else if (pl != journal.period_entries.end() && pos == (*pl)->beg_pos) { @@ -124,8 +124,10 @@ datetime_t interval_t::first(const datetime_t& moment) const { datetime_t quant(begin); + if (! advanced) + advanced = true; + #if 0 - // jww (2008-05-08): Implement if (is_valid(moment) && moment > quant) { // Find an efficient starting point for the upcoming while loop. // We want a date early enough that the range will be correct, but @@ -162,7 +164,6 @@ datetime_t interval_t::increment(const datetime_t& moment) const #if 0 struct std::tm * desc = std::localtime(&moment.when); - // jww (2008-05-08): Implement if (years) desc->tm_year += years; if (months) @@ -72,23 +72,33 @@ struct interval_t unsigned short minutes; unsigned short seconds; - datetime_t begin; - datetime_t end; + datetime_t begin; + datetime_t end; + + mutable bool advanced; interval_t(int _days = 0, int _months = 0, int _years = 0, const datetime_t& _begin = datetime_t(), const datetime_t& _end = datetime_t()) : years(_years), months(_months), days(_days), hours(0), minutes(0), seconds(0), - begin(_begin), end(_end) {} + begin(_begin), end(_end), advanced(false) { + TRACE_CTOR(interval_t, + "int, int, int, const datetime_t&, const datetime_t&"); + } interval_t(const string& desc) : years(0), months(0), days(0), - hours(0), minutes(0), seconds(0) { + hours(0), minutes(0), seconds(0), advanced(false) { + TRACE_CTOR(interval_t, "const string&"); std::istringstream stream(desc); parse(stream); } + ~interval_t() throw() { + TRACE_DTOR(interval_t); + } + operator bool() const { return (years > 0 || months > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0); @@ -98,6 +108,7 @@ struct interval_t begin = first(moment); } datetime_t first(const datetime_t& moment = datetime_t()) const; + datetime_t increment(const datetime_t&) const; void parse(std::istream& in); @@ -423,6 +423,8 @@ string::~string() { #endif // VERIFY_ON +ledger::string empty_string(""); + /********************************************************************** * * Logging @@ -242,6 +242,8 @@ inline bool operator!=(const string& __lhs, const char* __rhs) #endif // VERIFY_ON +extern ledger::string empty_string; + #define IF_VERIFY() if (DO_VERIFY()) /********************************************************************** @@ -1,4 +1,36 @@ +/* + * Copyright (c) 2003-2007, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #include "valexpr.h" +#include "parsexp.h" #include "walk.h" #include "utils.h" @@ -68,7 +100,7 @@ namespace { int count_leaves(ptr_op_t expr) { int count = 0; - if (expr->kind != op_t::O_COM) { + if (expr->kind != op_t::O_COMMA) { count = 1; } else { count += count_leaves(expr->left()); @@ -85,7 +117,7 @@ namespace { value_expr temp; - if (expr->kind != op_t::O_COM) { + if (expr->kind != op_t::O_COMMA) { if (expr->kind < op_t::TERMINALS) { temp.reset(expr); } else { @@ -94,7 +126,7 @@ namespace { expr->compute(temp->as_value(), details, context); } } else { - temp.reset(new op_t(op_t::O_COM)); + temp.reset(new op_t(op_t::O_COMMA)); temp->set_left(reduce_leaves(expr->left(), details, context)); temp->set_right(reduce_leaves(expr->right(), details, context)); } @@ -106,7 +138,7 @@ namespace { if (! context) return NULL; - if (context->kind != op_t::O_COM) { + if (context->kind != op_t::O_COMMA) { if (goal == found++) return context; } else { @@ -604,7 +636,7 @@ void op_t::compute(value_t& result, const details_t& details, break; } - case O_COM: + case O_COMMA: if (! left()) throw new compute_error("Comma operator missing left operand", new valexpr_context(const_cast<op_t *>(this))); @@ -755,1254 +787,290 @@ void op_t::compute(value_t& result, const details_t& details, } } -static inline void unexpected(char c, char wanted = '\0') { - if ((unsigned char) c == 0xff) { - if (wanted) - throw new value_expr_error(string("Missing '") + wanted + "'"); - else - throw new value_expr_error("Unexpected end"); - } else { - if (wanted) - throw new value_expr_error(string("Invalid char '") + c + - "' (wanted '" + wanted + "')"); - else - throw new value_expr_error(string("Invalid char '") + c + "'"); +void valexpr_context::describe(std::ostream& out) const throw() +{ + if (! expr) { + out << "valexpr_context expr not set!" << std::endl; + return; } -} -ptr_op_t parse_value_term(std::istream& in, scope_t * scope, - const short flags); + if (! desc.empty()) + out << desc << std::endl; -inline ptr_op_t parse_value_term(const char * p, scope_t * scope, - const short flags) { - std::istringstream stream(p); - return parse_value_term(stream, scope, flags); + out << " "; +#if 0 + unsigned long start = (long)out.tellp() - 1; + unsigned long begin; + unsigned long end; + bool found = print_value_expr(out, expr, true, error_node, &begin, &end); + out << std::endl; + if (found) { + out << " "; + for (unsigned int i = 0; i < end - start; i++) { + if (i >= begin - start) + out << "^"; + else + out << " "; + } + out << std::endl; + } +#endif } -ptr_op_t parse_value_term(std::istream& in, scope_t * scope, - const short flags) +#if 0 +xpath_t::ptr_op_t xpath_t::op_t::compile(scope_t& scope) { - value_expr node; - - char buf[256]; - char c = peek_next_nonws(in); - - if (flags & PARSE_VALEXPR_RELAXED) { - if (c == '@') { - in.get(c); - c = peek_next_nonws(in); - } - else if (! (c == '(' || c == '[' || c == '{' || c == '/')) { - amount_t temp; - char prev_c = c; - unsigned long pos = 0; - // When in relaxed parsing mode, we do want to migrate commodity - // flags, so that any precision specified by the user updates - // the current maximum precision displayed. - try { - pos = (long)in.tellg(); - - unsigned char parse_flags = 0; - if (flags & PARSE_VALEXPR_NO_MIGRATE) - parse_flags |= AMOUNT_PARSE_NO_MIGRATE; - if (flags & PARSE_VALEXPR_NO_REDUCE) - parse_flags |= AMOUNT_PARSE_NO_REDUCE; - - temp.parse(in, parse_flags); - } - catch (amount_error * err) { - // If the amount had no commodity, it must be an unambiguous - // variable reference - if (std::strcmp(err->what(), "No quantity specified for amount") == 0) { - in.clear(); - in.seekg(pos, std::ios::beg); - c = prev_c; - goto parse_ident; - } else { - throw err; - } - } - node.reset(new op_t(op_t::VALUE)); - node->set_value(temp); - goto parsed; + switch (kind) { + case VAR_NAME: + case FUNC_NAME: + if (ptr_op_t def = scope.lookup(as_string())) { +#if 1 + return def; +#else + // Aren't definitions compiled when they go in? Would + // recompiling here really add any benefit? + return def->compile(scope); +#endif } - } + return this; - 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; + default: + break; } - else if (std::isalnum(c) || c == '_') { - bool have_args = false; - istream_pos_type beg; - - READ_INTO(in, buf, 255, c, std::isalnum(c) || c == '_'); - c = peek_next_nonws(in); - if (c == '(') { - in.get(c); - beg = in.tellg(); - - int paren_depth = 0; - while (! in.eof()) { - in.get(c); - if (c == '(' || c == '{' || c == '[') - paren_depth++; - else if (c == ')' || c == '}' || c == ']') { - if (paren_depth == 0) - break; - paren_depth--; - } - } - if (c != ')') - unexpected(c, ')'); - have_args = true; - c = peek_next_nonws(in); - } + if (kind < TERMINALS) + return this; - bool definition = false; - if (c == '=') { - in.get(c); - if (peek_next_nonws(in) == '=') { - in.unget(); - c = '\0'; - } else { - definition = true; - } - } + ptr_op_t lhs(left()->compile(scope)); + ptr_op_t rhs(right() ? right()->compile(scope) : ptr_op_t()); - 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 (lhs == left() && (! rhs || rhs == right())) + return this; - if (peek_next_nonws(in) != '=') { - in.get(c); - unexpected(c, '='); - } - in.get(c); - } + ptr_op_t intermediate(copy(lhs, rhs)); - // Define the value associated with the defined identifier - value_expr def(parse_boolean_expr(in, params.get(), flags)); - if (! def.get()) - throw new value_expr_error(string("Definition failed for '") + buf + "'"); + if (lhs->is_value() && (! rhs || rhs->is_value())) + return wrap_value(intermediate->calc(scope)); - 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()); + return intermediate; +} - 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; - } +value_t xpath_t::op_t::calc(scope_t& scope) +{ + bool find_all_nodes = false; - 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; + switch (kind) { + case VALUE: + return as_value(); - // 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; - } - } + case VAR_NAME: + case FUNC_NAME: + if (ptr_op_t reference = compile(scope)) { + return reference->calc(scope); } else { - in.get(c); + throw_(calc_error, "No " << (kind == VAR_NAME ? "variable" : "function") + << " named '" << as_string() << "'"); } - - // Read in the regexp - READ_INTO(in, buf, 255, c, c != '/'); - if (c != '/') - unexpected(c, '/'); - - 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()); + case FUNCTION: + // This should never be evaluated directly; it only appears as the + // left node of an O_CALL operator. + assert(false); break; - } - default: - in.unget(); - break; - } + case O_CALL: { + call_scope_t call_args(scope); - parsed: - return node.release(); -} + if (right()) + call_args.set_args(right()->calc(scope)); -ptr_op_t parse_mul_expr(std::istream& in, scope_t * scope, - const short flags) -{ - value_expr node; - - if (peek_next_nonws(in) == '%') { - char c; - in.get(c); - node.reset(new op_t(op_t::O_PERC)); - node->set_left(parse_value_term(in, scope, flags)); - return node.release(); - } + ptr_op_t func = left(); + string name; - node.reset(parse_value_term(in, scope, flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - while (c == '*' || c == '/') { - in.get(c); - switch (c) { - case '*': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_MUL)); - node->set_left(prev.release()); - node->set_right(parse_value_term(in, scope, flags)); - break; - } - - case '/': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_DIV)); - node->set_left(prev.release()); - node->set_right(parse_value_term(in, scope, flags)); - break; - } - } - c = peek_next_nonws(in); + if (func->kind == FUNC_NAME) { + name = func->as_string(); + func = func->compile(scope); } - } - return node.release(); -} + if (func->kind != FUNCTION) + throw_(calc_error, + name.empty() ? string("Attempt to call non-function") : + (string("Attempt to call unknown function '") + name + "'")); -ptr_op_t parse_add_expr(std::istream& in, scope_t * scope, - const short flags) -{ - value_expr node; - - if (peek_next_nonws(in) == '-') { - char c; - in.get(c); - value_expr expr(parse_mul_expr(in, scope, flags)); - if (expr->kind == op_t::VALUE) { - expr->as_value().in_place_negate(); - return expr.release(); - } - node.reset(new op_t(op_t::O_NEG)); - node->set_left(expr.release()); - return node.release(); + return func->as_function()(call_args); } - node.reset(parse_mul_expr(in, scope, flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - while (c == '+' || c == '-') { - in.get(c); - switch (c) { - case '+': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_ADD)); - node->set_left(prev.release()); - node->set_right(parse_mul_expr(in, scope, flags)); - break; - } + case ARG_INDEX: { + call_scope_t& args(CALL_SCOPE(scope)); - case '-': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_SUB)); - node->set_left(prev.release()); - node->set_right(parse_mul_expr(in, scope, flags)); - break; - } - } - c = peek_next_nonws(in); - } + if (as_long() >= 0 && as_long() < args.size()) + return args[as_long()]; + else + throw_(calc_error, "Reference to non-existing argument"); + break; } - return node.release(); -} + case O_FIND: + case O_RFIND: + return select_nodes(scope, left()->calc(scope), right(), kind == O_RFIND); -ptr_op_t parse_logic_expr(std::istream& in, scope_t * scope, - const short flags) -{ - value_expr node; - - if (peek_next_nonws(in) == '!') { - char c; - in.get(c); - node.reset(new op_t(op_t::O_NOT)); - node->set_left(parse_add_expr(in, scope, flags)); - return node.release(); - } + case O_PRED: { + value_t values = left()->calc(scope); - node.reset(parse_add_expr(in, scope, flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - if (c == '!' || c == '=' || c == '<' || c == '>') { - in.get(c); - switch (c) { - case '!': - case '=': { - bool negate = c == '!'; - if ((c = peek_next_nonws(in)) == '=') - in.get(c); - else - unexpected(c, '='); - value_expr prev(node.release()); - node.reset(new op_t(negate ? op_t::O_NEQ : - op_t::O_EQ)); - node->set_left(prev.release()); - node->set_right(parse_add_expr(in, scope, flags)); - break; - } + if (! values.is_null()) { + op_predicate pred(right()); - case '<': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_LT)); - if (peek_next_nonws(in) == '=') { - in.get(c); - node->kind = op_t::O_LTE; - } - node->set_left(prev.release()); - node->set_right(parse_add_expr(in, scope, flags)); - break; - } + if (! values.is_sequence()) { + context_scope_t value_scope(scope, values, 0, 1); + if (pred(value_scope)) + return values; + return NULL_VALUE; + } else { + std::size_t index = 0; + std::size_t size = values.as_sequence().size(); - case '>': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_GT)); - if (peek_next_nonws(in) == '=') { - in.get(c); - node->kind = op_t::O_GTE; - } - node->set_left(prev.release()); - node->set_right(parse_add_expr(in, scope, flags)); - break; - } + value_t result; - default: - if (! in.eof()) - unexpected(c); - break; + foreach (const value_t& value, values.as_sequence()) { + context_scope_t value_scope(scope, value, index, size); + if (pred(value_scope)) + result.push_back(value); + index++; + } + return result; } } + break; } - return node.release(); -} + case NODE_ID: + switch (as_name()) { + case document_t::CURRENT: + return current_value(scope); -ptr_op_t parse_boolean_expr(std::istream& in, scope_t * scope, - const short flags) -{ - value_expr node(parse_logic_expr(in, scope, flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - while (c == '&' || c == '|' || c == '?') { - in.get(c); - switch (c) { - case '&': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_AND)); - node->set_left(prev.release()); - node->set_right(parse_logic_expr(in, scope, flags)); - break; - } + case document_t::PARENT: + if (optional<parent_node_t&> parent = current_xml_node(scope).parent()) + return &*parent; + else + throw_(std::logic_error, "Attempt to access parent of root node"); + break; - case '|': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_OR)); - node->set_left(prev.release()); - node->set_right(parse_logic_expr(in, scope, flags)); - break; - } + case document_t::ROOT: + return ¤t_xml_node(scope).document(); - case '?': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_QUES)); - node->set_left(prev.release()); - node->set_right(new op_t(op_t::O_COL)); - node->right()->set_left(parse_logic_expr(in, scope, flags)); - c = peek_next_nonws(in); - if (c != ':') - unexpected(c, ':'); - in.get(c); - node->right()->set_right(parse_logic_expr(in, scope, flags)); - break; - } + case document_t::ALL: + find_all_nodes = true; + break; - default: - if (! in.eof()) - unexpected(c); - break; - } - c = peek_next_nonws(in); + default: + break; // pass down to the NODE_NAME case } - } + // fall through... - return node.release(); -} + case NODE_NAME: { + node_t& current_node(current_xml_node(scope)); -void init_value_expr() -{ - global_scope.reset(new 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); -} + if (current_node.is_parent_node()) { + const bool have_name_id = kind == NODE_ID; -ptr_op_t parse_value_expr(std::istream& in, scope_t * scope, - const short flags) -{ - if (! global_scope.get()) - init_value_expr(); - - std::auto_ptr<symbol_scope_t> - this_scope(new symbol_scope_t(scope ? *scope : *global_scope.get())); - value_expr node; - node.reset(parse_boolean_expr(in, this_scope.get(), flags)); - - if (node.get() && ! in.eof()) { - char c = peek_next_nonws(in); - while (c == ',') { - in.get(c); - switch (c) { - case ',': { - value_expr prev(node.release()); - node.reset(new op_t(op_t::O_COM)); - node->set_left(prev.release()); - node->set_right(parse_logic_expr(in, this_scope.get(), flags)); - break; - } + parent_node_t& parent(current_node.as_parent_node()); - default: - if (! in.eof()) - unexpected(c); - break; + value_t result; + foreach (node_t * child, parent) { + if (find_all_nodes || + ( have_name_id && as_name() == child->name_id()) || + (! have_name_id && as_string() == child->name())) + result.push_back(child); } - c = peek_next_nonws(in); - } - } - - char c; - if (! node.get()) { - in.get(c); - if (in.eof()) - throw new value_expr_error(string("Failed to parse value expression")); - else - unexpected(c); - } else if (! (flags & PARSE_VALEXPR_PARTIAL)) { - in.get(c); - if (! in.eof()) - unexpected(c); - else - in.unget(); - } - - return node.release(); -} - -void valexpr_context::describe(std::ostream& out) const throw() -{ - if (! expr) { - out << "valexpr_context expr not set!" << std::endl; - return; - } - - if (! desc.empty()) - out << desc << std::endl; - - out << " "; - unsigned long start = (long)out.tellp() - 1; - unsigned long begin; - unsigned long end; - bool found = print_value_expr(out, expr, true, error_node, &begin, &end); - out << std::endl; - if (found) { - out << " "; - for (unsigned int i = 0; i < end - start; i++) { - if (i >= begin - start) - out << "^"; - else - out << " "; + return result; } - out << std::endl; - } -} - -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; + break; } - string symbol; + case ATTR_ID: + case ATTR_NAME: + if (optional<value_t&> value = + kind == ATTR_ID ? current_xml_node(scope).get_attr(as_name()) : + current_xml_node(scope).get_attr(as_string())) + return *value; - switch (node->kind) { - case 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 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 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; + 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); -#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 O_NEG: + assert(! right()); + return left()->calc(scope).negate(); - 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 O_NOT: + assert(! right()); + return ! left()->calc(scope); - 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 O_AND: + return left()->calc(scope) && right()->calc(scope); + case O_OR: + return left()->calc(scope) || right()->calc(scope); + + case O_COMMA: + case O_UNION: { + value_t result(left()->calc(scope)); + + ptr_op_t next = right(); + while (next) { + ptr_op_t value_op; + if (next->kind == O_COMMA || next->kind == O_UNION) { + value_op = next->left(); + next = next->right(); + } else { + value_op = next; + next = NULL; + } - case 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 << ")"; + result.push_back(value_op->calc(scope)); } - 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; + return result; + } - case op_t::LAST: + case 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; + return NULL_VALUE; } +#endif -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; - } +} // namespace expr - out << " (" << node->refc << ')' << std::endl; +value_expr::value_expr(const string& _expr_str) : expr_str(_expr_str) +{ + TRACE_CTOR(value_expr, "const string&"); - 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); - } + if (! _expr_str.empty()) + ptr = expr::parser_t(expr_str).expr.ptr; } -} // namespace expr } // namespace ledger @@ -1,3 +1,34 @@ +/* + * 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. + */ + #ifndef _VALEXPR_H #define _VALEXPR_H @@ -5,8 +36,6 @@ #include "utils.h" #include "mask.h" -#include <memory> - namespace ledger { class entry_t; @@ -15,7 +44,6 @@ class account_t; namespace expr { -DECLARE_EXCEPTION(error, parse_error); DECLARE_EXCEPTION(error, compile_error); DECLARE_EXCEPTION(error, calc_error); @@ -375,7 +403,7 @@ struct op_t : public noncopyable O_OR, O_QUES, O_COL, - O_COM, + O_COMMA, O_DEF, O_REF, O_ARG, @@ -632,60 +660,11 @@ class value_expr_error : public error { extern std::auto_ptr<symbol_scope_t> global_scope; extern datetime_t terminus; -extern bool initialized; - -void init_value_expr(); bool compute_amount(const ptr_op_t expr, amount_t& amt, const transaction_t * xact, const ptr_op_t context = NULL); -#define PARSE_VALEXPR_NORMAL 0x00 -#define PARSE_VALEXPR_PARTIAL 0x01 -#define PARSE_VALEXPR_RELAXED 0x02 -#define PARSE_VALEXPR_NO_MIGRATE 0x04 -#define PARSE_VALEXPR_NO_REDUCE 0x08 - -ptr_op_t parse_boolean_expr(std::istream& in, scope_t * scope, - const short flags); - -ptr_op_t parse_value_expr(std::istream& in, - scope_t * scope = NULL, - const short flags = PARSE_VALEXPR_RELAXED); - -inline ptr_op_t -parse_value_expr(const string& str, - scope_t * scope = NULL, - const short flags = PARSE_VALEXPR_RELAXED) { - std::istringstream stream(str); - try { - return parse_value_expr(stream, scope, flags); - } - catch (error * err) { - err->context.push_back - (new line_context(str, (long)stream.tellg() - 1, - "While parsing value expression:")); - throw err; - } -} - -inline ptr_op_t -parse_value_expr(const char * p, - scope_t * scope = NULL, - const short flags = PARSE_VALEXPR_RELAXED) { - return parse_value_expr(string(p), scope, flags); -} - -void dump_value_expr(std::ostream& out, const ptr_op_t node, - const int depth = 0); - -bool print_value_expr(std::ostream& out, - const ptr_op_t node, - const bool relaxed = true, - const ptr_op_t node_to_find = NULL, - unsigned long * start_pos = NULL, - unsigned long * end_pos = NULL); - ////////////////////////////////////////////////////////////////////// inline void guarded_compute(const ptr_op_t expr, @@ -769,6 +748,40 @@ inline ptr_op_t op_t::wrap_functor(const function_t& fobj) { return temp; } +#if 0 +class xpath_t +{ +public: +public: + void parse(const string& _expr, flags_t _flags = XPATH_PARSE_RELAXED) { + expr = _expr; + flags = _flags; + ptr = parse_expr(_expr, _flags); + } + void parse(std::istream& in, flags_t _flags = XPATH_PARSE_RELAXED) { + expr = ""; + flags = _flags; + ptr = parse_expr(in, _flags); + } + + void compile(scope_t& scope) { + if (ptr.get()) + ptr = ptr->compile(scope); + } + + value_t calc(scope_t& scope) const { + if (ptr.get()) + return ptr->calc(scope); + return NULL_VALUE; + } + + static value_t eval(const string& _expr, scope_t& scope) { + return xpath_t(_expr).calc(scope); + } + +}; +#endif + } // namespace expr ////////////////////////////////////////////////////////////////////// @@ -778,60 +791,46 @@ class value_expr expr::ptr_op_t ptr; public: - string expr; + string expr_str; typedef expr::details_t details_t; - value_expr() : ptr(NULL) {} + value_expr() {} - value_expr(const string& _expr) : expr(_expr) { - DEBUG("ledger.memory.ctors", "ctor value_expr"); - if (! _expr.empty()) - ptr = expr::parse_value_expr(expr); - else - ptr = expr::ptr_op_t(); - } - value_expr(const expr::ptr_op_t _ptr) : ptr(_ptr) { - DEBUG("ledger.memory.ctors", "ctor value_expr"); - } - value_expr(const value_expr& other) : ptr(other.ptr), expr(other.expr) { - DEBUG("ledger.memory.ctors", "ctor value_expr"); + value_expr(const string& _expr_str); + value_expr(const expr::ptr_op_t _ptr, const string& _expr_str = "") + : ptr(_ptr), expr_str(_expr_str) { + TRACE_CTOR(value_expr, "const expr::ptr_op_t"); } - virtual ~value_expr() { - DEBUG("ledger.memory.dtors", "dtor value_expr"); - if (ptr) - ptr->release(); - } - - value_expr& operator=(const string& _expr) { - expr = _expr; - reset(expr::parse_value_expr(expr)); - return *this; - } - value_expr& operator=(expr::ptr_op_t _expr) { - expr = ""; - reset(_expr); - return *this; + value_expr(const value_expr& other) + : ptr(other.ptr), expr_str(other.expr_str) { + TRACE_CTOR(value_expr, "copy"); } value_expr& operator=(const value_expr& _expr) { - expr = _expr.expr; + expr_str = _expr.expr_str; reset(_expr.get()); return *this; } + virtual ~value_expr() throw() { + TRACE_DTOR(value_expr); + } + operator bool() const throw() { return ptr != NULL; } operator string() const throw() { - return expr; + return expr_str; } operator const expr::ptr_op_t() const throw() { return ptr; } +#if 0 const expr::op_t& operator*() const throw() { return *ptr; } +#endif const expr::ptr_op_t operator->() const throw() { return ptr; } @@ -892,14 +891,6 @@ inline value_t compute_total(const details_t& details = details_t()) { return total_expr->compute(details); } -inline void parse_value_definition(const string& str, - expr::scope_t * scope = NULL) { - std::istringstream def(str); - value_expr expr - (expr::parse_boolean_expr(def, scope ? scope : expr::global_scope.get(), - PARSE_VALEXPR_RELAXED)); -} - ////////////////////////////////////////////////////////////////////// template <typename T> @@ -909,13 +900,13 @@ public: value_expr predicate; item_predicate() { - TRACE_CTOR(item_predicate, "ctor item_predicate<T>()"); + TRACE_CTOR(item_predicate, ""); } item_predicate(const value_expr& _predicate) : predicate(_predicate) { - TRACE_CTOR(item_predicate, "ctor item_predicate<T>(const value_expr&)"); + TRACE_CTOR(item_predicate, "const value_expr&"); } item_predicate(const string& _predicate) : predicate(_predicate) { - TRACE_CTOR(item_predicate, "ctor item_predicate<T>(const string&)"); + TRACE_CTOR(item_predicate, "const string&"); } ~item_predicate() { |