diff options
author | John Wiegley <johnw@newartisans.com> | 2007-04-30 06:26:38 +0000 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-04-13 03:38:33 -0400 |
commit | c8899addfd2deed3d84be2de234681db64987722 (patch) | |
tree | 07f9a5eb603ff4ec83fe18c83083575d2b7a439a /src/xpath.cc | |
parent | aa9cc125796711afcaa459898e95527fdae8e912 (diff) | |
download | fork-ledger-c8899addfd2deed3d84be2de234681db64987722.tar.gz fork-ledger-c8899addfd2deed3d84be2de234681db64987722.tar.bz2 fork-ledger-c8899addfd2deed3d84be2de234681db64987722.zip |
Rearranged the sources a bit.
Diffstat (limited to 'src/xpath.cc')
-rw-r--r-- | src/xpath.cc | 2414 |
1 files changed, 2414 insertions, 0 deletions
diff --git a/src/xpath.cc b/src/xpath.cc new file mode 100644 index 00000000..6eb30d48 --- /dev/null +++ b/src/xpath.cc @@ -0,0 +1,2414 @@ +#include "xpath.h" +#include "parser.h" + +namespace ledger { +namespace xml { + +#ifndef THREADSAFE +xpath_t::token_t * xpath_t::lookahead = NULL; +#endif + +void xpath_t::initialize() +{ + lookahead = new xpath_t::token_t; +} + +void xpath_t::shutdown() +{ + delete lookahead; + lookahead = NULL; +} + +void xpath_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 == '.'); + + 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; + case 'u': + if (std::strcmp(buf, "union") == 0) + kind = KW_UNION; + break; + } + + if (kind == IDENT) + value.set_string(buf); +} + +void xpath_t::token_t::next(std::istream& in, unsigned short 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 & XPATH_PARSE_RELAXED) && + (std::isalpha(c) || c == '_')) { + parse_ident(in); + return; + } + + switch (c) { + case '@': + in.get(c); + kind = AT_SYM; + break; +#if 0 + case '$': + in.get(c); + kind = DOLLAR; + break; +#endif + + case '(': + in.get(c); + kind = LPAREN; + break; + case ')': + in.get(c); + kind = RPAREN; + break; + + case '[': { + in.get(c); + if (flags & XPATH_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.next(); + } else { + kind = LBRACKET; + } + break; + } + + case ']': { + in.get(c); + kind = RBRACKET; + break; + } + + case '"': { + in.get(c); + char buf[4096]; + READ_INTO_(in, buf, 4095, c, length, c != '"'); + if (c != '"') + unexpected(c, '"'); + 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; + } +#if 0 + else if (c == '~') { + in.get(c); + symbol[1] = c; + symbol[2] = '\0'; + kind = NMATCH; + length = 2; + break; + } +#endif + kind = EXCLAM; + break; + + case '-': + in.get(c); + kind = MINUS; + break; + case '+': + in.get(c); + kind = PLUS; + break; + + case '*': + in.get(c); + if (in.peek() == '*') { + in.get(c); + symbol[1] = c; + symbol[2] = '\0'; + kind = POWER; + length = 2; + break; + } + kind = STAR; + break; + + case '/': + in.get(c); +#if 0 + if (flags & XPATH_PARSE_REGEXP) { + char buf[1024]; + READ_INTO_(in, buf, 1023, c, length, c != '/'); + in.get(c); + if (c != '/') + unexpected(c, '/'); + kind = REGEXP; + value.set_string(buf); + break; + } +#endif + kind = SLASH; + break; + + case '=': + in.get(c); +#if 0 + if (in.peek() == '~') { + in.get(c); + symbol[1] = c; + symbol[2] = '\0'; + kind = MATCH; + length = 2; + break; + } +#endif + 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 = AMPER; + break; + case '|': + in.get(c); + kind = PIPE; + break; + case '?': + in.get(c); + kind = QUESTION; + break; + case ':': + in.get(c); + if (in.peek() == '=') { + in.get(c); + symbol[1] = c; + symbol[2] = '\0'; + kind = ASSIGN; + length = 2; + break; + } + kind = COLON; + break; + case ',': + in.get(c); + kind = COMMA; + break; +#if 0 + case '%': + in.get(c); + kind = PERCENT; + break; +#endif + + 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 & XPATH_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 & XPATH_PARSE_NO_MIGRATE) + parse_flags |= AMOUNT_PARSE_NO_MIGRATE; + if (flags & XPATH_PARSE_NO_REDUCE) + parse_flags |= AMOUNT_PARSE_NO_REDUCE; + + temp.parse(in, parse_flags); + + kind = VALUE; + value = temp; + } + catch (amount_exception& 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 xpath_t::token_t::rewind(std::istream& in) +{ + for (unsigned int i = 0; i < length; i++) + in.unget(); +} + + +void xpath_t::token_t::unexpected() +{ + switch (kind) { + case TOK_EOF: + throw_(parse_exception, "Unexpected end of expression"); + case IDENT: + throw_(parse_exception, "Unexpected symbol '" << value << "'"); + case VALUE: + throw_(parse_exception, "Unexpected value '" << value << "'"); + default: + throw_(parse_exception, "Unexpected operator '" << symbol << "'"); + } +} + +void xpath_t::token_t::unexpected(char c, char wanted) +{ + if ((unsigned char) c == 0xff) { + if (wanted) + throw_(parse_exception, "Missing '" << wanted << "'"); + else + throw_(parse_exception, "Unexpected end"); + } else { + if (wanted) + throw_(parse_exception, "Invalid char '" << c << + "' (wanted '" << wanted << "')"); + else + throw_(parse_exception, "Invalid char '" << c << "'"); + } +} + +xpath_t::op_t * xpath_t::wrap_value(const value_t& val) +{ + xpath_t::op_t * temp = new xpath_t::op_t(xpath_t::op_t::VALUE); + temp->valuep = new value_t(val); + return temp; +} + +xpath_t::op_t * xpath_t::wrap_sequence(value_t::sequence_t * val) +{ + if (val->size() == 0) + return wrap_value(false); + else if (val->size() == 1) + return wrap_value(val->front()); + else + return wrap_value(val); +} + +xpath_t::op_t * xpath_t::wrap_functor(functor_t * fobj) +{ + xpath_t::op_t * temp = new xpath_t::op_t(xpath_t::op_t::FUNCTOR); + temp->functor = fobj; + return temp; +} + +#if 0 +xpath_t::op_t * xpath_t::wrap_mask(const string& pattern) +{ + xpath_t::op_t * temp = new xpath_t::op_t(xpath_t::op_t::MASK); + temp->mask = new mask_t(pattern); + return temp; +} +#endif + +void xpath_t::scope_t::define(const string& name, op_t * def) +{ + DEBUG_("ledger.xpath.syms", "Defining '" << name << "' = " << def); + + std::pair<symbol_map::iterator, bool> result + = symbols.insert(symbol_pair(name, def)); + if (! result.second) { + symbol_map::iterator i = symbols.find(name); + assert(i != symbols.end()); + (*i).second->release(); + symbols.erase(i); + + std::pair<symbol_map::iterator, bool> result2 + = symbols.insert(symbol_pair(name, def)); + if (! result2.second) + throw_(compile_exception, + "Redefinition of '" << name << "' in same scope"); + } + def->acquire(); +} + +xpath_t::op_t * +xpath_t::scope_t::lookup(const string& name) +{ + symbol_map::const_iterator i = symbols.find(name); + if (i != symbols.end()) + return (*i).second; + else if (parent) + return parent->lookup(name); + return NULL; +} + +void xpath_t::scope_t::define(const string& name, functor_t * def) { + define(name, wrap_functor(def)); +} + +bool xpath_t::function_scope_t::resolve(const string& name, + value_t& result, + scope_t * locals) +{ + switch (name[0]) { + case 'l': + if (name == "last") { + if (sequence) + result = (long)sequence->size(); + else + result = 1L; + return true; + } + break; + + case 'p': + if (name == "position") { + result = (long)index + 1; + return true; + } + break; + + case 't': + if (name == "text") { + if (value->type == value_t::XML_NODE) + result.set_string(value->to_xml_node()->text()); + else + throw_(calc_exception, "Attempt to call text() on a non-node value"); + return true; + } + break; + } + return scope_t::resolve(name, result, locals); +} + +xpath_t::op_t::~op_t() +{ + TRACE_DTOR(xpath_t::op_t); + + DEBUG_("ledger.xpath.memory", "Destroying " << this); + assert(refc == 0); + + switch (kind) { + case VALUE: + assert(! left); + assert(valuep); + delete valuep; + break; + + case NODE_NAME: + case FUNC_NAME: + case ATTR_NAME: + case VAR_NAME: + assert(! left); + assert(name); + delete name; + break; + + case ARG_INDEX: + break; + + case FUNCTOR: + assert(! left); + assert(functor); + delete functor; + break; + +#if 0 + case MASK: + assert(! left); + assert(mask); + delete mask; + break; +#endif + + default: + assert(kind < LAST); + if (left) + left->release(); + if (kind > TERMINALS && right) + right->release(); + break; + } +} + +void xpath_t::op_t::get_value(value_t& result) const +{ + switch (kind) { + case VALUE: + result = *valuep; + break; + case ARG_INDEX: + result = (long)arg_index; + break; + default: + throw_(calc_exception, + "Cannot determine value of expression symbol '" << *this << "'"); + } +} + +xpath_t::op_t * +xpath_t::parse_value_term(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node; + + token_t& tok = next_token(in, tflags); + + switch (tok.kind) { + case token_t::VALUE: + node.reset(new op_t(op_t::VALUE)); + node->valuep = new value_t(tok.value); + break; + + case token_t::IDENT: { +#if 0 +#ifdef USE_BOOST_PYTHON + if (tok.value->to_string() == "lambda") // special + try { + char c, buf[4096]; + + std::strcpy(buf, "lambda "); + READ_INTO(in, &buf[7], 4000, c, true); + + op_t * eval = new op_t(op_t::O_EVAL); + op_t * lambda = new op_t(op_t::FUNCTOR); + lambda->functor = new python_functor_t(python_eval(buf)); + eval->set_left(lambda); + op_t * sym = new op_t(op_t::SYMBOL); + sym->name = new string("__ptr"); + eval->set_right(sym); + + node.reset(eval); + + goto done; + } + catch(const boost::python::error_already_set&) { + throw_(parse_exception, "Error parsing lambda expression"); + } +#endif /* USE_BOOST_PYTHON */ +#endif + + string ident = tok.value.to_string(); + int id = -1; + if (std::isdigit(ident[0])) { + node.reset(new op_t(op_t::ARG_INDEX)); + node->arg_index = std::atol(ident.c_str()); + } + else if ((id = document_t::lookup_builtin_id(ident)) != -1) { + node.reset(new op_t(op_t::NODE_ID)); + node->name_id = id; + } + else { + node.reset(new op_t(op_t::NODE_NAME)); + node->name = new string(ident); + } + + // An identifier followed by ( represents a function call + tok = next_token(in, tflags); + if (tok.kind == token_t::LPAREN) { + node->kind = op_t::FUNC_NAME; + + std::auto_ptr<op_t> call_node; + call_node.reset(new op_t(op_t::O_EVAL)); + call_node->set_left(node.release()); + call_node->set_right(parse_value_expr(in, tflags | XPATH_PARSE_PARTIAL)); + + tok = next_token(in, tflags); + if (tok.kind != token_t::RPAREN) + tok.unexpected(); // jww (2006-09-09): wanted ) + + node.reset(call_node.release()); + } else { + push_token(tok); + } + break; + } + + case token_t::AT_SYM: + tok = next_token(in, tflags); + if (tok.kind != token_t::IDENT) + throw_(parse_exception, "@ symbol must be followed by attribute name"); + + node.reset(new op_t(op_t::ATTR_NAME)); + node->name = new string(tok.value.to_string()); + break; + +#if 0 + case token_t::DOLLAR: + tok = next_token(in, tflags); + if (tok.kind != token_t::IDENT) + throw parse_error("$ symbol must be followed by variable name"); + + node.reset(new op_t(op_t::VAR_NAME)); + node->name = new string(tok.value.to_string()); + break; +#endif + + case token_t::DOT: + node.reset(new op_t(op_t::NODE_ID)); + node->name_id = document_t::CURRENT; + break; + case token_t::DOTDOT: + node.reset(new op_t(op_t::NODE_ID)); + node->name_id = document_t::PARENT; + break; + case token_t::SLASH: + node.reset(new op_t(op_t::NODE_ID)); + node->name_id = document_t::ROOT; + push_token(); + break; + case token_t::STAR: + node.reset(new op_t(op_t::NODE_ID)); + node->name_id = document_t::ALL; + break; + + case token_t::LPAREN: + node.reset(parse_value_expr(in, tflags | XPATH_PARSE_PARTIAL)); + if (! node.get()) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + tok = next_token(in, tflags); + if (tok.kind != token_t::RPAREN) + tok.unexpected(); // jww (2006-09-09): wanted ) + break; + +#if 0 + case token_t::REGEXP: + node.reset(wrap_mask(tok.value.to_string())); + break; +#endif + + default: + push_token(tok); + break; + } + +#if 0 +#ifdef USE_BOOST_PYTHON + done: +#endif +#endif + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_predicate_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_value_term(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + while (tok.kind == token_t::LBRACKET) { + std::auto_ptr<op_t> prev(node.release()); + node.reset(new op_t(op_t::O_PRED)); + node->set_left(prev.release()); + node->set_right(parse_value_expr(in, tflags | XPATH_PARSE_PARTIAL)); + if (! node->right) + throw_(parse_exception, "[ operator not followed by valid expression"); + + tok = next_token(in, tflags); + if (tok.kind != token_t::RBRACKET) + tok.unexpected(); // jww (2006-09-09): wanted ] + + tok = next_token(in, tflags); + } + + push_token(tok); + } + + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_path_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_predicate_expr(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + while (tok.kind == token_t::SLASH) { + std::auto_ptr<op_t> prev(node.release()); + + tok = next_token(in, tflags); + node.reset(new op_t(tok.kind == token_t::SLASH ? + op_t::O_RFIND : op_t::O_FIND)); + if (tok.kind != token_t::SLASH) + push_token(tok); + + node->set_left(prev.release()); + node->set_right(parse_predicate_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, "/ operator not followed by a valid term"); + + tok = next_token(in, tflags); + } + + push_token(tok); + } + + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_unary_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node; + + token_t& tok = next_token(in, tflags); + + switch (tok.kind) { + case token_t::EXCLAM: { + std::auto_ptr<op_t> texpr(parse_path_expr(in, tflags)); + if (! texpr.get()) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + // A very quick optimization + if (texpr->kind == op_t::VALUE) { + *texpr->valuep = ! *texpr->valuep; + node.reset(texpr.release()); + } else { + node.reset(new op_t(op_t::O_NOT)); + node->set_left(texpr.release()); + } + break; + } + + case token_t::MINUS: { + std::auto_ptr<op_t> texpr(parse_path_expr(in, tflags)); + if (! texpr.get()) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + // A very quick optimization + if (texpr->kind == op_t::VALUE) { + texpr->valuep->in_place_negate(); + node.reset(texpr.release()); + } else { + node.reset(new op_t(op_t::O_NEG)); + node->set_left(texpr.release()); + } + break; + } + +#if 0 + case token_t::PERCENT: { + std::auto_ptr<op_t> texpr(parse_path_expr(in, tflags)); + if (! texpr.get()) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + // A very quick optimization + if (texpr->kind == op_t::VALUE) { + static value_t perc("100.0%"); + *texpr->valuep = perc * *texpr->valuep; + node.reset(texpr.release()); + } else { + node.reset(new op_t(op_t::O_PERC)); + node->set_left(texpr.release()); + } + break; + } +#endif + + default: + push_token(tok); + node.reset(parse_path_expr(in, tflags)); + break; + } + + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_union_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_unary_expr(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + if (tok.kind == token_t::PIPE || tok.kind == token_t::KW_UNION) { + std::auto_ptr<op_t> prev(node.release()); + node.reset(new op_t(op_t::O_UNION)); + node->set_left(prev.release()); + node->set_right(parse_union_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + } else { + push_token(tok); + } + } + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_mul_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_union_expr(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + if (tok.kind == token_t::STAR || tok.kind == token_t::KW_DIV) { + std::auto_ptr<op_t> prev(node.release()); + node.reset(new op_t(tok.kind == token_t::STAR ? + op_t::O_MUL : op_t::O_DIV)); + node->set_left(prev.release()); + node->set_right(parse_mul_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + + tok = next_token(in, tflags); + } + push_token(tok); + } + + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_add_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_mul_expr(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + if (tok.kind == token_t::PLUS || + tok.kind == token_t::MINUS) { + std::auto_ptr<op_t> prev(node.release()); + node.reset(new op_t(tok.kind == token_t::PLUS ? + op_t::O_ADD : op_t::O_SUB)); + node->set_left(prev.release()); + node->set_right(parse_add_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + + tok = next_token(in, tflags); + } + push_token(tok); + } + + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_logic_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_add_expr(in, tflags)); + + if (node.get()) { + op_t::kind_t kind = op_t::LAST; + + unsigned short _flags = tflags; + + token_t& tok = next_token(in, tflags); + switch (tok.kind) { + case token_t::ASSIGN: + kind = op_t::O_DEFINE; + break; + case token_t::EQUAL: + kind = op_t::O_EQ; + break; + case token_t::NEQUAL: + kind = op_t::O_NEQ; + break; +#if 0 + case token_t::MATCH: + kind = op_t::O_MATCH; + _flags |= XPATH_PARSE_REGEXP; + break; + case token_t::NMATCH: + kind = op_t::O_NMATCH; + _flags |= XPATH_PARSE_REGEXP; + break; +#endif + 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) { + std::auto_ptr<op_t> prev(node.release()); + node.reset(new op_t(kind)); + node->set_left(prev.release()); + if (kind == op_t::O_DEFINE) + node->set_right(parse_querycolon_expr(in, tflags)); + else + node->set_right(parse_add_expr(in, _flags)); + + if (! node->right) { + if (tok.kind == token_t::PLUS) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + else + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + } + } + } + + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_and_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_logic_expr(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + if (tok.kind == token_t::KW_AND) { + std::auto_ptr<op_t> prev(node.release()); + node.reset(new op_t(op_t::O_AND)); + node->set_left(prev.release()); + node->set_right(parse_and_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + } else { + push_token(tok); + } + } + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_or_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_and_expr(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + if (tok.kind == token_t::KW_OR) { + std::auto_ptr<op_t> prev(node.release()); + node.reset(new op_t(op_t::O_OR)); + node->set_left(prev.release()); + node->set_right(parse_or_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + } else { + push_token(tok); + } + } + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_querycolon_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_or_expr(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + if (tok.kind == token_t::QUESTION) { + std::auto_ptr<op_t> 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_COLON)); + node->right->set_left(parse_querycolon_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + tok = next_token(in, tflags); + if (tok.kind != token_t::COLON) + tok.unexpected(); // jww (2006-09-09): wanted : + node->right->set_right(parse_querycolon_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + } else { + push_token(tok); + } + } + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_value_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_querycolon_expr(in, tflags)); + + if (node.get()) { + token_t& tok = next_token(in, tflags); + if (tok.kind == token_t::COMMA) { + std::auto_ptr<op_t> prev(node.release()); + node.reset(new op_t(op_t::O_COMMA)); + node->set_left(prev.release()); + node->set_right(parse_value_expr(in, tflags)); + if (! node->right) + throw_(parse_exception, + tok.symbol << " operator not followed by argument"); + tok = next_token(in, tflags); + } + + if (tok.kind != token_t::TOK_EOF) { + if (tflags & XPATH_PARSE_PARTIAL) + push_token(tok); + else + tok.unexpected(); + } + } + else if (! (tflags & XPATH_PARSE_PARTIAL)) { + throw_(parse_exception, "Failed to parse value expression"); + } + + return node.release(); +} + +xpath_t::op_t * +xpath_t::parse_expr(std::istream& in, unsigned short tflags) const +{ + std::auto_ptr<op_t> node(parse_value_expr(in, tflags)); + + if (use_lookahead) { + use_lookahead = false; +#ifdef THREADSAFE + lookahead.rewind(in); +#else + lookahead->rewind(in); +#endif + } +#ifdef THREADSAFE + lookahead.clear(); +#else + lookahead->clear(); +#endif + + return node.release(); +} + +xpath_t::op_t * +xpath_t::op_t::new_node(kind_t kind, op_t * left, op_t * right) +{ + std::auto_ptr<op_t> node(new op_t(kind)); + if (left) + node->set_left(left); + if (right) + node->set_right(right); + return node.release(); +} + +xpath_t::op_t * +xpath_t::op_t::copy(op_t * tleft, op_t * tright) const +{ + std::auto_ptr<op_t> node(new op_t(kind)); + if (tleft) + node->set_left(tleft); + if (tright) + node->set_right(tright); + return node.release(); +} + +void xpath_t::op_t::find_values(value_t * context, scope_t * scope, + value_t::sequence_t& result_seq, + bool recursive) +{ + xpath_t expr(compile(context, scope, true)); + + if (expr->kind == VALUE) + append_value(*expr->valuep, result_seq); + + if (recursive) { + if (context->type == value_t::XML_NODE) { + node_t * ptr = context->to_xml_node(); + if (ptr->flags & XML_NODE_IS_PARENT) { + parent_node_t * parent = static_cast<parent_node_t *>(ptr); + for (node_t * node = parent->children(); + node; + node = node->next) { + value_t temp(node); + find_values(&temp, scope, result_seq, recursive); + } + } + } else { + throw_(calc_exception, "Recursive path selection on a non-node value"); + } + } +} + +bool xpath_t::op_t::test_value(value_t * context, scope_t * scope, + int index) +{ + xpath_t expr(compile(context, scope, true)); + + if (expr->kind != VALUE) + throw_(calc_exception, "Predicate expression does not yield a constant value"); + + switch (expr->valuep->type) { + case value_t::INTEGER: + case value_t::AMOUNT: + return *expr->valuep == (long)index + 1; + + default: + return expr->valuep->to_boolean(); + } +} + +xpath_t::op_t * xpath_t::op_t::defer_sequence(value_t::sequence_t& result_seq) +{ + // If not all of the elements were constants, transform the result + // into an expression sequence using O_COMMA. + + assert(! result_seq.empty()); + + if (result_seq.size() == 1) + return wrap_value(result_seq.front())->acquire(); + + value_t::sequence_t::iterator i = result_seq.begin(); + + std::auto_ptr<op_t> lit_seq(new op_t(O_COMMA)); + + lit_seq->set_left(wrap_value(*i++)); + op_t ** opp = &lit_seq->right; + + for (; i != result_seq.end(); i++) { + if (*opp) { + op_t * val = *opp; + *opp = new op_t(O_COMMA); + (*opp)->set_left(val); + opp = &(*opp)->right; + } + + if ((*i).type != value_t::POINTER) + *opp = wrap_value(*i)->acquire(); + else + *opp = static_cast<op_t *>((*i).to_pointer()); + } + + return lit_seq.release(); +} + +void xpath_t::op_t::append_value(value_t& val, + value_t::sequence_t& result_seq) +{ + if (val.type == value_t::SEQUENCE) { + value_t::sequence_t * subseq = val.to_sequence(); + for (value_t::sequence_t::iterator i = subseq->begin(); + i != subseq->end(); + i++) + result_seq.push_back(*i); + } else { + result_seq.push_back(val); + } +} + +xpath_t::op_t * xpath_t::op_t::compile(value_t * context, scope_t * scope, + bool resolve) +{ +#if 0 + try { +#endif + switch (kind) { + case VALUE: + return acquire(); + + case NODE_ID: + switch (name_id) { + case document_t::CURRENT: + return wrap_value(context)->acquire(); + + case document_t::PARENT: + if (context->type != value_t::XML_NODE) + throw_(compile_exception, "Referencing parent node from a non-node value"); + else if (context->to_xml_node()->parent) + return wrap_value(context->to_xml_node()->parent)->acquire(); + else + throw_(compile_exception, "Referencing parent node from the root node"); + + case document_t::ROOT: + if (context->type != value_t::XML_NODE) + throw_(compile_exception, "Referencing root node from a non-node value"); + else + return wrap_value(context->to_xml_node()->document->top)->acquire(); + + case document_t::ALL: { + if (context->type != value_t::XML_NODE) + throw_(compile_exception, "Referencing child nodes from a non-node value"); + + node_t * ptr = context->to_xml_node(); + if (! (ptr->flags & XML_NODE_IS_PARENT)) + throw_(compile_exception, "Request for child nodes of a leaf node"); + + parent_node_t * parent = static_cast<parent_node_t *>(ptr); + + value_t::sequence_t * nodes = new value_t::sequence_t; + for (node_t * node = parent->children(); node; node = node->next) + nodes->push_back(node); + + return wrap_value(nodes)->acquire(); + } + + default: + break; // pass down to the NODE_NAME case + } + // fall through... + + case NODE_NAME: + if (context->type == value_t::XML_NODE) { + node_t * ptr = context->to_xml_node(); + if (resolve) { + // First, look up the symbol as a node name within the current + // context. If any exist, then return the set of names. + + std::auto_ptr<value_t::sequence_t> nodes(new value_t::sequence_t); + + if (ptr->flags & XML_NODE_IS_PARENT) { + parent_node_t * parent = static_cast<parent_node_t *>(ptr); + for (node_t * node = parent->children(); + node; + node = node->next) { + if ((kind == NODE_NAME && + std::strcmp(name->c_str(), node->name()) == 0) || + (kind == NODE_ID && name_id == node->name_id)) + nodes->push_back(node); + } + } + return wrap_value(nodes.release())->acquire(); + } else { + assert(ptr); + int id = ptr->document->lookup_name_id(*name); + if (id != -1) { + op_t * node = new_node(NODE_ID); + node->name_id = id; + return node->acquire(); + } + } + } + return acquire(); + + case ATTR_NAME: { + // jww (2006-09-29): Attrs should map strings to values, not strings + const char * value = context->to_xml_node()->get_attr(name->c_str()); + return wrap_value(value)->acquire(); + } + + case VAR_NAME: + case FUNC_NAME: + if (scope) { + if (resolve) { + value_t temp; + if (scope->resolve(*name, temp)) + return wrap_value(temp)->acquire(); + } + if (op_t * def = scope->lookup(*name)) + return def->compile(context, scope, resolve); + } + return acquire(); + + case ARG_INDEX: + if (scope && scope->kind == scope_t::ARGUMENT) { + assert(scope->args.type == value_t::SEQUENCE); + if (arg_index < scope->args.to_sequence()->size()) + return wrap_value((*scope->args.to_sequence())[arg_index])->acquire(); + else + throw_(compile_exception, "Reference to non-existing argument"); + } else { + return acquire(); + } + + case FUNCTOR: + if (resolve) { + value_t temp; + (*functor)(temp, scope); + return wrap_value(temp)->acquire(); + } else { + return acquire(); + } + break; + +#if 0 + case MASK: + return acquire(); +#endif + + case O_NOT: { + assert(left); + xpath_t expr(left->compile(context, scope, resolve)); + if (! expr->constant()) { + if (left == expr) + return acquire(); + else + return copy(expr)->acquire(); + } + + if (left == expr) { + if (expr->valuep->strip_annotations()) + return wrap_value(false)->acquire(); + else + return wrap_value(true)->acquire(); + } else { + if (expr->valuep->strip_annotations()) + *expr->valuep = false; + else + *expr->valuep = true; + + return expr->acquire(); + } + } + + case O_NEG: { + assert(left); + xpath_t expr(left->compile(context, scope, resolve)); + if (! expr->constant()) { + if (left == expr) + return acquire(); + else + return copy(expr)->acquire(); + } + + if (left == expr) { + return wrap_value(expr->valuep->negate())->acquire(); + } else { + expr->valuep->in_place_negate(); + return expr->acquire(); + } + } + + case O_UNION: { + assert(left); + assert(right); + xpath_t lexpr(left->compile(context, scope, resolve)); + xpath_t rexpr(right->compile(context, scope, resolve)); + if (! lexpr->constant() || ! rexpr->constant()) { + if (left == lexpr && right == rexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + std::auto_ptr<value_t::sequence_t> result_seq(new value_t::sequence_t); + + append_value(*lexpr->valuep, *result_seq); + append_value(*rexpr->valuep, *result_seq); + + if (result_seq->size() == 1) + return wrap_value(result_seq->front())->acquire(); + else + return wrap_sequence(result_seq.release())->acquire(); + break; + } + + case O_ADD: + case O_SUB: + case O_MUL: + case O_DIV: { + assert(left); + assert(right); + xpath_t lexpr(left->compile(context, scope, resolve)); + xpath_t rexpr(right->compile(context, scope, resolve)); + if (! lexpr->constant() || ! rexpr->constant()) { + if (left == lexpr && right == rexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + if (left == lexpr) { + value_t temp(*lexpr->valuep); + switch (kind) { + case O_ADD: temp += *rexpr->valuep; break; + case O_SUB: temp -= *rexpr->valuep; break; + case O_MUL: temp *= *rexpr->valuep; break; + case O_DIV: temp /= *rexpr->valuep; break; + default: assert(0); break; + } + return wrap_value(temp)->acquire(); + } else { + switch (kind) { + case O_ADD: *lexpr->valuep += *rexpr->valuep; break; + case O_SUB: *lexpr->valuep -= *rexpr->valuep; break; + case O_MUL: *lexpr->valuep *= *rexpr->valuep; break; + case O_DIV: *lexpr->valuep /= *rexpr->valuep; break; + default: assert(0); break; + } + return lexpr->acquire(); + } + } + + case O_NEQ: + case O_EQ: + case O_LT: + case O_LTE: + case O_GT: + case O_GTE: { + assert(left); + assert(right); + xpath_t lexpr(left->compile(context, scope, resolve)); + xpath_t rexpr(right->compile(context, scope, resolve)); + if (! lexpr->constant() || ! rexpr->constant()) { + if (left == lexpr && right == rexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + if (left == lexpr) { + switch (kind) { + case O_NEQ: + return wrap_value(*lexpr->valuep != *rexpr->valuep)->acquire(); + break; + case O_EQ: + return wrap_value(*lexpr->valuep == *rexpr->valuep)->acquire(); + break; + case O_LT: + return wrap_value(*lexpr->valuep < *rexpr->valuep)->acquire(); + break; + case O_LTE: + return wrap_value(*lexpr->valuep <= *rexpr->valuep)->acquire(); + break; + case O_GT: + return wrap_value(*lexpr->valuep > *rexpr->valuep)->acquire(); + break; + case O_GTE: + return wrap_value(*lexpr->valuep >= *rexpr->valuep)->acquire(); + break; + default: assert(0); break; + } + } else { + switch (kind) { + case O_NEQ: *lexpr->valuep = *lexpr->valuep != *rexpr->valuep; break; + case O_EQ: *lexpr->valuep = *lexpr->valuep == *rexpr->valuep; break; + case O_LT: *lexpr->valuep = *lexpr->valuep < *rexpr->valuep; break; + case O_LTE: *lexpr->valuep = *lexpr->valuep <= *rexpr->valuep; break; + case O_GT: *lexpr->valuep = *lexpr->valuep > *rexpr->valuep; break; + case O_GTE: *lexpr->valuep = *lexpr->valuep >= *rexpr->valuep; break; + default: assert(0); break; + } + return lexpr->acquire(); + } + } + + case O_AND: { + assert(left); + assert(right); + xpath_t lexpr(left->compile(context, scope, resolve)); + if (lexpr->constant() && ! lexpr->valuep->strip_annotations()) { + *lexpr->valuep = false; + return lexpr->acquire(); + } + + xpath_t rexpr(right->compile(context, scope, resolve)); + if (! lexpr->constant() || ! rexpr->constant()) { + if (left == lexpr && right == rexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + if (! rexpr->valuep->strip_annotations()) { + if (left == lexpr) { + return wrap_value(false)->acquire(); + } else { + *lexpr->valuep = false; + return lexpr->acquire(); + } + } else { + return rexpr->acquire(); + } + } + + case O_OR: { + assert(left); + assert(right); + xpath_t lexpr(left->compile(context, scope, resolve)); + if (lexpr->constant() && lexpr->valuep->strip_annotations()) + return lexpr->acquire(); + + xpath_t rexpr(right->compile(context, scope, resolve)); + if (! lexpr->constant() || ! rexpr->constant()) { + if (left == lexpr && right == rexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + if (rexpr->valuep->strip_annotations()) { + return rexpr->acquire(); + } else { + if (left == lexpr) { + return wrap_value(false)->acquire(); + } else { + *lexpr->valuep = false; + return lexpr->acquire(); + } + } + } + + case O_QUES: { + assert(left); + assert(right); + assert(right->kind == O_COLON); + xpath_t lexpr(left->compile(context, scope, resolve)); + if (! lexpr->constant()) { + xpath_t rexpr(right->compile(context, scope, resolve)); + if (left == lexpr && right == rexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + if (lexpr->valuep->strip_annotations()) + return right->left->compile(context, scope, resolve); + else + return right->right->compile(context, scope, resolve); + } + + case O_COLON: { + xpath_t lexpr(left->compile(context, scope, resolve)); + xpath_t rexpr(right->compile(context, scope, resolve)); + if (left == lexpr && right == rexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + case O_COMMA: { + assert(left); + assert(right); + // jww (2006-09-29): This should act just like union + xpath_t lexpr(left->compile(context, scope, resolve)); // for side-effects + return right->compile(context, scope, resolve); + } + +#if 0 + case O_MATCH: + case O_NMATCH: { + assert(left); + assert(right); + xpath_t rexpr(right->compile(context, scope, resolve)); + xpath_t lexpr(left->compile(context, scope, resolve)); + if (! lexpr->constant() || rexpr->kind != MASK) { + if (left == lexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + if (lexpr->valuep->type != value_t::STRING) + throw_(compile_exception, "Left operand of mask operator is not a string"); + + assert(rexpr->mask); + + bool result = rexpr->mask->match(lexpr->valuep->to_string()); + if (kind == O_NMATCH) + result = ! result; + + if (left == lexpr) { + return wrap_value(result)->acquire(); + } else { + *lexpr->valuep = result; + return lexpr->acquire(); + } + } +#endif + + case O_DEFINE: + assert(left); + assert(right); + if (left->kind == VAR_NAME || left->kind == FUNC_NAME) { + xpath_t rexpr(right->compile(context, scope, resolve)); + if (scope) + scope->define(*left->name, rexpr); + return rexpr->acquire(); + } else { + assert(left->kind == O_EVAL); + assert(left->left->kind == FUNC_NAME); + + std::auto_ptr<scope_t> arg_scope(new scope_t(scope)); + + int index = 0; + op_t * args = left->right; + while (args) { + op_t * arg = args; + if (args->kind == O_COMMA) { + arg = args->left; + args = args->right; + } else { + args = NULL; + } + + // Define the parameter so that on lookup the parser will find + // an ARG_INDEX value. + std::auto_ptr<op_t> ref(new op_t(ARG_INDEX)); + ref->arg_index = index++; + + assert(arg->kind == NODE_NAME); + arg_scope->define(*arg->name, ref.release()); + } + + // jww (2006-09-16): If I compile the definition of a function, + // I eliminate the possibility of future lookups + //xpath_t rexpr(right->compile(arg_scope.get(), resolve)); + + if (scope) + scope->define(*left->left->name, right); + + return right->acquire(); + } + + case O_EVAL: { + assert(left); + + std::auto_ptr<scope_t> call_args(new scope_t(scope)); + call_args->kind = scope_t::ARGUMENT; + + std::auto_ptr<value_t::sequence_t> call_seq; + + op_t * args = right; + while (args) { + op_t * arg = args; + if (args->kind == O_COMMA) { + arg = args->left; + args = args->right; + } else { + args = NULL; + } + + if (! call_seq.get()) + call_seq.reset(new value_t::sequence_t); + + // jww (2006-09-15): Need to return a reference to these, if + // there are undetermined arguments! + call_seq->push_back(arg->compile(context, scope, resolve)->value()); + } + + if (call_seq.get()) + call_args->args = call_seq.release(); + + if (left->kind == FUNC_NAME) { + if (resolve) { + value_t temp; + if (scope && scope->resolve(*left->name, temp, call_args.get())) + return wrap_value(temp)->acquire(); + } + + // Don't compile to the left, otherwise the function name may + // get resolved before we have a chance to call it + xpath_t func(left->compile(context, scope, false)); + if (func->kind == FUNCTOR) { + value_t temp; + (*func->functor)(temp, call_args.get()); + return wrap_value(temp)->acquire(); + } + else if (! resolve) { + return func->compile(context, call_args.get(), resolve); + } + else { + throw_(calc_exception, "Unknown function name '" << *left->name << "'"); + } + } + else if (left->kind == FUNCTOR) { + value_t temp; + (*left->functor)(temp, call_args.get()); + return wrap_value(temp)->acquire(); + } + else { + assert(0); + } + break; + } + + case O_FIND: + case O_RFIND: + case O_PRED: { + assert(left); + assert(right); + xpath_t lexpr(left->compile(context, scope, resolve)); + xpath_t rexpr(resolve ? right->acquire() : + right->compile(context, scope, false)); + + if (! lexpr->constant() || ! resolve) { + if (left == lexpr) + return acquire(); + else + return copy(lexpr, rexpr)->acquire(); + } + + std::auto_ptr<value_t::sequence_t> result_seq(new value_t::sequence_t); + + // jww (2006-09-24): What about when nothing is found? + switch (lexpr->valuep->type) { + case value_t::XML_NODE: { + function_scope_t xpath_fscope(NULL, lexpr->valuep, 0, scope); + if (kind == O_PRED) { + if (rexpr->test_value(lexpr->valuep, &xpath_fscope)) + result_seq->push_back(*lexpr->valuep); + } else { + rexpr->find_values(lexpr->valuep, &xpath_fscope, *result_seq.get(), + kind == O_RFIND); + } + break; + } + + case value_t::SEQUENCE: { + value_t::sequence_t * seq = lexpr->valuep->to_sequence(); + + int index = 0; + for (value_t::sequence_t::iterator i = seq->begin(); + i != seq->end(); + i++, index++) { + assert((*i).type != value_t::SEQUENCE); + if ((*i).type != value_t::XML_NODE) + throw_(compile_exception, "Attempting to apply path selection " + "to non-node(s)"); + + function_scope_t xpath_fscope(seq, &(*i), index, scope); + if (kind == O_PRED) { + if (rexpr->test_value(&(*i), &xpath_fscope, index)) + result_seq->push_back(*i); + } else { + rexpr->find_values(&(*i), &xpath_fscope, *result_seq.get(), + kind == O_RFIND); + } + } + break; + } + + default: + throw_(compile_exception, "Attempting to apply path selection " + "to non-node(s)"); + } + + if (result_seq->size() == 1) + return wrap_value(result_seq->front())->acquire(); + else + return wrap_sequence(result_seq.release())->acquire(); + } + +#if 0 + case O_PERC: { + assert(left); + xpath_t expr(left->compile(context, scope, resolve)); + if (! expr->constant()) { + if (left == expr) + return acquire(); + else + return copy(expr)->acquire(); + } + + static value_t perc("100.0%"); + *expr->valuep = perc * *expr->valuep; + return expr->acquire(); + } +#endif + + case LAST: + default: + assert(0); + break; + } +#if 0 + } + catch (error * err) { +#if 0 + // jww (2006-09-09): I need a reference to the parent xpath_t + if (err->context.empty() || + ! dynamic_cast<context *>(err->context.back())) + err->context.push_back(new context(this)); +#endif + throw err; + } +#endif + + assert(0); + return NULL; +} + +void xpath_t::calc(value_t& result, node_t * node, scope_t * scope) const +{ +#if 0 + try { +#endif + if (node) { + value_t context_node(node); + xpath_t final(ptr->compile(&context_node, scope, true)); + // jww (2006-09-09): Give a better error here if this is not + // actually a value + final->get_value(result); + } else { + std::auto_ptr<terminal_node_t> fake_node(new terminal_node_t(NULL)); + value_t context_node(fake_node.get()); + xpath_t final(ptr->compile(&context_node, scope, true)); + final->get_value(result); + } +#if 0 + } + catch (error * err) { + if (err->context.empty() || + ! dynamic_cast<context *>(err->context.back())) + err->context.push_back + (new context(*this, ptr, "While calculating value expression:")); +#if 0 + error_context * last = err->context.back(); + if (context * ctxt = dynamic_cast<context *>(last)) { + ctxt->xpath = *this; + ctxt->desc = "While calculating value expression:"; + } +#endif + throw err; + } +#endif +} + +#if 0 +xpath_t::context::context(const xpath_t& _xpath, + const op_t * _err_node, + const string& desc) throw() + : error_context(desc), xpath(_xpath), err_node(_err_node) +{ + _err_node->acquire(); +} + +xpath_t::context::~context() throw() +{ + if (err_node) err_node->release(); +} + +void xpath_t::context::describe(std::ostream& out) const throw() +{ + if (! xpath) { + out << "xpath_t::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 = false; + if (xpath) + xpath.write(out, true, err_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 + +bool xpath_t::op_t::write(std::ostream& out, + const bool relaxed, + const op_t * op_to_find, + unsigned long * start_pos, + unsigned long * end_pos) const +{ + int arg_index = 0; + bool found = false; + + if (start_pos && this == op_to_find) { + *start_pos = (long)out.tellp() - 1; + found = true; + } + + string symbol; + + switch (kind) { + case VALUE: + switch (valuep->type) { + case value_t::BOOLEAN: + if (*(valuep)) + out << "1"; + else + out << "0"; + break; + case value_t::INTEGER: + case value_t::AMOUNT: + if (! relaxed) + out << '{'; + out << *(valuep); + if (! relaxed) + out << '}'; + break; + case value_t::BALANCE: + case value_t::BALANCE_PAIR: + assert(0); + break; + case value_t::DATETIME: + out << '[' << *valuep << ']'; + break; + case value_t::STRING: + out << '"' << *valuep << '"'; + break; + + case value_t::XML_NODE: + out << '<' << valuep << '>'; + break; + case value_t::POINTER: + out << '&' << valuep; + break; + case value_t::SEQUENCE: + out << '~' << valuep << '~'; + break; + } + break; + + case NODE_ID: +#ifdef THREADSAFE + out << '%' << name_id; +#else + out << node_t::document->lookup_name(name_id); +#endif + break; + + case NODE_NAME: + case FUNC_NAME: + out << *name; + break; + + case ATTR_NAME: + out << '@' << *name; + break; + + case VAR_NAME: + out << '$' << *name; + break; + + case FUNCTOR: + out << functor->name(); + break; + +#if 0 + case MASK: + out << '/' << mask->pattern << '/'; + break; +#endif + + case ARG_INDEX: + out << '@' << arg_index; + break; + + case O_NOT: + out << "!"; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + case O_NEG: + out << "-"; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + + case O_UNION: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " | "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + + case O_ADD: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " + "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_SUB: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " - "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_MUL: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " * "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_DIV: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " / "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + + case O_NEQ: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " != "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_EQ: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " == "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_LT: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " < "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_LTE: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " <= "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_GT: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " > "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_GTE: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " >= "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + + case O_AND: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " & "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_OR: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " | "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + + case O_QUES: + out << "("; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " ? "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + case O_COLON: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " : "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + + case O_COMMA: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ", "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + +#if 0 + case O_MATCH: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " =~ "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + case O_NMATCH: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << " !~ "; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; +#endif + + case O_DEFINE: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << '='; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + case O_EVAL: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << "("; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << ")"; + break; + + case O_FIND: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << "/"; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + case O_RFIND: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << "//"; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; + case O_PRED: + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << "["; + if (right && right->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + out << "]"; + break; + +#if 0 + case O_PERC: + out << "%"; + if (left && left->write(out, relaxed, op_to_find, start_pos, end_pos)) + found = true; + break; +#endif + + case LAST: + default: + assert(0); + break; + } + + if (! symbol.empty()) { + if (commodity_t::find(symbol)) + out << '@'; + out << symbol; + } + + if (end_pos && this == op_to_find) + *end_pos = (long)out.tellp() - 1; + + return found; +} + +void xpath_t::op_t::dump(std::ostream& out, const int depth) const +{ + out.setf(std::ios::left); + out.width(10); + out << this << " "; + + for (int i = 0; i < depth; i++) + out << " "; + + switch (kind) { + case VALUE: + out << "VALUE - " << *valuep; + break; + + case NODE_NAME: + out << "NODE_NAME - " << *name; + break; + + case NODE_ID: +#ifdef THREADSAFE + out << "NODE_ID - " << name_id; +#else + out << "NODE_ID - " << node_t::document->lookup_name(name_id); +#endif + break; + + case ATTR_NAME: + out << "ATTR_NAME - " << *name; + break; + + case FUNC_NAME: + out << "FUNC_NAME - " << *name; + break; + + case VAR_NAME: + out << "VAR_NAME - " << *name; + break; + + case ARG_INDEX: + out << "ARG_INDEX - " << arg_index; + break; + + case FUNCTOR: + out << "FUNCTOR - " << functor->name(); + break; +#if 0 + case MASK: + out << "MASK - " << mask->pattern; + break; +#endif + + case O_NOT: out << "O_NOT"; break; + case O_NEG: out << "O_NEG"; break; + + case O_UNION: out << "O_UNION"; 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_QUES: out << "O_QUES"; break; + case O_COLON: out << "O_COLON"; break; + + case O_COMMA: out << "O_COMMA"; break; + +#if 0 + case O_MATCH: out << "O_MATCH"; break; + case O_NMATCH: out << "O_NMATCH"; break; +#endif + + case O_DEFINE: out << "O_DEFINE"; break; + case O_EVAL: out << "O_EVAL"; break; + + case O_FIND: out << "O_FIND"; break; + case O_RFIND: out << "O_RFIND"; break; + case O_PRED: out << "O_PRED"; break; + +#if 0 + case O_PERC: out << "O_PERC"; break; +#endif + + case LAST: + default: + assert(0); + 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); + } + } else { + assert(! left); + } +} + +} // namespace xml +} // namespace ledger |