From 200d919fe7c8bcf021011c16fb6ec50821444d5e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 29 Jul 2008 18:57:02 -0400 Subject: Changed the way scopes are structured for an upcoming design change. --- journal.h | 6 +- parsexp.cc | 1067 --------------------------------------------------------- parsexp.h | 235 ------------- pyinterp.cc | 5 +- pyinterp.h | 6 +- report.cc | 2 +- report.h | 6 +- scope.h | 224 +----------- session.cc | 6 +- session.h | 2 +- valexpr.cc | 1095 ----------------------------------------------------------- valexpr.h | 1007 ------------------------------------------------------ 12 files changed, 29 insertions(+), 3632 deletions(-) delete mode 100644 parsexp.cc delete mode 100644 parsexp.h delete mode 100644 valexpr.cc delete mode 100644 valexpr.h diff --git a/journal.h b/journal.h index 2248714e..636616f2 100644 --- a/journal.h +++ b/journal.h @@ -351,7 +351,7 @@ class account_t account_t(account_t * _parent = NULL, const string& _name = "", - const optional _note = none) + const optional& _note = none) : parent(_parent), name(_name), note(_note), depth(parent ? parent->depth + 1 : 0), data(NULL), ident(0) { TRACE_CTOR(account_t, "account_t *, const string&, const string&"); @@ -502,10 +502,10 @@ public: { public: parser_t() { - TRACE_CTOR(parser_t, ""); + TRACE_CTOR(journal_t::parser_t, ""); } virtual ~parser_t() { - TRACE_DTOR(parser_t); + TRACE_DTOR(journal_t::parser_t); } virtual bool test(std::istream& in) const = 0; diff --git a/parsexp.cc b/parsexp.cc deleted file mode 100644 index dcebbcd6..00000000 --- a/parsexp.cc +++ /dev/null @@ -1,1067 +0,0 @@ -/* - * Copyright (c) 2003-2008, John Wiegley. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of New Artisans LLC nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "parsexp.h" -#include "parser.h" - -namespace ledger { -namespace expr { - -void parser_t::token_t::parse_ident(std::istream& in) -{ - if (in.eof()) { - kind = TOK_EOF; - return; - } - assert(in.good()); - - char c = peek_next_nonws(in); - - if (in.eof()) { - kind = TOK_EOF; - return; - } - assert(in.good()); - - kind = IDENT; - length = 0; - - char buf[256]; - READ_INTO_(in, buf, 255, c, length, - std::isalnum(c) || c == '_' || c == '.' || c == '-'); - - switch (buf[0]) { - case 'a': - if (std::strcmp(buf, "and") == 0) - kind = KW_AND; - break; - case 'd': - if (std::strcmp(buf, "div") == 0) - kind = KW_DIV; - break; - case 'e': - if (std::strcmp(buf, "eq") == 0) - kind = EQUAL; - break; - case 'f': - if (std::strcmp(buf, "false") == 0) { - kind = VALUE; - value = false; - } - break; - case 'g': - if (std::strcmp(buf, "gt") == 0) - kind = GREATER; - else if (std::strcmp(buf, "ge") == 0) - kind = GREATEREQ; - break; - case 'i': - if (std::strcmp(buf, "is") == 0) - kind = EQUAL; - break; - case 'l': - if (std::strcmp(buf, "lt") == 0) - kind = LESS; - else if (std::strcmp(buf, "le") == 0) - kind = LESSEQ; - break; - case 'm': - if (std::strcmp(buf, "mod") == 0) - kind = KW_MOD; - break; - case 'n': - if (std::strcmp(buf, "ne") == 0) - kind = NEQUAL; - break; - case 'o': - if (std::strcmp(buf, "or") == 0) - kind = KW_OR; - break; - case 't': - if (std::strcmp(buf, "true") == 0) { - kind = VALUE; - value = true; - } - break; - } - - if (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 = KW_AND; - break; - - case '(': - in.get(c); - kind = LPAREN; - break; - case ')': - in.get(c); - kind = RPAREN; - break; - - case '[': { - in.get(c); - if (! (flags & EXPR_PARSE_NO_DATES)) { - char buf[256]; - READ_INTO_(in, buf, 255, c, length, c != ']'); - if (c != ']') - unexpected(c, ']'); - in.get(c); - length++; - interval_t timespan(buf); - kind = VALUE; - value = timespan.first(); - } else { - kind = LBRACKET; - } - break; - } - - case ']': { - in.get(c); - kind = RBRACKET; - break; - } - - - case '\'': - case '"': { - char delim; - in.get(delim); - char buf[4096]; - READ_INTO_(in, buf, 4095, c, length, c != delim); - if (c != delim) - unexpected(c, delim); - in.get(c); - length++; - kind = VALUE; - value.set_string(buf); - break; - } - - case '{': { - in.get(c); - amount_t temp; - temp.parse(in, AMOUNT_PARSE_NO_MIGRATE); - in.get(c); - if (c != '}') - unexpected(c, '}'); - length++; - kind = VALUE; - value = temp; - break; - } - - case '!': - in.get(c); - c = in.peek(); - if (c == '=') { - in.get(c); - symbol[1] = c; - symbol[2] = '\0'; - kind = NEQUAL; - length = 2; - break; - } - kind = EXCLAM; - break; - - case '-': - in.get(c); - kind = MINUS; - break; - case '+': - in.get(c); - kind = PLUS; - break; - - case '*': - in.get(c); - kind = STAR; - break; - - case 'c': - case 'C': - case 'p': - case 'w': - case 'W': - case 'e': - case '/': { - bool code_mask = c == 'c'; - bool commodity_mask = c == 'C'; - bool payee_mask = c == 'p'; - bool note_mask = c == 'e'; - bool short_account_mask = c == 'w'; - - in.get(c); - if (c == '/') { - c = peek_next_nonws(in); - if (c == '/') { - in.get(c); - c = in.peek(); - if (c == '/') { - in.get(c); - c = in.peek(); - short_account_mask = true; - } else { - payee_mask = true; - } - } - } else { - in.get(c); - } - - // Read in the regexp - char buf[256]; - READ_INTO_(in, buf, 255, c, length, c != '/'); - if (c != '/') - unexpected(c, '/'); - in.get(c); - length++; - - if (short_account_mask) - kind = SHORT_ACCOUNT_MASK; - else if (code_mask) - kind = CODE_MASK; - else if (commodity_mask) - kind = COMMODITY_MASK; - else if (payee_mask) - kind = PAYEE_MASK; - else if (note_mask) - kind = NOTE_MASK; - else - kind = ACCOUNT_MASK; - - value.set_string(buf); - break; - } - - case '=': - in.get(c); - kind = EQUAL; - break; - - case '<': - in.get(c); - if (in.peek() == '=') { - in.get(c); - symbol[1] = c; - symbol[2] = '\0'; - kind = LESSEQ; - length = 2; - break; - } - kind = LESS; - break; - - case '>': - in.get(c); - if (in.peek() == '=') { - in.get(c); - symbol[1] = c; - symbol[2] = '\0'; - kind = GREATEREQ; - length = 2; - break; - } - kind = GREATER; - break; - - 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. - 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; - - if (! temp.parse(in, parse_flags | AMOUNT_PARSE_SOFT_FAIL)) { - // If the amount had no commodity, it must be an unambiguous - // variable reference - - in.clear(); - in.seekg(pos, std::ios::beg); - - c = in.peek(); - assert(! (std::isdigit(c) || c == '.')); - parse_ident(in); - } else { - kind = VALUE; - value = temp; - } - } - break; - } -} - -void parser_t::token_t::rewind(std::istream& in) -{ - for (unsigned int i = 0; i < length; i++) - in.unget(); -} - - -void parser_t::token_t::unexpected() -{ - switch (kind) { - case TOK_EOF: - throw_(parse_error, "Unexpected end of expression"); - case IDENT: - throw_(parse_error, "Unexpected symbol '" << value << "'"); - case VALUE: - throw_(parse_error, "Unexpected value '" << value << "'"); - default: - throw_(parse_error, "Unexpected operator '" << symbol << "'"); - } -} - -void parser_t::token_t::unexpected(char c, char wanted) -{ - if ((unsigned char) c == 0xff) { - if (wanted) - throw_(parse_error, "Missing '" << wanted << "'"); - else - throw_(parse_error, "Unexpected end"); - } else { - if (wanted) - throw_(parse_error, "Invalid char '" << c << - "' (wanted '" << wanted << "')"); - else - throw_(parse_error, "Invalid char '" << c << "'"); - } -} - -ptr_op_t -parser_t::parse_value_term(std::istream& in, scope_t& scope, const flags_t tflags) const -{ - ptr_op_t node; - - token_t& tok = next_token(in, tflags); - - switch (tok.kind) { - case token_t::VALUE: - node = new op_t(op_t::VALUE); - node->set_value(tok.value); - break; - - case token_t::SHORT_ACCOUNT_MASK: - node = new op_t(op_t::F_SHORT_ACCOUNT_MASK); - node->set_mask(tok.value.as_string()); - break; - case token_t::CODE_MASK: - node = new op_t(op_t::F_CODE_MASK); - node->set_mask(tok.value.as_string()); - break; - case token_t::COMMODITY_MASK: - node = new op_t(op_t::F_COMMODITY_MASK); - node->set_mask(tok.value.as_string()); - break; - case token_t::PAYEE_MASK: - node = new op_t(op_t::F_PAYEE_MASK); - node->set_mask(tok.value.as_string()); - break; - case token_t::NOTE_MASK: - node = new op_t(op_t::F_NOTE_MASK); - node->set_mask(tok.value.as_string()); - break; - case token_t::ACCOUNT_MASK: - node = new op_t(op_t::F_ACCOUNT_MASK); - node->set_mask(tok.value.as_string()); - break; - - case token_t::IDENT: { -#if 0 -#ifdef USE_BOOST_PYTHON - if (tok.value->as_string() == "lambda") // special - try { - char c, buf[4096]; - - std::strcpy(buf, "lambda "); - READ_INTO(in, &buf[7], 4000, c, true); - - ptr_op_t eval = new op_t(op_t::O_EVAL); - ptr_op_t lambda = new op_t(op_t::FUNCTION); - lambda->functor = new python_functor_t(python_eval(buf)); - eval->set_left(lambda); - ptr_op_t sym = new op_t(op_t::SYMBOL); - sym->name = new string("__ptr"); - eval->set_right(sym); - - node = eval; - - goto done; - } - catch(const boost::python::error_already_set&) { - throw_(parse_error, "Error parsing lambda expression"); - } -#endif /* USE_BOOST_PYTHON */ -#endif - - string ident = tok.value.as_string(); - - // An identifier followed by ( represents a function call - tok = next_token(in, tflags); - - if (tok.kind == token_t::LPAREN) { -#if 0 - 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; -#endif - } else { - if (std::isdigit(ident[0])) { - node = new op_t(op_t::O_ARG); - node->set_long(lexical_cast(ident.c_str())); - } else { - node = new op_t(op_t::O_LOOKUP); - node->set_string(ident); - } - push_token(tok); - } - break; - } - - case token_t::LPAREN: - node = new op_t(op_t::O_COMMA); - node->set_left(parse_value_expr(in, scope, tflags | EXPR_PARSE_PARTIAL)); - if (! node->left()) - throw_(parse_error, tok.symbol << " operator not followed by argument"); - - tok = next_token(in, tflags); - if (tok.kind != token_t::RPAREN) - tok.unexpected(0xff, ')'); - break; - - default: - push_token(tok); - break; - } - -#if 0 -#ifdef USE_BOOST_PYTHON - done: -#endif -#endif - return node; -} - -ptr_op_t -parser_t::parse_unary_expr(std::istream& in, scope_t& scope, - const flags_t tflags) const -{ - ptr_op_t node; - - token_t& tok = next_token(in, tflags); - - switch (tok.kind) { - case token_t::EXCLAM: { - ptr_op_t term(parse_value_term(in, scope, tflags)); - if (! term) - throw_(parse_error, - tok.symbol << " operator not followed by argument"); - - // A very quick optimization - if (term->kind == op_t::VALUE) { - term->as_value_lval().in_place_negate(); - node = term; - } else { - node = new op_t(op_t::O_NOT); - node->set_left(term); - } - break; - } - - case token_t::MINUS: { - ptr_op_t term(parse_value_term(in, scope, tflags)); - if (! term) - throw_(parse_error, - tok.symbol << " operator not followed by argument"); - - // A very quick optimization - if (term->kind == op_t::VALUE) { - term->as_value_lval().in_place_negate(); - node = term; - } else { - node = new op_t(op_t::O_NEG); - node->set_left(term); - } - break; - } - - default: - push_token(tok); - node = parse_value_term(in, scope, tflags); - break; - } - - return node; -} - -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: - if (tflags & EXPR_PARSE_NO_ASSIGN) - tok.rewind(in); - else - 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; -} - -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: { - as_value().print(out, context.relaxed); - break; - } - -#if 0 - case FUNC_NAME: - out << as_string(); - break; - - case ATTR_NAME: - out << '@' << as_string(); - break; - - case VAR_NAME: - out << '$' << as_string(); - break; -#endif - - case FUNCTION: - out << ""; - break; - - case ARG_INDEX: - out << '@' << as_long(); - break; - - case O_NOT: - out << "!"; - if (left() && left()->print(out, context)) - found = true; - break; - case O_NEG: - out << "-"; - if (left() && left()->print(out, context)) - found = true; - break; - - 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; -#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 ATTR_NAME: - out << "ATTR_NAME - " << as_string(); - 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; - - case O_ADD: out << "O_ADD"; break; - case O_SUB: out << "O_SUB"; break; - case O_MUL: out << "O_MUL"; break; - case O_DIV: out << "O_DIV"; break; - - case O_NEQ: out << "O_NEQ"; break; - case O_EQ: out << "O_EQ"; break; - case O_LT: out << "O_LT"; break; - case O_LTE: out << "O_LTE"; break; - case O_GT: out << "O_GT"; break; - case O_GTE: out << "O_GTE"; break; - - case O_AND: out << "O_AND"; break; - case O_OR: out << "O_OR"; break; - - case O_COMMA: out << "O_COMMA"; break; - - case LAST: - default: - assert(false); - break; - } - - out << " (" << refc << ')' << std::endl; - - if (kind > TERMINALS) { - if (left()) { - left()->dump(out, depth + 1); - if (right()) - right()->dump(out, depth + 1); - } else { - assert(! right()); - } - } -} - -} // namespace expr -} // namespace ledger diff --git a/parsexp.h b/parsexp.h deleted file mode 100644 index 900f8d0c..00000000 --- a/parsexp.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2003-2008, John Wiegley. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of New Artisans LLC nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _PARSEXP_H -#define _PARSEXP_H - -#include "valexpr.h" - -namespace ledger { -namespace expr { - -DECLARE_EXCEPTION(error, parse_error); - -class parser_t : public noncopyable -{ -#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_NO_ASSIGN 0x10 -#define EXPR_PARSE_NO_DATES 0x20 - -public: - typedef uint_least8_t flags_t; - -private: - struct token_t : public noncopyable - { - enum kind_t { - VALUE, // any kind of literal value - - SHORT_ACCOUNT_MASK, - CODE_MASK, - COMMODITY_MASK, - PAYEE_MASK, - NOTE_MASK, - ACCOUNT_MASK, - - 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, - - 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() throw() { - 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; - } - - ptr_op_t parse_value_term(std::istream& in, scope_t& scope, - const flags_t flags) const; - ptr_op_t parse_unary_expr(std::istream& in, scope_t& scope, - const flags_t flags) const; - 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; - - value_expr 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); - - if (use_lookahead) { - use_lookahead = false; - lookahead.rewind(in); - } - lookahead.clear(); - - return value_expr(top_node, str); - } - catch (error * err) { - err->context.push_back - (new line_context(str, (long)in.tellg() - 1, - "While parsing value expression:")); - throw err; - } - } - -public: - parser_t() : use_lookahead(false) {} - - value_expr parse(std::istream& in, - const flags_t flags = EXPR_PARSE_RELAXED) - { - return parse_expr(in, empty_string, *global_scope, flags); - } - - value_expr parse(std::istream& in, scope_t& scope, - const flags_t flags = EXPR_PARSE_RELAXED) - { - return parse_expr(in, empty_string, scope, flags); - } - - value_expr parse(string& str, const flags_t flags = EXPR_PARSE_RELAXED) - { - std::istringstream stream(str); - return parse_expr(stream, str, *global_scope, flags); - } - - value_expr parse(string& str, scope_t& scope, - const flags_t flags = EXPR_PARSE_RELAXED) - { - std::istringstream stream(str); - return parse_expr(stream, str, scope, flags); - } -}; - -} // namespace expr -} // namespace ledger - -#endif // _PARESXP_H diff --git a/pyinterp.cc b/pyinterp.cc index d702f291..07a619fe 100644 --- a/pyinterp.cc +++ b/pyinterp.cc @@ -86,9 +86,8 @@ struct python_run } }; -python_interpreter_t::python_interpreter_t(expr_t::scope_t& parent) - : expr_t::symbol_scope_t(parent), - mmodule(borrowed(PyImport_AddModule("__main__"))), +python_interpreter_t::python_interpreter_t() + : scope_t(), mmodule(borrowed(PyImport_AddModule("__main__"))), nspace(handle<>(borrowed(PyModule_GetDict(mmodule.get())))) { TRACE_CTOR(python_interpreter_t, "expr_t::scope_t&"); diff --git a/pyinterp.h b/pyinterp.h index 4d3ac0c8..5486eea5 100644 --- a/pyinterp.h +++ b/pyinterp.h @@ -39,8 +39,7 @@ namespace ledger { -class python_interpreter_t - : public noncopyable, public expr_t::symbol_scope_t +class python_interpreter_t : public noncopyable, public scope_t { boost::python::handle<> mmodule; @@ -95,8 +94,7 @@ public: virtual expr_t::ptr_op_t lookup(const string& name) { if (boost::python::object func = eval(name)) return WRAP_FUNCTOR(functor_t(name, func)); - else - return expr_t::symbol_scope_t::lookup(name); + return expr_t::ptr_op_t(); } class lambda_t : public functor_t { diff --git a/report.cc b/report.cc index ff8000ee..3742e020 100644 --- a/report.cc +++ b/report.cc @@ -453,7 +453,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name) break; } - return symbol_scope_t::lookup(name); + return session.lookup(name); } } // namespace ledger diff --git a/report.h b/report.h index 0a1366e1..7c5c6384 100644 --- a/report.h +++ b/report.h @@ -81,7 +81,7 @@ namespace ledger { // says that the formatter should be "flushed" after the entities are // iterated. This does not happen for the commodities iteration, however. -class report_t : public symbol_scope_t +class report_t : public noncopyable, public scope_t { report_t(); @@ -134,9 +134,7 @@ public: session_t& session; explicit report_t(session_t& _session) - : symbol_scope_t(downcast(_session)), - - head_entries(0), + : head_entries(0), tail_entries(0), show_collapsed(false), diff --git a/scope.h b/scope.h index 4a031104..1abde444 100644 --- a/scope.h +++ b/scope.h @@ -37,36 +37,17 @@ namespace ledger { -class scope_t : public noncopyable +class scope_t { - scope_t(); - -protected: - enum type_t { - CHILD_SCOPE, - SYMBOL_SCOPE, - CALL_SCOPE, - CONTEXT_SCOPE - } type_; - public: - explicit scope_t(type_t _type) : type_(_type) { - TRACE_CTOR(scope_t, "type_t"); + explicit scope_t() { + TRACE_CTOR(scope_t, ""); } virtual ~scope_t() { TRACE_DTOR(scope_t); } - const type_t type() const { - return type_; - } - - void define(const string& name, const value_t& val) { - define(name, expr_t::op_t::wrap_value(val)); - } - void define(const string& name, const function_t& func) { - define(name, expr_t::op_t::wrap_functor(func)); - } + virtual expr_t::ptr_op_t lookup(const string& name) = 0; value_t resolve(const string& name) { expr_t::ptr_op_t definition = lookup(name); @@ -75,81 +56,29 @@ public: else return NULL_VALUE; } - - virtual void define(const string& name, expr_t::ptr_op_t def) = 0; - virtual expr_t::ptr_op_t lookup(const string& name) = 0; - -protected: - virtual optional find_scope(const type_t _type, - bool skip_this = false) = 0; - virtual optional find_first_scope(const type_t _type1, - const type_t _type2, - bool skip_this = false) = 0; - template - T& find_scope(bool skip_this = false) { - assert(false); - } - template - optional maybe_find_scope(bool skip_this = false) { - assert(false); - } - - friend class child_scope_t; - friend class expr_t::op_t; }; -class child_scope_t : public scope_t +class child_scope_t : public noncopyable, public scope_t { scope_t * parent; - child_scope_t(); - public: - explicit child_scope_t(type_t _type = CHILD_SCOPE) - : scope_t(_type), parent(NULL) { - TRACE_CTOR(child_scope_t, "type_t"); + explicit child_scope_t() : parent(NULL) { + TRACE_CTOR(child_scope_t, ""); } - explicit child_scope_t(scope_t& _parent, type_t _type = CHILD_SCOPE) - : scope_t(_type), parent(&_parent) { - TRACE_CTOR(child_scope_t, "scope_t&, type_t"); + explicit child_scope_t(scope_t& _parent) + : parent(&_parent) { + TRACE_CTOR(child_scope_t, "scope_t&"); } virtual ~child_scope_t() { TRACE_DTOR(child_scope_t); } - virtual void define(const string& name, expr_t::ptr_op_t def) { - if (parent) - parent->define(name, def); - } virtual expr_t::ptr_op_t lookup(const string& name) { if (parent) return parent->lookup(name); return expr_t::ptr_op_t(); } - -protected: - virtual optional find_scope(type_t _type, - bool skip_this = false) { - for (scope_t * ptr = (skip_this ? parent : this); ptr; ) { - if (ptr->type() == _type) - return *ptr; - - ptr = polymorphic_downcast(ptr)->parent; - } - return none; - } - - virtual optional find_first_scope(const type_t _type1, - const type_t _type2, - bool skip_this = false) { - for (scope_t * ptr = (skip_this ? parent : this); ptr; ) { - if (ptr->type() == _type1 || ptr->type() == _type2) - return *ptr; - - ptr = polymorphic_downcast(ptr)->parent; - } - return none; - } }; class symbol_scope_t : public child_scope_t @@ -159,12 +88,10 @@ class symbol_scope_t : public child_scope_t symbol_map symbols; public: - explicit symbol_scope_t() - : child_scope_t(SYMBOL_SCOPE) { + explicit symbol_scope_t() { TRACE_CTOR(symbol_scope_t, ""); } - explicit symbol_scope_t(scope_t& _parent) - : child_scope_t(_parent, SYMBOL_SCOPE) { + explicit symbol_scope_t(scope_t& _parent) : child_scope_t(_parent) { TRACE_CTOR(symbol_scope_t, "scope_t&"); } virtual ~symbol_scope_t() { @@ -172,13 +99,13 @@ public: } void define(const string& name, const value_t& val) { - scope_t::define(name, val); + define(name, expr_t::op_t::wrap_value(val)); } void define(const string& name, const function_t& func) { - scope_t::define(name, func); + define(name, expr_t::op_t::wrap_functor(func)); } - virtual void define(const string& name, expr_t::ptr_op_t def); + virtual expr_t::ptr_op_t lookup(const string& name); }; @@ -189,8 +116,7 @@ class call_scope_t : public child_scope_t call_scope_t(); public: - explicit call_scope_t(scope_t& _parent) - : child_scope_t(_parent, CALL_SCOPE) { + explicit call_scope_t(scope_t& _parent) : child_scope_t(_parent) { TRACE_CTOR(call_scope_t, "scope_t&"); } virtual ~call_scope_t() { @@ -250,124 +176,6 @@ public: T * operator->() { return value; } }; -#if 0 -class context_scope_t : public child_scope_t -{ -public: - value_t current_element; - std::size_t element_index; - std::size_t sequence_size; - - explicit context_scope_t(scope_t& _parent, - const value_t& _element = NULL_VALUE, - const std::size_t _element_index = 0, - const std::size_t _sequence_size = 0) - : child_scope_t(_parent, CONTEXT_SCOPE), current_element(_element), - element_index(_element_index), sequence_size(_sequence_size) - { - TRACE_CTOR(expr::context_scope_t, "scope_t&, const value_t&, ..."); - } - virtual ~context_scope_t() { - TRACE_DTOR(expr::context_scope_t); - } - - const std::size_t index() const { - return element_index; - } - const std::size_t size() const { - return sequence_size; - } - - value_t& value() { - return current_element; - } -}; - -struct context_t -{ - const entry_t * entry() { - return NULL; - } - const transaction_t * xact() { - return NULL; - } - const account_t * account() { - return NULL; - } -}; - -struct entry_context_t : public context_t -{ - const entry_t * entry_; - - const entry_t * entry() { - return entry_; - } -}; - -struct xact_context_t : public context_t -{ - const transaction_t * xact_; - - const entry_t * entry() { - return xact_->entry; - } - const transaction_t * xact() { - return xact_; - } - const account_t * account() { - return xact_->account; - } -}; - -struct account_context_t : public context_t -{ - const account_t * account_; - - const account_t * account() { - return account_; - } -}; -#endif - -template<> -inline symbol_scope_t& -scope_t::find_scope(bool skip_this) { - optional scope = find_scope(SYMBOL_SCOPE, skip_this); - assert(scope); - return downcast(*scope); -} - -template<> -inline call_scope_t& -scope_t::find_scope(bool skip_this) { - optional scope = find_scope(CALL_SCOPE, skip_this); - assert(scope); - return downcast(*scope); -} - -#if 0 -template<> -inline context_scope_t& -scope_t::find_scope(bool skip_this) { - optional scope = find_scope(CONTEXT_SCOPE, skip_this); - assert(scope); - return downcast(*scope); -} -#endif - -#define FIND_SCOPE(scope_type, scope_ref) \ - downcast(scope_ref).find_scope() - -#define CALL_SCOPE(scope_ref) \ - FIND_SCOPE(call_scope_t, scope_ref) -#define SYMBOL_SCOPE(scope_ref) \ - FIND_SCOPE(symbol_scope_t, scope_ref) -#if 0 -#define CONTEXT_SCOPE(scope_ref) \ - FIND_SCOPE(context_scope_t, scope_ref) -#endif - } // namespace ledger #endif // _SCOPE_H diff --git a/session.cc b/session.cc index 4a565a9e..efb38d89 100644 --- a/session.cc +++ b/session.cc @@ -67,9 +67,7 @@ void release_session_context() } session_t::session_t() - : symbol_scope_t(), - - register_format + : register_format ("%D %-.20P %-.22A %12.67t %!12.80T\n%/" "%32|%-.22A %12.67t %!12.80T\n"), wide_register_format @@ -328,7 +326,7 @@ expr_t::ptr_op_t session_t::lookup(const string& name) break; } - return symbol_scope_t::lookup(name); + return expr_t::ptr_op_t(); } // jww (2007-04-26): All of Ledger should be accessed through a diff --git a/session.h b/session.h index 3c1bc89a..944b9ce2 100644 --- a/session.h +++ b/session.h @@ -37,7 +37,7 @@ namespace ledger { -class session_t : public symbol_scope_t +class session_t : public noncopyable, public scope_t { static void initialize(); static void shutdown(); diff --git a/valexpr.cc b/valexpr.cc deleted file mode 100644 index 5826f420..00000000 --- a/valexpr.cc +++ /dev/null @@ -1,1095 +0,0 @@ -/* - * Copyright (c) 2003-2008, John Wiegley. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of New Artisans LLC nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "valexpr.h" -#include "parsexp.h" -#include "walk.h" -#include "utils.h" - -namespace ledger { - -namespace expr { - -std::auto_ptr global_scope; -datetime_t terminus; - -details_t::details_t(const transaction_t& _xact) - : entry(_xact.entry), xact(&_xact), account(xact_account(_xact)) -{ - TRACE_CTOR(details_t, "const transaction_t&"); -} - -bool compute_amount(ptr_op_t expr, amount_t& amt, - const transaction_t * xact, ptr_op_t context) -{ - value_t result; - try { - expr->compute(result, xact ? details_t(*xact) : details_t(), context); - - // Most of the time when computing the amount of a transaction this cast - // will do nothing at all. - assert(result.valid()); - result.in_place_cast(value_t::AMOUNT); - amt = result.as_amount(); - assert(amt.valid()); - } - catch (error * err) { - if (err->context.empty() || - ! dynamic_cast(err->context.back())) - err->context.push_back(new valexpr_context(expr)); - error_context * last = err->context.back(); - if (valexpr_context * ctxt = dynamic_cast(last)) { - ctxt->expr = expr; - ctxt->desc = "While computing amount expression:"; - } - throw err; - } - return true; -} - -void scope_t::define(const string& name, const value_t& val) { - define(name, op_t::wrap_value(val)); -} - -value_t scope_t::resolve(const string& name) { - ptr_op_t definition = lookup(name); - if (definition) - return definition->calc(*this); - else - return NULL_VALUE; -} - -void symbol_scope_t::define(const string& name, ptr_op_t def) -{ - DEBUG("ledger.xpath.syms", "Defining '" << name << "' = " << def); - - std::pair result - = symbols.insert(symbol_map::value_type(name, def)); - if (! result.second) { - symbol_map::iterator i = symbols.find(name); - assert(i != symbols.end()); - symbols.erase(i); - - std::pair result2 - = symbols.insert(symbol_map::value_type(name, def)); - if (! result2.second) - throw_(compile_error, - "Redefinition of '" << name << "' in same scope"); - } -} - -namespace { - int count_leaves(ptr_op_t expr) - { - int count = 0; - if (expr->kind != op_t::O_COMMA) { - count = 1; - } else { - count += count_leaves(expr->left()); - count += count_leaves(expr->right()); - } - return count; - } - - ptr_op_t reduce_leaves(ptr_op_t expr, const details_t& details, - ptr_op_t context) - { - if (! expr) - return NULL; - - value_expr temp; - - if (expr->kind != op_t::O_COMMA) { - if (expr->kind < op_t::TERMINALS) { - temp.reset(expr); - } else { - temp.reset(new op_t(op_t::VALUE)); - temp->set_value(NULL_VALUE); - expr->compute(temp->as_value_lval(), details, context); - } - } else { - 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)); - } - return temp.release(); - } - - ptr_op_t find_leaf(ptr_op_t context, int goal, long& found) - { - if (! context) - return NULL; - - if (context->kind != op_t::O_COMMA) { - if (goal == found++) - return context; - } else { - ptr_op_t expr = find_leaf(context->left(), goal, found); - if (expr) - return expr; - expr = find_leaf(context->right(), goal, found); - if (expr) - return expr; - } - return NULL; - } -} - -value_t get_amount(scope_t& scope) -{ - assert("I can't get the amount!"); -} - -ptr_op_t symbol_scope_t::lookup(const string& name) -{ - switch (name[0]) { - case 'a': - if (name[1] == '\0' || name == "amount") - return WRAP_FUNCTOR(bind(get_amount, _1)); - break; - } - - symbol_map::const_iterator i = symbols.find(name); - if (i != symbols.end()) - return (*i).second; - - return child_scope_t::lookup(name); -} - - -void op_t::compute(value_t& result, const details_t& details, - ptr_op_t context) const -{ - try { - switch (kind) { - case ARG_INDEX: - throw new compute_error("Cannot directly compute an arg_index"); - - case VALUE: - result = as_value(); - break; - - case F_NOW: - result = terminus; - break; - - case AMOUNT: - if (details.xact) { - if (transaction_has_xdata(*details.xact) && - transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOUND) - result = transaction_xdata_(*details.xact).value; - else - result = details.xact->amount; - } - else if (details.account && account_has_xdata(*details.account)) { - result = account_xdata(*details.account).value; - } - else { - result = 0L; - } - break; - - case PRICE: - if (details.xact) { - bool set = false; - if (transaction_has_xdata(*details.xact)) { - transaction_xdata_t& xdata(transaction_xdata_(*details.xact)); - if (xdata.dflags & TRANSACTION_COMPOUND) { - result = xdata.value.value(); - set = true; - } - } - if (! set) { - optional value = details.xact->amount.value(); - if (value) - result = *value; - else - result = 0L; - } - } - else if (details.account && account_has_xdata(*details.account)) { - result = account_xdata(*details.account).value.value(); - } - else { - result = 0L; - } - break; - - case COST: - if (details.xact) { - bool set = false; - if (transaction_has_xdata(*details.xact)) { - transaction_xdata_t& xdata(transaction_xdata_(*details.xact)); - if (xdata.dflags & TRANSACTION_COMPOUND) { - result = xdata.value.cost(); - set = true; - } - } - - if (! set) { - if (details.xact->cost) - result = *details.xact->cost; - else - result = details.xact->amount; - } - } - else if (details.account && account_has_xdata(*details.account)) { - result = account_xdata(*details.account).value.cost(); - } - else { - result = 0L; - } - break; - - case TOTAL: - if (details.xact && transaction_has_xdata(*details.xact)) - result = transaction_xdata_(*details.xact).total; - else if (details.account && account_has_xdata(*details.account)) - result = account_xdata(*details.account).total; - else - result = 0L; - break; - case PRICE_TOTAL: - if (details.xact && transaction_has_xdata(*details.xact)) - result = transaction_xdata_(*details.xact).total.value(); - else if (details.account && account_has_xdata(*details.account)) - result = account_xdata(*details.account).total.value(); - else - result = 0L; - break; - case COST_TOTAL: - if (details.xact && transaction_has_xdata(*details.xact)) - result = transaction_xdata_(*details.xact).total.cost(); - else if (details.account && account_has_xdata(*details.account)) - result = account_xdata(*details.account).total.cost(); - else - result = 0L; - break; - - case VALUE_EXPR: - if (value_expr::amount_expr.get()) - value_expr::amount_expr->compute(result, details, context); - else - result = 0L; - break; - case TOTAL_EXPR: - if (value_expr::total_expr.get()) - value_expr::total_expr->compute(result, details, context); - else - result = 0L; - break; - - case DATE: - if (details.xact && transaction_has_xdata(*details.xact) && - is_valid(transaction_xdata_(*details.xact).date)) - result = transaction_xdata_(*details.xact).date; - else if (details.xact) - result = details.xact->date(); - else if (details.entry) - result = details.entry->date(); - else - result = terminus; - break; - - case ACT_DATE: - if (details.xact && transaction_has_xdata(*details.xact) && - is_valid(transaction_xdata_(*details.xact).date)) - result = transaction_xdata_(*details.xact).date; - else if (details.xact) - result = details.xact->actual_date(); - else if (details.entry) - result = details.entry->actual_date(); - else - result = terminus; - break; - - case EFF_DATE: - if (details.xact && transaction_has_xdata(*details.xact) && - is_valid(transaction_xdata_(*details.xact).date)) - result = transaction_xdata_(*details.xact).date; - else if (details.xact) - result = details.xact->effective_date(); - else if (details.entry) - result = details.entry->effective_date(); - else - result = terminus; - break; - - case CLEARED: - if (details.xact) - result = details.xact->state == transaction_t::CLEARED; - else - result = false; - break; - case PENDING: - if (details.xact) - result = details.xact->state == transaction_t::PENDING; - else - result = false; - break; - - case REAL: - if (details.xact) - result = ! (details.xact->has_flags(TRANSACTION_VIRTUAL)); - else - result = true; - break; - - case ACTUAL: - if (details.xact) - result = ! (details.xact->has_flags(TRANSACTION_AUTO)); - else - result = true; - break; - - case INDEX: - if (details.xact && transaction_has_xdata(*details.xact)) - result = long(transaction_xdata_(*details.xact).index + 1); - else if (details.account && account_has_xdata(*details.account)) - result = long(account_xdata(*details.account).count); - else - result = 0L; - break; - - case COUNT: - if (details.xact && transaction_has_xdata(*details.xact)) - result = long(transaction_xdata_(*details.xact).index + 1); - else if (details.account && account_has_xdata(*details.account)) - result = long(account_xdata(*details.account).total_count); - else - result = 0L; - break; - - case DEPTH: - if (details.account) - result = long(details.account->depth); - else - result = 0L; - break; - - case F_PRICE: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - result = result.value(); - break; - } - - case F_DATE: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - result = result.as_datetime(); - break; - } - - case F_DATECMP: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - result = result.as_datetime(); - if (! result) - break; - - arg_index = 0; - expr = find_leaf(context, 1, arg_index); - value_t moment; - expr->compute(moment, details, context); - if (moment.is_type(value_t::DATETIME)) { - result.cast(value_t::INTEGER); - moment.cast(value_t::INTEGER); - result -= moment; - } else { - throw new compute_error("Invalid date passed to datecmp(value,date)", - new valexpr_context(expr)); - } - break; - } - - case F_YEAR: - case F_MONTH: - case F_DAY: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - - if (! result.is_type(value_t::DATETIME)) - throw new compute_error("Invalid date passed to year|month|day(date)", - new valexpr_context(expr)); - - const datetime_t& moment(result.as_datetime()); - switch (kind) { - case F_YEAR: - result = (long)moment.date().year(); - break; - case F_MONTH: - result = (long)moment.date().month(); - break; - case F_DAY: - result = (long)moment.date().day(); - break; - default: - break; - } - break; - } - - case F_ARITH_MEAN: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - if (details.xact && transaction_has_xdata(*details.xact)) { - expr->compute(result, details, context); - result /= amount_t(long(transaction_xdata_(*details.xact).index + 1)); - } - else if (details.account && account_has_xdata(*details.account) && - account_xdata(*details.account).total_count) { - expr->compute(result, details, context); - result /= amount_t(long(account_xdata(*details.account).total_count)); - } - else { - result = 0L; - } - break; - } - - case F_PARENT: - if (details.account && details.account->parent) - left()->compute(result, details_t(*details.account->parent), context); - break; - - case F_ABS: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - result.abs(); - break; - } - - case F_ROUND: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - result.round(); - break; - } - - case F_COMMODITY: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - if (! result.is_type(value_t::AMOUNT)) - throw new compute_error("Argument to commodity() must be a commoditized amount", - new valexpr_context(expr)); - amount_t temp("1"); - temp.set_commodity(result.as_amount().commodity()); - result = temp; - break; - } - - case F_SET_COMMODITY: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - value_t temp; - expr->compute(temp, details, context); - - arg_index = 0; - expr = find_leaf(context, 1, arg_index); - expr->compute(result, details, context); - if (! result.is_type(value_t::AMOUNT)) - throw new compute_error - ("Second argument to set_commodity() must be a commoditized amount", - new valexpr_context(expr)); - amount_t one("1"); - one.set_commodity(result.as_amount().commodity()); - result = one; - - result *= temp; - break; - } - - case F_QUANTITY: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - - const balance_t * bal = NULL; - switch (result.type()) { - case value_t::BALANCE_PAIR: - bal = &(result.as_balance_pair().quantity()); - // fall through... - - case value_t::BALANCE: - if (! bal) - bal = &result.as_balance(); - - if (bal->amounts.size() < 2) { - result.cast(value_t::AMOUNT); - } else { - value_t temp; - for (balance_t::amounts_map::const_iterator i = bal->amounts.begin(); - i != bal->amounts.end(); - i++) { - amount_t x = (*i).second; - x.clear_commodity(); - temp += x; - } - result = temp; - assert(temp.is_type(value_t::AMOUNT)); - } - // fall through... - - case value_t::AMOUNT: - result.as_amount_lval().clear_commodity(); - break; - - default: - break; - } - break; - } - - case F_CODE_MASK: - if (details.entry && details.entry->code) - result = as_mask().match(*details.entry->code); - else - result = false; - break; - - case F_PAYEE_MASK: - if (details.entry) - result = as_mask().match(details.entry->payee); - else - result = false; - break; - - case F_NOTE_MASK: - if (details.xact && details.xact->note) - result = as_mask().match(*details.xact->note); - else - result = false; - break; - - case F_ACCOUNT_MASK: - if (details.account) - result = as_mask().match(details.account->fullname()); - else - result = false; - break; - - case F_SHORT_ACCOUNT_MASK: - if (details.account) - result = as_mask().match(details.account->name); - else - result = false; - break; - - case F_COMMODITY_MASK: - if (details.xact) - result = as_mask().match(details.xact->amount.commodity().base_symbol()); - else - result = false; - break; - - case O_ARG: { - long arg_index = 0; - assert(left()->kind == ARG_INDEX); - ptr_op_t expr = find_leaf(context, left()->as_long(), arg_index); - if (expr) - expr->compute(result, details, context); - else - result = 0L; - break; - } - - case O_COMMA: - if (! left()) - throw new compute_error("Comma operator missing left operand", - new valexpr_context(const_cast(this))); - if (! right()) - throw new compute_error("Comma operator missing right operand", - new valexpr_context(const_cast(this))); - left()->compute(result, details, context); - right()->compute(result, details, context); - break; - - case O_DEF: - result = 0L; - break; - - case O_REF: { - assert(left()); - if (right()) { - value_expr args(reduce_leaves(right(), details, context)); - left()->compute(result, details, args.get()); - } else { - left()->compute(result, details, context); - } - break; - } - - case F_VALUE: { - long arg_index = 0; - ptr_op_t expr = find_leaf(context, 0, arg_index); - expr->compute(result, details, context); - - arg_index = 0; - expr = find_leaf(context, 1, arg_index); - value_t moment; - expr->compute(moment, details, context); - if (! moment.is_type(value_t::DATETIME)) - throw new compute_error("Invalid date passed to P(value,date)", - new valexpr_context(expr)); - - result = result.value(moment.as_datetime()); - break; - } - - case O_NOT: - left()->compute(result, details, context); - if (result.strip_annotations()) - result = false; - else - result = true; - break; - - case O_QUES: { - assert(left()); - assert(right()); - assert(right()->kind == O_COL); - left()->compute(result, details, context); - if (result.strip_annotations()) - right()->left()->compute(result, details, context); - else - right()->right()->compute(result, details, context); - break; - } - - case O_AND: - assert(left()); - assert(right()); - left()->compute(result, details, context); - result = result.strip_annotations(); - if (result) - right()->compute(result, details, context); - break; - - case O_OR: - assert(left()); - assert(right()); - left()->compute(result, details, context); - if (! result.strip_annotations()) - right()->compute(result, details, context); - break; - - case O_NEQ: - case O_EQ: - case O_LT: - case O_LTE: - case O_GT: - case O_GTE: { - assert(left()); - assert(right()); - value_t temp; - left()->compute(temp, details, context); - right()->compute(result, details, context); - switch (kind) { - case O_NEQ: result = temp != result; break; - case O_EQ: result = temp == result; break; - case O_LT: result = temp < result; break; - case O_LTE: result = temp <= result; break; - case O_GT: result = temp > result; break; - case O_GTE: result = temp >= result; break; - default: assert(false); break; - } - break; - } - - case O_NEG: - assert(left()); - left()->compute(result, details, context); - result.negate(); - break; - - case O_ADD: - case O_SUB: - case O_MUL: - case O_DIV: { - assert(left()); - assert(right()); - value_t temp; - right()->compute(temp, details, context); - left()->compute(result, details, context); - switch (kind) { - case O_ADD: result += temp; break; - case O_SUB: result -= temp; break; - case O_MUL: result *= temp; break; - case O_DIV: result /= temp; break; - default: assert(false); break; - } - break; - } - - case O_PERC: { - assert(left()); - result = "100.0%"; - value_t temp; - left()->compute(temp, details, context); - result *= temp; - break; - } - - case LAST: - default: - assert(false); - break; - } - } - catch (error * err) { - if (err->context.empty() || - ! dynamic_cast(err->context.back())) - err->context.push_back(new valexpr_context(const_cast(this))); - throw err; - } -} - -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 << " "; -#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 op_t::compile(scope_t& scope) -{ - switch (kind) { -#if 0 - 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; -#endif - - default: - break; - } - - if (kind < TERMINALS) - return this; - - ptr_op_t lhs(left()->compile(scope)); - ptr_op_t rhs(right() ? right()->compile(scope) : ptr_op_t()); - - if (lhs == left() && (! rhs || rhs == right())) - return this; - - ptr_op_t intermediate(copy(lhs, rhs)); - - if (lhs->is_value() && (! rhs || rhs->is_value())) - return wrap_value(intermediate->calc(scope)); - - return intermediate; -} - - -value_t op_t::calc(scope_t& scope) -{ -#if 0 - bool find_all_nodes = false; -#endif - - switch (kind) { - case VALUE: - return as_value(); - -#if 0 - case VAR_NAME: - case FUNC_NAME: - if (ptr_op_t reference = compile(scope)) { - return reference->calc(scope); - } else { - throw_(calc_error, "No " << (kind == VAR_NAME ? "variable" : "function") - << " named '" << as_string() << "'"); - } - break; -#endif - - case FUNCTION: - // This should never be evaluated directly; it only appears as the - // left node of an O_CALL operator. - assert(false); - break; - -#if 0 - case O_CALL: { - call_scope_t call_args(scope); - - if (right()) - call_args.set_args(right()->calc(scope)); - - ptr_op_t func = left(); - string name; - - if (func->kind == FUNC_NAME) { - name = func->as_string(); - func = func->compile(scope); - } - - if (func->kind != FUNCTION) - throw_(calc_error, - name.empty() ? string("Attempt to call non-function") : - (string("Attempt to call unknown function '") + name + "'")); - - return func->as_function()(call_args); - } -#endif - - case ARG_INDEX: { - call_scope_t& args(CALL_SCOPE(scope)); - - if (as_long() >= 0 && as_long() < args.size()) - return args[as_long()]; - else - throw_(calc_error, "Reference to non-existing argument"); - break; - } - -#if 0 - case O_FIND: - case O_RFIND: - return select_nodes(scope, left()->calc(scope), right(), kind == O_RFIND); - - case O_PRED: { - value_t values = left()->calc(scope); - - if (! values.is_null()) { - op_predicate pred(right()); - - 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(); - - value_t result; - - 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; - } - - case NODE_ID: - switch (as_name()) { - case document_t::CURRENT: - return current_value(scope); - - case document_t::PARENT: - if (optional parent = current_xml_node(scope).parent()) - return &*parent; - else - throw_(std::logic_error, "Attempt to access parent of root node"); - break; - - case document_t::ROOT: - return ¤t_xml_node(scope).document(); - - case document_t::ALL: - find_all_nodes = true; - break; - - default: - break; // pass down to the NODE_NAME case - } - // fall through... - - case NODE_NAME: { - node_t& current_node(current_xml_node(scope)); - - if (current_node.is_parent_node()) { - const bool have_name_id = kind == NODE_ID; - - parent_node_t& parent(current_node.as_parent_node()); - - 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); - } - return result; - } - break; - } - - case ATTR_ID: - case ATTR_NAME: - if (optional value = - kind == ATTR_ID ? current_xml_node(scope).get_attr(as_name()) : - current_xml_node(scope).get_attr(as_string())) - return *value; - - break; -#endif - - case O_NEQ: - return left()->calc(scope) != right()->calc(scope); - case O_EQ: - return left()->calc(scope) == right()->calc(scope); - case O_LT: - return left()->calc(scope) < right()->calc(scope); - case O_LTE: - return left()->calc(scope) <= right()->calc(scope); - case O_GT: - return left()->calc(scope) > right()->calc(scope); - case O_GTE: - return left()->calc(scope) >= right()->calc(scope); - - case O_ADD: - return left()->calc(scope) + right()->calc(scope); - case O_SUB: - return left()->calc(scope) - right()->calc(scope); - case O_MUL: - return left()->calc(scope) * right()->calc(scope); - case O_DIV: - return left()->calc(scope) / right()->calc(scope); - - case O_NEG: - assert(! right()); - return left()->calc(scope).negate(); - - case O_NOT: - assert(! right()); - return ! left()->calc(scope); - - case O_AND: - return left()->calc(scope) && right()->calc(scope); - case O_OR: - return left()->calc(scope) || right()->calc(scope); - -#if 0 - case O_UNION: -#endif - case O_COMMA: { - value_t result(left()->calc(scope)); - - ptr_op_t next = right(); - while (next) { - ptr_op_t value_op; - if (next->kind == O_COMMA /* || next->kind == O_UNION */) { - value_op = next->left(); - next = next->right(); - } else { - value_op = next; - next = NULL; - } - - result.push_back(value_op->calc(scope)); - } - return result; - } - - case LAST: - default: - assert(false); - break; - } - - return NULL_VALUE; -} - -} // namespace expr - -std::auto_ptr value_expr::amount_expr; -std::auto_ptr value_expr::total_expr; -std::auto_ptr value_expr::parser; - -void value_expr::initialize() -{ - parser.reset(new expr::parser_t); -} - -void value_expr::shutdown() -{ - amount_expr.reset(); - total_expr.reset(); - parser.reset(); -} - -value_expr::value_expr(const string& _expr_str) : expr_str(_expr_str) -{ - TRACE_CTOR(value_expr, "const string&"); - - if (! _expr_str.empty()) - ptr = parser->parse(expr_str).ptr; -} - -} // namespace ledger diff --git a/valexpr.h b/valexpr.h deleted file mode 100644 index 7c2a63a1..00000000 --- a/valexpr.h +++ /dev/null @@ -1,1007 +0,0 @@ -/* - * Copyright (c) 2003-2008, John Wiegley. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of New Artisans LLC nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _VALEXPR_H -#define _VALEXPR_H - -#include "value.h" -#include "utils.h" -#include "mask.h" - -namespace ledger { - -class entry_t; -class transaction_t; -class account_t; - -namespace expr { - -DECLARE_EXCEPTION(error, compile_error); -DECLARE_EXCEPTION(error, calc_error); - -#if 0 -struct context_t -{ - const entry_t * entry() { - return NULL; - } - const transaction_t * xact() { - return NULL; - } - const account_t * account() { - return NULL; - } -}; - -struct entry_context_t : public context_t -{ - const entry_t * entry_; - - const entry_t * entry() { - return entry_; - } -}; - -struct xact_context_t : public context_t -{ - const transaction_t * xact_; - - const entry_t * entry() { - return xact_->entry; - } - const transaction_t * xact() { - return xact_; - } - const account_t * account() { - return xact_->account; - } -}; - -struct account_context_t : public context_t -{ - const account_t * account_; - - const account_t * account() { - return account_; - } -}; -#endif - -struct details_t -{ - const entry_t * entry; - const transaction_t * xact; - const account_t * account; - - details_t() : entry(NULL), xact(NULL), account(NULL) { - TRACE_CTOR(details_t, ""); - } - details_t(const details_t& other) - : entry(other.entry), - xact(other.xact), - account(other.account) { - TRACE_CTOR(details_t, "copy"); - } - details_t(const entry_t& _entry) - : entry(&_entry), xact(NULL), account(NULL) { - TRACE_CTOR(details_t, "const entry_t&"); - } - details_t(const transaction_t& _xact); - details_t(const account_t& _account) - : entry(NULL), xact(NULL), account(&_account) { - TRACE_CTOR(details_t, "const account_t&"); - } - ~details_t() throw() { - TRACE_DTOR(details_t); - } -}; - -struct op_t; -typedef intrusive_ptr ptr_op_t; - -class call_scope_t; - -typedef function function_t; - -#define MAKE_FUNCTOR(x) expr::op_t::wrap_functor(bind(&x, this, _1)) -#define WRAP_FUNCTOR(x) expr::op_t::wrap_functor(x) - -class scope_t : public noncopyable -{ - scope_t(); - -public: - enum type_t { - CHILD_SCOPE, - SYMBOL_SCOPE, - CALL_SCOPE, - CONTEXT_SCOPE - } type_; - - explicit scope_t(type_t _type) : type_(_type) { - TRACE_CTOR(scope_t, "type_t"); - } - virtual ~scope_t() { - TRACE_DTOR(scope_t); - } - - const type_t type() const { - return type_; - } - - virtual void define(const string& name, ptr_op_t def) = 0; - void define(const string& name, const value_t& val); - virtual ptr_op_t lookup(const string& name) = 0; - value_t resolve(const string& name); - - virtual optional find_scope(const type_t _type, - bool skip_this = false) = 0; - virtual optional find_first_scope(const type_t _type1, - const type_t _type2, - bool skip_this = false) = 0; - - template - T& find_scope(bool skip_this = false) { - assert(false); - } - template - optional maybe_find_scope(bool skip_this = false) { - assert(false); - } -}; - -class child_scope_t : public scope_t -{ - scope_t * parent; - - child_scope_t(); - -public: - explicit child_scope_t(type_t _type = CHILD_SCOPE) - : scope_t(_type), parent(NULL) { - TRACE_CTOR(child_scope_t, "type_t"); - } - explicit child_scope_t(scope_t& _parent, type_t _type = CHILD_SCOPE) - : scope_t(_type), parent(&_parent) { - TRACE_CTOR(child_scope_t, "scope_t&, type_t"); - } - virtual ~child_scope_t() { - TRACE_DTOR(child_scope_t); - } -public: - virtual void define(const string& name, ptr_op_t def) { - if (parent) - parent->define(name, def); - } - virtual ptr_op_t lookup(const string& name) { - if (parent) - return parent->lookup(name); - return ptr_op_t(); - } - - virtual optional find_scope(type_t _type, - bool skip_this = false) { - for (scope_t * ptr = (skip_this ? parent : this); ptr; ) { - if (ptr->type() == _type) - return *ptr; - - ptr = polymorphic_downcast(ptr)->parent; - } - return none; - } - - virtual optional find_first_scope(const type_t _type1, - const type_t _type2, - bool skip_this = false) { - for (scope_t * ptr = (skip_this ? parent : this); ptr; ) { - if (ptr->type() == _type1 || ptr->type() == _type2) - return *ptr; - - ptr = polymorphic_downcast(ptr)->parent; - } - return none; - } -}; - -class symbol_scope_t : public child_scope_t -{ - typedef std::map symbol_map; - symbol_map symbols; - -public: - explicit symbol_scope_t() - : child_scope_t(SYMBOL_SCOPE) { - TRACE_CTOR(symbol_scope_t, ""); - } - explicit symbol_scope_t(scope_t& _parent) - : child_scope_t(_parent, SYMBOL_SCOPE) { - TRACE_CTOR(symbol_scope_t, "scope_t&"); - } - virtual ~symbol_scope_t() { - TRACE_DTOR(symbol_scope_t); - } - - virtual void define(const string& name, ptr_op_t def); - void define(const string& name, const value_t& val) { - scope_t::define(name, val); - } - virtual ptr_op_t lookup(const string& name); -}; - -class call_scope_t : public child_scope_t -{ - value_t args; - - call_scope_t(); - -public: - explicit call_scope_t(scope_t& _parent) - : child_scope_t(_parent, CALL_SCOPE) { - TRACE_CTOR(call_scope_t, "scope_t&"); - } - virtual ~call_scope_t() { - TRACE_DTOR(call_scope_t); - } - - void set_args(const value_t& _args) { - args = _args; - } - - value_t& value() { - return args; - } - - value_t& operator[](const unsigned int index) { - // jww (2008-07-21): exception here if it's out of bounds - return args[index]; - } - const value_t& operator[](const unsigned int index) const { - // jww (2008-07-21): exception here if it's out of bounds - return args[index]; - } - - void push_back(const value_t& val) { - args.push_back(val); - } - void pop_back() { - args.pop_back(); - } - - const std::size_t size() const { - return args.size(); - } -}; - -template -class var_t : public noncopyable -{ - T * value; - - var_t(); - -public: - // jww (2008-07-21): Give a good exception here if we can't find "name" - var_t(scope_t& scope, const string& name) - : value(scope.resolve(name).template as_pointer()) { - TRACE_CTOR(var_t, "scope_t&, const string&"); - } - var_t(call_scope_t& scope, const unsigned int idx) - : value(scope[idx].template as_pointer()) { - TRACE_CTOR(var_t, "call_scope_t&, const unsigned int"); - } - ~var_t() throw() { - TRACE_DTOR(var_t); - } - - T& operator *() { return *value; } - T * operator->() { return value; } -}; - -#if 0 -class context_scope_t : public child_scope_t -{ -public: - value_t current_element; - std::size_t element_index; - std::size_t sequence_size; - - explicit context_scope_t(scope_t& _parent, - const value_t& _element = NULL_VALUE, - const std::size_t _element_index = 0, - const std::size_t _sequence_size = 0) - : child_scope_t(_parent, CONTEXT_SCOPE), current_element(_element), - element_index(_element_index), sequence_size(_sequence_size) - { - TRACE_CTOR(expr::context_scope_t, "scope_t&, const value_t&, ..."); - } - virtual ~context_scope_t() { - TRACE_DTOR(expr::context_scope_t); - } - - const std::size_t index() const { - return element_index; - } - const std::size_t size() const { - return sequence_size; - } - - value_t& value() { - return current_element; - } -}; -#endif - -class op_t : public noncopyable -{ - op_t(); - -public: - enum kind_t { - // Constants - VALUE, - MASK, - ARG_INDEX, - - CONSTANTS, - - // Item details - AMOUNT, - COST, - PRICE, - DATE, - ACT_DATE, - EFF_DATE, - CLEARED, - PENDING, - REAL, - ACTUAL, - INDEX, - DEPTH, - - // Item totals - COUNT, - TOTAL, - COST_TOTAL, - PRICE_TOTAL, - - // Relating to format_t - VALUE_EXPR, - TOTAL_EXPR, - - // Functions - FUNCTION, - - F_NOW, - F_ARITH_MEAN, - F_QUANTITY, - F_COMMODITY, - F_SET_COMMODITY, - F_VALUE, - F_ABS, - F_ROUND, - F_PRICE, - F_DATE, - F_DATECMP, - F_YEAR, - F_MONTH, - F_DAY, - - BEGIN_MASKS, - F_CODE_MASK, - F_PAYEE_MASK, - F_NOTE_MASK, - F_ACCOUNT_MASK, - F_SHORT_ACCOUNT_MASK, - F_COMMODITY_MASK, - END_MASKS, - - TERMINALS, - - F_PARENT, - - // Binary operators - O_NEG, - O_ADD, - O_SUB, - O_MUL, - O_DIV, - O_PERC, - O_NEQ, - O_EQ, - O_LT, - O_LTE, - O_GT, - O_GTE, - O_NOT, - O_AND, - O_OR, - O_QUES, - O_COL, - O_COMMA, - O_DEF, - O_REF, - O_ARG, - O_LOOKUP, - - LAST - }; - - kind_t kind; - mutable short refc; - ptr_op_t left_; - - variant // used by all binary operators - data; - - explicit op_t(const kind_t _kind) : kind(_kind), refc(0){ - TRACE_CTOR(op_t, "const kind_t"); - } - ~op_t() { - TRACE_DTOR(op_t); - assert(refc == 0); - } - - bool is_long() const { - return data.type() == typeid(unsigned int); - } - unsigned int& as_long_lval() { - assert(kind == ARG_INDEX || kind == O_ARG); - return boost::get(data); - } - const unsigned int& as_long() const { - return const_cast(this)->as_long_lval(); - } - void set_long(unsigned int val) { - data = val; - } - - bool is_value() const { - if (kind == VALUE) { - assert(data.type() == typeid(value_t)); - return true; - } - return false; - } - value_t& as_value_lval() { - assert(is_value()); - value_t& val(boost::get(data)); - assert(val.valid()); - return val; - } - const value_t& as_value() const { - return const_cast(this)->as_value_lval(); - } - void set_value(const value_t& val) { - assert(val.valid()); - data = val; - } - - bool is_string() const { - if (kind == VALUE) { - assert(data.type() == typeid(value_t)); - return boost::get(data).is_string(); - } - return false; - } - string& as_string_lval() { - assert(is_string()); - return boost::get(data).as_string_lval(); - } - const string& as_string() const { - return const_cast(this)->as_string_lval(); - } - void set_string(const string& val) { - data = value_t(val); - } - - bool is_mask() const { - if (kind > BEGIN_MASKS && kind < END_MASKS) { - assert(data.type() == typeid(mask_t)); - return true; - } - return false; - } - mask_t& as_mask_lval() { - assert(is_mask()); - return boost::get(data); - } - const mask_t& as_mask() const { - return const_cast(this)->as_mask_lval(); - } - void set_mask(const mask_t& val) { - data = val; - } - void set_mask(const string& expr) { - data = mask_t(expr); - } - - bool is_function() const { - return kind == FUNCTION; - } - function_t& as_function_lval() { - assert(kind == FUNCTION); - return boost::get(data); - } - const function_t& as_function() const { - return const_cast(this)->as_function_lval(); - } - void set_function(const function_t& val) { - data = val; - } - -#if 0 - bool is_name() const { - return data.type() == typeid(node_t::nameid_t); - } - node_t::nameid_t& as_name_lval() { - assert(kind == NODE_ID || kind == ATTR_ID); - return boost::get(data); - } - const node_t::nameid_t& as_name() const { - return const_cast(this)->as_name_lval(); - } - void set_name(const node_t::nameid_t& val) { - data = val; - } -#endif - - ptr_op_t& as_op_lval() { - assert(kind > TERMINALS); - return boost::get(data); - } - const ptr_op_t& as_op() const { - return const_cast(this)->as_op_lval(); - } - - void acquire() const { - DEBUG("ledger.xpath.memory", - "Acquiring " << this << ", refc now " << refc + 1); - assert(refc >= 0); - refc++; - } - void release() const { - DEBUG("ledger.xpath.memory", - "Releasing " << this << ", refc now " << refc - 1); - assert(refc > 0); - if (--refc == 0) - checked_delete(this); - } - - ptr_op_t& left() { - return left_; - } - const ptr_op_t& left() const { - assert(kind > TERMINALS); - return left_; - } - void set_left(const ptr_op_t& expr) { - assert(kind > TERMINALS); - left_ = expr; - } - - ptr_op_t& right() { - assert(kind > TERMINALS); - return as_op_lval(); - } - const ptr_op_t& right() const { - assert(kind > TERMINALS); - return as_op(); - } - void set_right(const ptr_op_t& expr) { - assert(kind > TERMINALS); - data = expr; - } - - static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL, - ptr_op_t _right = NULL); - ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const { - return new_node(kind, _left, _right); - } - - static ptr_op_t wrap_value(const value_t& val); - static ptr_op_t wrap_functor(const function_t& fobj); - - ptr_op_t compile(scope_t& scope); - value_t current_value(scope_t& scope); -#if 0 - node_t& current_xml_node(scope_t& scope); -#endif - value_t calc(scope_t& scope); - - void compute(value_t& result, - const details_t& details = details_t(), - ptr_op_t context = NULL) const; - - value_t compute(const details_t& details = details_t(), - ptr_op_t context = NULL) const { - value_t temp; - compute(temp, details, context); - return temp; - } - - struct print_context_t - { - scope_t& scope; - const bool relaxed; - const ptr_op_t& op_to_find; - unsigned long * start_pos; - unsigned long * end_pos; - - print_context_t(scope_t& _scope, - const bool _relaxed = false, - const ptr_op_t& _op_to_find = ptr_op_t(), - unsigned long * _start_pos = NULL, - unsigned long * _end_pos = NULL) - : scope(_scope), relaxed(_relaxed), op_to_find(_op_to_find), - start_pos(_start_pos), end_pos(_end_pos) {} - }; - - bool print(std::ostream& out, print_context_t& context) const; - void dump(std::ostream& out, const int depth) const; - - friend inline void intrusive_ptr_add_ref(op_t * op) { - op->acquire(); - } - friend inline void intrusive_ptr_release(op_t * op) { - op->release(); - } -}; - -class op_predicate : public noncopyable -{ - ptr_op_t op; - - op_predicate(); - -public: - explicit op_predicate(ptr_op_t _op) : op(_op) { - TRACE_CTOR(op_predicate, "ptr_op_t"); - } - ~op_predicate() throw() { - TRACE_DTOR(op_predicate); - } - bool operator()(scope_t& scope) { - return op->calc(scope).to_boolean(); - } -}; - -class valexpr_context : public error_context -{ -public: - ptr_op_t expr; - ptr_op_t error_node; - - valexpr_context(const ptr_op_t& _expr, - const string& desc = "") throw() - : error_context(desc), expr(_expr), error_node(_expr) {} - virtual ~valexpr_context() throw() {} - - virtual void describe(std::ostream& out) const throw(); -}; - -class compute_error : public error -{ -public: - compute_error(const string& reason, error_context * ctxt = NULL) throw() - : error(reason, ctxt) {} - virtual ~compute_error() throw() {} -}; - -class value_expr_error : public error -{ -public: - value_expr_error(const string& reason, - error_context * ctxt = NULL) throw() - : error(reason, ctxt) {} - virtual ~value_expr_error() throw() {} -}; - -extern std::auto_ptr global_scope; - -extern datetime_t terminus; - -bool compute_amount(const ptr_op_t expr, amount_t& amt, - const transaction_t * xact, - const ptr_op_t context = NULL); - -////////////////////////////////////////////////////////////////////// - -inline void guarded_compute(const ptr_op_t expr, - value_t& result, - const details_t& details = details_t(), - const ptr_op_t context = NULL) { - try { - expr->compute(result, details); - } - catch (error * err) { - if (err->context.empty() || - ! dynamic_cast(err->context.back())) - err->context.push_back(new valexpr_context(expr)); - error_context * last = err->context.back(); - if (valexpr_context * ctxt = dynamic_cast(last)) { - ctxt->expr = expr; - ctxt->desc = "While computing value expression:"; - } - throw err; - } -} - -inline value_t guarded_compute(const ptr_op_t expr, - const details_t& details = details_t(), - ptr_op_t context = NULL) { - value_t temp; - guarded_compute(expr, temp, details, context); - return temp; -} - -template<> -inline symbol_scope_t& -scope_t::find_scope(bool skip_this) { - optional scope = find_scope(SYMBOL_SCOPE, skip_this); - assert(scope); - return downcast(*scope); -} - -template<> -inline call_scope_t& -scope_t::find_scope(bool skip_this) { - optional scope = find_scope(CALL_SCOPE, skip_this); - assert(scope); - return downcast(*scope); -} - -#if 0 -template<> -inline context_scope_t& -scope_t::find_scope(bool skip_this) { - optional scope = find_scope(CONTEXT_SCOPE, skip_this); - assert(scope); - return downcast(*scope); -} -#endif - -#define FIND_SCOPE(scope_type, scope_ref) \ - downcast(scope_ref).find_scope() - -#define CALL_SCOPE(scope_ref) \ - FIND_SCOPE(call_scope_t, scope_ref) -#define SYMBOL_SCOPE(scope_ref) \ - FIND_SCOPE(symbol_scope_t, scope_ref) -#if 0 -#define CONTEXT_SCOPE(scope_ref) \ - FIND_SCOPE(context_scope_t, scope_ref) -#endif - -inline ptr_op_t op_t::new_node(kind_t _kind, ptr_op_t _left, ptr_op_t _right) { - ptr_op_t node(new op_t(_kind)); - node->set_left(_left); - node->set_right(_right); - return node; -} - -inline ptr_op_t op_t::wrap_value(const value_t& val) { - ptr_op_t temp(new op_t(op_t::VALUE)); - temp->set_value(val); - return temp; -} - -inline ptr_op_t op_t::wrap_functor(const function_t& fobj) { - ptr_op_t temp(new op_t(op_t::FUNCTION)); - temp->set_function(fobj); - return temp; -} - -class parser_t; - -} // namespace expr - -////////////////////////////////////////////////////////////////////// - -class value_expr -{ - expr::ptr_op_t ptr; - -public: - string expr_str; - - typedef expr::details_t details_t; - - value_expr() { - TRACE_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"); - } - value_expr(const value_expr& other) - : ptr(other.ptr), expr_str(other.expr_str) { - TRACE_CTOR(value_expr, "copy"); - } - virtual ~value_expr() throw() { - TRACE_DTOR(value_expr); - } - - value_expr& operator=(const value_expr& _expr) { - expr_str = _expr.expr_str; - reset(_expr.get()); - return *this; - } - value_expr& operator=(const string& _expr) { - return *this = value_expr(_expr); - } - - operator bool() const throw() { - return ptr.get() != NULL; - } - operator string() const throw() { - return expr_str; - } - operator const expr::ptr_op_t() const throw() { - return ptr; - } - - const expr::ptr_op_t operator->() const throw() { - return ptr; - } - - const expr::ptr_op_t get() const throw() { return ptr; } - const expr::ptr_op_t release() throw() { - const expr::ptr_op_t tmp = ptr; - ptr = expr::ptr_op_t(); - return tmp; - } - void reset(const expr::ptr_op_t p = expr::ptr_op_t()) throw() { - ptr = p; - } - - virtual void compute(value_t& result, - const details_t& details = details_t(), - expr::ptr_op_t context = NULL) { - guarded_compute(ptr, result, details, context); - } - virtual value_t compute(const details_t& details = details_t(), - expr::ptr_op_t context = NULL) { - value_t temp; - guarded_compute(ptr, temp, details, context); - return temp; - } - -#if 0 - 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); - } - - path_iterator_t find_all(scope_t& scope) { - return path_iterator_t(*this, scope); - } - - void print(std::ostream& out, scope_t& scope) const { - op_t::print_context_t context(scope); - print(out, context); - } - - void dump(std::ostream& out) const { - if (ptr) - ptr->dump(out, 0); - } -#endif - - friend bool print_value_expr(std::ostream& out, - const expr::ptr_op_t node, - const expr::ptr_op_t node_to_find, - unsigned long * start_pos, - unsigned long * end_pos); - - static std::auto_ptr amount_expr; - static std::auto_ptr total_expr; - static std::auto_ptr parser; - - static void initialize(); - static void shutdown(); -}; - -typedef value_expr::details_t details_t; // jww (2008-07-20): remove - -inline void compute_amount(value_t& result, - const details_t& details = details_t()) { - if (value_expr::amount_expr.get()) - value_expr::amount_expr->compute(result, details); -} - -inline value_t compute_amount(const details_t& details = details_t()) { - if (value_expr::amount_expr.get()) - return value_expr::amount_expr->compute(details); -} - -inline void compute_total(value_t& result, - const details_t& details = details_t()) { - if (value_expr::total_expr.get()) - value_expr::total_expr->compute(result, details); -} - -inline value_t compute_total(const details_t& details = details_t()) { - if (value_expr::total_expr.get()) - return value_expr::total_expr->compute(details); -} - -////////////////////////////////////////////////////////////////////// - -template -class item_predicate -{ -public: - value_expr predicate; - - item_predicate() { - TRACE_CTOR(item_predicate, ""); - } - item_predicate(const item_predicate& other) : predicate(other.predicate) { - TRACE_CTOR(item_predicate, "copy"); - } - item_predicate(const value_expr& _predicate) : predicate(_predicate) { - TRACE_CTOR(item_predicate, "const value_expr&"); - } - item_predicate(const string& _predicate) : predicate(_predicate) { - TRACE_CTOR(item_predicate, "const string&"); - } - ~item_predicate() throw() { - TRACE_DTOR(item_predicate); - } - - bool operator()(const T& item) const { - return (! predicate || - predicate->compute(value_expr::details_t(item)).strip_annotations()); - } -}; - -} // namespace ledger - -#endif // _VALEXPR_H -- cgit v1.2.3