summaryrefslogtreecommitdiff
path: root/src/traversal
diff options
context:
space:
mode:
Diffstat (limited to 'src/traversal')
-rw-r--r--src/traversal/abbrev.cc94
-rw-r--r--src/traversal/abbrev.h23
-rw-r--r--src/traversal/transform.cc357
-rw-r--r--src/traversal/transform.h164
-rw-r--r--src/traversal/xpath.cc1670
-rw-r--r--src/traversal/xpath.h873
6 files changed, 0 insertions, 3181 deletions
diff --git a/src/traversal/abbrev.cc b/src/traversal/abbrev.cc
deleted file mode 100644
index 089b8342..00000000
--- a/src/traversal/abbrev.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-#include "abbrev.h"
-
-namespace ledger {
-
-string abbreviate(const string& str,
- unsigned int width,
- elision_style_t elision_style,
- const bool is_account,
- int abbrev_length)
-{
- const unsigned int len = str.length();
- if (len <= width)
- return str;
-
- assert(width < 4095);
-
- static char buf[4096];
-
- switch (elision_style) {
- case TRUNCATE_LEADING:
- // This method truncates at the beginning.
- std::strncpy(buf, str.c_str() + (len - width), width);
- buf[0] = '.';
- buf[1] = '.';
- break;
-
- case TRUNCATE_MIDDLE:
- // This method truncates in the middle.
- std::strncpy(buf, str.c_str(), width / 2);
- std::strncpy(buf + width / 2,
- str.c_str() + (len - (width / 2 + width % 2)),
- width / 2 + width % 2);
- buf[width / 2 - 1] = '.';
- buf[width / 2] = '.';
- break;
-
- case ABBREVIATE:
- if (is_account) {
- std::list<string> parts;
- string::size_type beg = 0;
- for (string::size_type pos = str.find(':');
- pos != string::npos;
- beg = pos + 1, pos = str.find(':', beg))
- parts.push_back(string(str, beg, pos - beg));
- parts.push_back(string(str, beg));
-
- string result;
- unsigned int newlen = len;
- for (std::list<string>::iterator i = parts.begin();
- i != parts.end();
- i++) {
- // Don't contract the last element
- std::list<string>::iterator x = i;
- if (++x == parts.end()) {
- result += *i;
- break;
- }
-
- if (newlen > width) {
- result += string(*i, 0, abbrev_length);
- result += ":";
- newlen -= (*i).length() - abbrev_length;
- } else {
- result += *i;
- result += ":";
- }
- }
-
- if (newlen > width) {
- // Even abbreviated its too big to show the last account, so
- // abbreviate all but the last and truncate at the beginning.
- std::strncpy(buf, result.c_str() + (result.length() - width), width);
- buf[0] = '.';
- buf[1] = '.';
- } else {
- std::strcpy(buf, result.c_str());
- }
- break;
- }
- // fall through...
-
- case TRUNCATE_TRAILING:
- // This method truncates at the end (the default).
- std::strncpy(buf, str.c_str(), width - 2);
- buf[width - 2] = '.';
- buf[width - 1] = '.';
- break;
- }
- buf[width] = '\0';
-
- return buf;
-}
-
-} // namespace ledger
diff --git a/src/traversal/abbrev.h b/src/traversal/abbrev.h
deleted file mode 100644
index ad880e45..00000000
--- a/src/traversal/abbrev.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef _ABBREV_H
-#define _ABBREV_H
-
-#include "utils.h"
-
-namespace ledger {
-
-enum elision_style_t {
- TRUNCATE_TRAILING,
- TRUNCATE_MIDDLE,
- TRUNCATE_LEADING,
- ABBREVIATE
-};
-
-string abbreviate(const string& str,
- unsigned int width,
- elision_style_t elision_style = TRUNCATE_TRAILING,
- const bool is_account = false,
- int abbrev_length = 2);
-
-} // namespace ledger
-
-#endif // _ABBREV_H
diff --git a/src/traversal/transform.cc b/src/traversal/transform.cc
deleted file mode 100644
index 3331c2f3..00000000
--- a/src/traversal/transform.cc
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of New Artisans LLC nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "transform.h"
-
-namespace ledger {
-
-#if 0
-void populate_account(account_t& acct, xml::document_t& document)
-{
- if (! acct.parent)
- return;
-
- account_repitem_t * acct_item;
- if (acct.data == NULL) {
- acct.data = acct_item =
- static_cast<account_repitem_t *>(repitem_t::wrap(&acct));
- if (acct.parent) {
- if (acct.parent->data == NULL)
- populate_account(*acct.parent, acct_item);
- else
- static_cast<account_repitem_t *>(acct.parent->data)->
- add_child(acct_item);
- }
- } else {
- acct_item = static_cast<account_repitem_t *>(acct.data);
- }
-
- if (item->kind == repitem_t::ACCOUNT)
- acct_item->add_child(item);
- else
- acct_item->add_content(item);
-}
-
-class populate_accounts : public repitem_t::select_callback_t {
- virtual void operator()(xml::document_t& document) {
- if (item->kind == repitem_t::TRANSACTION) {
- item->extract();
- populate_account(*static_cast<xact_repitem_t *>(item)->account(), item);
- }
- }
-};
-
-class clear_account_data : public repitem_t::select_callback_t {
- virtual void operator()(xml::document_t& document) {
- if (item->kind == repitem_t::ACCOUNT)
- static_cast<account_repitem_t *>(item)->account->data = NULL;
- }
-};
-
-void accounts_transform::execute(xml::document_t& document)
-{
- populate_accounts cb1;
- items->select_all(cb1);
-
- for (repitem_t * j = items->children; j; j = j->next) {
- assert(j->kind == repitem_t::JOURNAL);
-
- j->clear();
-
- for (accounts_map::iterator i = j->journal->master->accounts.begin();
- i != j->journal->master->accounts.end();
- i++) {
- assert((*i).second->data);
- j->add_child(static_cast<account_repitem_t *>((*i).second->data));
- (*i).second->data = NULL;
- }
- }
-
- clear_account_data cb2;
- items->select_all(cb2);
-}
-
-void compact_transform::execute(xml::document_t& document)
-{
- for (repitem_t * i = items; i; i = i->next) {
- if (i->kind == repitem_t::ACCOUNT) {
- while (! i->contents &&
- i->children && ! i->children->next) {
- account_repitem_t * p = static_cast<account_repitem_t *>(i);
- i = p->children;
- p->children = NULL;
- p->last_child = NULL;
-
- i->set_parent(p->parent);
- p->set_parent(NULL);
- i->prev = p->prev;
- if (p->prev)
- p->prev->next = i;
- p->prev = NULL;
- i->next = p->next;
- if (p->next)
- p->next->prev = i;
- p->next = NULL;
-
- if (i->parent->children == p)
- i->parent->children = i;
- if (i->parent->last_child == p)
- i->parent->last_child = i;
-
- account_repitem_t * acct = static_cast<account_repitem_t *>(i);
- acct->parents_elided = p->parents_elided + 1;
-
- checked_delete(p);
- }
- }
-
- if (i->children)
- execute(i->children);
- }
-}
-
-void clean_transform::execute(xml::document_t& document)
-{
- repitem_t * i = items;
- while (i) {
- if (i->kind == repitem_t::ACCOUNT) {
- value_t temp;
- i->add_total(temp);
- if (! temp) {
- repitem_t * next = i->next;
- checked_delete(i);
- i = next;
- continue;
- }
- }
-#if 0
- else if (i->kind == repitem_t::ENTRY && ! i->contents) {
- assert(! i->children);
- repitem_t * next = i->next;
- checked_delete(i);
- i = next;
- continue;
- }
-#endif
-
- if (i->children)
- execute(i->children);
-
- i = i->next;
- }
-}
-
-void entries_transform::execute(xml::document_t& document)
-{
-}
-
-void optimize_transform::execute(xml::document_t& document)
-{
- for (repitem_t * i = items; i; i = i->next) {
- if (i->kind == repitem_t::ENTRY) {
- if (i->contents &&
- i->contents->next &&
- ! i->contents->next->next) { // exactly two transactions
- xact_repitem_t * first =
- static_cast<xact_repitem_t *>(i->contents);
- xact_repitem_t * second =
- static_cast<xact_repitem_t *>(i->contents->next);
- if (first->xact->amount == - second->xact->amount)
- ;
- }
- }
-
- if (i->children)
- execute(i->children);
- }
-}
-
-void split_transform::execute(xml::document_t& document)
-{
- for (repitem_t * i = items; i; i = i->next) {
- if (i->contents && i->contents->next) {
- repitem_t * j;
-
- switch (i->kind) {
- case repitem_t::TRANSACTION:
- assert(false);
- j = new xact_repitem_t(static_cast<xact_repitem_t *>(i)->xact);
- break;
- case repitem_t::ENTRY:
- j = new entry_repitem_t(static_cast<entry_repitem_t *>(i)->entry);
- break;
- case repitem_t::ACCOUNT:
- j = new account_repitem_t(static_cast<account_repitem_t *>(i)->account);
- break;
- default:
- j = new repitem_t(i->kind);
- break;
- }
-
- j->set_parent(i->parent);
- j->prev = i;
- j->next = i->next;
- i->next = j;
-
- j->contents = i->contents->next;
- j->contents->prev = NULL;
- j->contents->set_parent(j);
- i->contents->next = NULL;
-
- j->last_content = i->last_content;
- if (j->contents == i->last_content)
- i->last_content = i->contents;
- }
-
- if (i->children)
- execute(i->children);
- }
-}
-
-void merge_transform::execute(xml::document_t& document)
-{
- for (repitem_t * i = items; i; i = i->next) {
- if (i->next) {
- assert(i->kind == i->next->kind);
- bool merge = false;
- switch (i->kind) {
- case repitem_t::TRANSACTION:
- assert(false);
- break;
- case repitem_t::ENTRY:
- if (static_cast<entry_repitem_t *>(i)->entry ==
- static_cast<entry_repitem_t *>(i->next)->entry)
- merge = true;
- break;
- case repitem_t::ACCOUNT:
-#if 0
- if (static_cast<account_repitem_t *>(i)->account ==
- static_cast<account_repitem_t *>(i->next)->account)
- merge = true;
-#endif
- break;
- default:
- break;
- }
-
- if (merge) {
- repitem_t * j = i->next;
-
- i->next = i->next->next;
- if (i->next)
- i->next->prev = i;
-
- for (repitem_t * k = j->contents; k; k = k->next)
- k->set_parent(i);
-
- i->last_content->next = j->contents;
- i->last_content = j->last_content;
-
- j->contents = NULL;
- assert(! j->children);
- checked_delete(j);
- }
- }
-
- if (i->children)
- execute(i->children);
- }
-}
-
-namespace {
-#define REPITEM_FLAGGED 0x1
-
- class mark_selected : public repitem_t::select_callback_t {
- virtual void operator()(xml::document_t& document) {
- item->flags |= REPITEM_FLAGGED;
- }
- };
-
- class mark_selected_and_ancestors : public repitem_t::select_callback_t {
- virtual void operator()(xml::document_t& document) {
- while (item->parent) {
- item->flags |= REPITEM_FLAGGED;
- item = item->parent;
- }
- }
- };
-
- class delete_unmarked : public repitem_t::select_callback_t {
- virtual void operator()(xml::document_t& document) {
- if (item->parent && ! (item->flags & REPITEM_FLAGGED))
- checked_delete(item);
- }
- };
-
- class delete_marked : public repitem_t::select_callback_t {
- virtual void operator()(xml::document_t& document) {
- if (item->flags & REPITEM_FLAGGED)
- checked_delete(item);
- }
- };
-
- class clear_flags : public repitem_t::select_callback_t {
- virtual void operator()(xml::document_t& document) {
- item->flags = 0;
- }
- };
-}
-
-void select_transform::execute(xml::document_t& document)
-{
- if (! path) {
- items->clear();
- return;
- }
- mark_selected_and_ancestors cb1;
- items->select(path, cb1);
-
- delete_unmarked cb2;
- items->select_all(cb2);
- clear_flags cb3;
- items->select_all(cb3);
-}
-
-void remove_transform::execute(xml::document_t& document)
-{
- if (! path)
- return;
- mark_selected cb1;
- items->select(path, cb1);
-
- delete_marked cb2;
- items->select_all(cb2);
- clear_flags cb3;
- items->select_all(cb3);
-}
-#endif
-
-} // namespace ledger
diff --git a/src/traversal/transform.h b/src/traversal/transform.h
deleted file mode 100644
index 158b9b6a..00000000
--- a/src/traversal/transform.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of New Artisans LLC nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _TRANSFORM_H
-#define _TRANSFORM_H
-
-#include "xpath.h"
-
-namespace ledger {
-
-class transform_t {
- public:
- virtual ~transform_t() {}
- virtual value_t operator()(xml::xpath_t::scope_t& args) = 0;
-};
-
-class check_transform : public transform_t {
- // --check checks the validity of the item list.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class accounts_transform : public transform_t {
- // --accounts transforms the report tree into an account-wise view.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class compact_transform : public transform_t {
- // --compact compacts an account tree to remove accounts with only
- // one child account.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class clean_transform : public transform_t {
- // --clean clears out entries and accounts that have no contents.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class entries_transform : public transform_t {
- // --entries transforms the report tree into an entries-wise view.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class optimize_transform : public transform_t {
- // --optimize optimizes entries for display by the print command.
- // What this means is that if an entry has two transactions of the
- // commodity (one the negative of the other), the amount of the
- // second transaction will be nulled out.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class split_transform : public transform_t {
- // --split breaks entry with two or more transactions into what
- // seems like two entries each with one transaction -- even though
- // it is the same entry being reported in both cases. This is
- // useful before sorting, for exampel, in order to sort by
- // transaction instead of by entry.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class merge_transform : public transform_t {
- // --merge is the opposite of --split: any adjacent transactions
- // which share the same entry will be merged into a group of
- // transactions under one reported entry.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class combine_transform : public transform_t {
- // --combine EXPR combines all transactions matching EXPR so that
- // they appear within the same virtual entry (whose date will span
- // the earliest to the latest of those entries, and whose payee name
- // will show the terminating date or a label that is characteristic
- // of the set).
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class group_transform : public transform_t {
- // --group groups all transactions that affect the same account
- // within an entry, so that they appear as a single transaction.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class collapse_transform : public transform_t {
- // --collapse makes all transactions within an entry appear as a
- // single transaction, even if they affect different accounts. The
- // fictitous account "<total>" is used to represent the final sum,
- // if multiple accounts are involved.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class subtotal_transform : public transform_t {
- // --subtotal will combine the transactions from all entries into
- // one giant entry. When used in conjunction with --group, the
- // affect is very similar to a regular balance report.
- public:
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-#if 0
-class select_transform : public transform_t
-{
- protected:
- xml::xpath_t xpath;
-
- public:
- select_transform(const string& selection_path) {
- xpath.parse(selection_path);
- }
- virtual ~select_transform() {}
-
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-
-class remove_transform : public select_transform
-{
- public:
- remove_transform(const string& selection_path)
- : select_transform(selection_path) {}
-
- virtual value_t operator()(xml::xpath_t::call_scope_t& args);
-};
-#endif
-
-} // namespace ledger
-
-#endif // _TRANSFORM_H
diff --git a/src/traversal/xpath.cc b/src/traversal/xpath.cc
deleted file mode 100644
index 85f25209..00000000
--- a/src/traversal/xpath.cc
+++ /dev/null
@@ -1,1670 +0,0 @@
-/*
- * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of New Artisans LLC nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "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()
-{
- checked_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 == '.' || 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, 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 & XPATH_PARSE_RELAXED) &&
- (std::isalpha(c) || c == '_')) {
- parse_ident(in);
- return;
- }
-
- switch (c) {
- case '@':
- in.get(c);
- kind = AT_SYM;
- break;
- case '$':
- in.get(c);
- kind = DOLLAR;
- break;
-
- case '(':
- in.get(c);
- kind = LPAREN;
- break;
- case ')':
- in.get(c);
- kind = RPAREN;
- break;
-
- case '[': {
- in.get(c);
- if (flags & 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 '\'':
- case '"': {
- char delim;
- in.get(delim);
- char buf[4096];
- READ_INTO_(in, buf, 4095, c, length, c != delim);
- if (c != delim)
- unexpected(c, delim);
- in.get(c);
- length++;
- kind = VALUE;
- value.set_string(buf);
- break;
- }
-
- case '{': {
- in.get(c);
- amount_t temp;
- temp.parse(in, AMOUNT_PARSE_NO_MIGRATE);
- in.get(c);
- if (c != '}')
- unexpected(c, '}');
- length++;
- kind = VALUE;
- value = temp;
- break;
- }
-
- case '!':
- in.get(c);
- c = in.peek();
- if (c == '=') {
- in.get(c);
- symbol[1] = c;
- symbol[2] = '\0';
- kind = NEQUAL;
- length = 2;
- break;
- }
- kind = EXCLAM;
- break;
-
- case '-':
- in.get(c);
- kind = MINUS;
- break;
- case '+':
- in.get(c);
- kind = PLUS;
- break;
-
- case '*':
- in.get(c);
- kind = STAR;
- break;
-
- case '/':
- in.get(c);
- kind = SLASH;
- break;
-
- case '=':
- in.get(c);
- kind = EQUAL;
- break;
-
- case '<':
- in.get(c);
- if (in.peek() == '=') {
- in.get(c);
- symbol[1] = c;
- symbol[2] = '\0';
- kind = LESSEQ;
- length = 2;
- break;
- }
- kind = LESS;
- break;
-
- case '>':
- in.get(c);
- if (in.peek() == '=') {
- in.get(c);
- symbol[1] = c;
- symbol[2] = '\0';
- kind = GREATEREQ;
- length = 2;
- break;
- }
- kind = GREATER;
- break;
-
- case '|':
- in.get(c);
- kind = PIPE;
- 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 & 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_error& err) {
- // If the amount had no commodity, it must be an unambiguous
- // variable reference
-
- // jww (2007-04-19): There must be a more efficient way to do this!
- if (std::strcmp(err.what(), "No quantity specified for amount") == 0) {
- in.clear();
- in.seekg(pos, std::ios::beg);
-
- c = in.peek();
- assert(! (std::isdigit(c) || c == '.'));
- parse_ident(in);
- } else {
- throw;
- }
- }
- }
- break;
- }
-}
-
-void 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_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 xpath_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 << "'");
- }
-}
-
-
-void xpath_t::scope_t::define(const string& name, const value_t& val) {
- define(name, op_t::wrap_value(val));
-}
-
-void xpath_t::symbol_scope_t::define(const string& name, ptr_op_t def)
-{
- DEBUG("ledger.xpath.syms", "Defining '" << name << "' = " << def);
-
- std::pair<symbol_map::iterator, bool> 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<symbol_map::iterator, bool> result2
- = symbols.insert(symbol_map::value_type(name, def));
- if (! result2.second)
- throw_(compile_error,
- "Redefinition of '" << name << "' in same scope");
- }
-}
-
-namespace {
- value_t xpath_fn_last(xpath_t::call_scope_t& scope)
- {
- xpath_t::context_scope_t& context(CONTEXT_SCOPE(scope));
- return context.size();
- }
-
- value_t xpath_fn_position(xpath_t::call_scope_t& scope)
- {
- xpath_t::context_scope_t& context(CONTEXT_SCOPE(scope));
- return context.index() + 1;
- }
-
- value_t xpath_fn_text(xpath_t::call_scope_t& scope)
- {
- xpath_t::context_scope_t& context(CONTEXT_SCOPE(scope));
- return value_t(context.xml_node().to_value().to_string(), true);
- }
-
- value_t xpath_fn_type(xpath_t::call_scope_t& scope)
- {
- if (scope.size() == 0) {
- xpath_t::context_scope_t& context(CONTEXT_SCOPE(scope));
- return string_value(context.value().label());
- }
- else if (scope.size() == 1) {
- return string_value(scope[0].label());
- }
- else {
- assert(false);
- return string_value("INVALID");
- }
- }
-}
-
-xpath_t::ptr_op_t
-xpath_t::symbol_scope_t::lookup(const string& name)
-{
- switch (name[0]) {
- case 'l':
- if (name == "last")
- return WRAP_FUNCTOR(bind(xpath_fn_last, _1));
- break;
-
- case 'p':
- if (name == "position")
- return WRAP_FUNCTOR(bind(xpath_fn_position, _1));
- break;
-
- case 't':
- if (name == "text")
- return WRAP_FUNCTOR(bind(xpath_fn_text, _1));
- else if (name == "type")
- return WRAP_FUNCTOR(bind(xpath_fn_type, _1));
- break;
- }
-
- symbol_map::const_iterator i = symbols.find(name);
- if (i != symbols.end())
- return (*i).second;
-
- return child_scope_t::lookup(name);
-}
-
-
-xpath_t::ptr_op_t
-xpath_t::parse_value_term(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node;
-
- token_t& tok = next_token(in, tflags);
-
- switch (tok.kind) {
- case token_t::VALUE:
- node = new op_t(op_t::VALUE);
- node->set_value(tok.value);
- break;
-
- case token_t::IDENT: {
-#if 0
-#ifdef USE_BOOST_PYTHON
- if (tok.value->as_string() == "lambda") // special
- try {
- char c, buf[4096];
-
- std::strcpy(buf, "lambda ");
- READ_INTO(in, &buf[7], 4000, c, true);
-
- ptr_op_t eval = new op_t(op_t::O_EVAL);
- ptr_op_t lambda = new op_t(op_t::FUNCTION);
- lambda->functor = new python_functor_t(python_eval(buf));
- eval->set_left(lambda);
- ptr_op_t sym = new op_t(op_t::SYMBOL);
- sym->name = new string("__ptr");
- eval->set_right(sym);
-
- node = eval;
-
- goto done;
- }
- catch(const boost::python::error_already_set&) {
- throw_(parse_error, "Error parsing lambda expression");
- }
-#endif /* USE_BOOST_PYTHON */
-#endif
-
- string ident = tok.value.as_string();
-
- // An identifier followed by ( represents a function call
- tok = next_token(in, tflags);
- if (tok.kind == token_t::LPAREN) {
- node = new op_t(op_t::FUNC_NAME);
- node->set_string(ident);
-
- ptr_op_t call_node(new op_t(op_t::O_CALL));
- call_node->set_left(node);
- call_node->set_right(parse_value_expr(in, tflags | XPATH_PARSE_PARTIAL));
-
- tok = next_token(in, tflags);
- if (tok.kind != token_t::RPAREN)
- tok.unexpected(0xff, ')');
-
- node = call_node;
- } else {
- if (std::isdigit(ident[0])) {
- node = new op_t(op_t::ARG_INDEX);
- node->set_long(lexical_cast<unsigned int>(ident.c_str()));
- }
- else if (optional<node_t::nameid_t> id =
- document_t::lookup_builtin_id(ident)) {
- node = new op_t(op_t::NODE_ID);
- node->set_name(*id);
- }
- else {
- node = new op_t(op_t::NODE_NAME);
- node->set_string(ident);
- }
- push_token(tok);
- }
- break;
- }
-
- case token_t::AT_SYM: {
- tok = next_token(in, tflags);
- if (tok.kind != token_t::IDENT)
- throw_(parse_error, "@ symbol must be followed by attribute name");
-
- string ident = tok.value.as_string();
- if (optional<node_t::nameid_t> id = document_t::lookup_builtin_id(ident)) {
- node = new op_t(op_t::ATTR_ID);
- node->set_name(*id);
- }
- else {
- node = new op_t(op_t::ATTR_NAME);
- node->set_string(ident);
- }
- break;
- }
-
- case token_t::DOLLAR:
- tok = next_token(in, tflags);
- if (tok.kind != token_t::IDENT)
- throw parse_error("$ symbol must be followed by variable name");
-
- node = new op_t(op_t::VAR_NAME);
- node->set_string(tok.value.as_string());
- break;
-
- case token_t::DOT:
- node = new op_t(op_t::NODE_ID);
- node->set_name(document_t::CURRENT);
- break;
- case token_t::DOTDOT:
- node = new op_t(op_t::NODE_ID);
- node->set_name(document_t::PARENT);
- break;
- case token_t::SLASH:
- node = new op_t(op_t::NODE_ID);
- node->set_name(document_t::ROOT);
- push_token();
- break;
- case token_t::STAR:
- node = new op_t(op_t::NODE_ID);
- node->set_name(document_t::ALL);
- break;
-
- case token_t::LPAREN:
- node = new op_t(op_t::O_COMMA);
- node->set_left(parse_value_expr(in, tflags | XPATH_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;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_predicate_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_value_term(in, tflags));
-
- if (node) {
- token_t& tok = next_token(in, tflags);
- while (tok.kind == token_t::LBRACKET) {
- ptr_op_t prev(node);
- node = new op_t(op_t::O_PRED);
- node->set_left(prev);
- node->set_right(parse_value_expr(in, tflags | XPATH_PARSE_PARTIAL));
- if (! node->right())
- throw_(parse_error, "[ operator not followed by valid expression");
-
- tok = next_token(in, tflags);
- if (tok.kind != token_t::RBRACKET)
- tok.unexpected(0xff, ']');
-
- tok = next_token(in, tflags);
- }
-
- push_token(tok);
- }
-
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_path_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_predicate_expr(in, tflags));
-
- if (node) {
- token_t& tok = next_token(in, tflags);
- if (tok.kind == token_t::SLASH) {
- ptr_op_t prev(node);
-
- tok = next_token(in, tflags);
- node = 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);
- node->set_right(parse_path_expr(in, tflags));
- if (! node->right())
- throw_(parse_error, "/ operator not followed by a valid term");
- } else {
- push_token(tok);
- }
- }
-
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_unary_expr(std::istream& in, 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 texpr(parse_path_expr(in, tflags));
- if (! texpr)
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
-
- // A very quick optimization
- if (texpr->kind == op_t::VALUE) {
- texpr->as_value().in_place_negate();
- node = texpr;
- } else {
- node = new op_t(op_t::O_NOT);
- node->set_left(texpr);
- }
- break;
- }
-
- case token_t::MINUS: {
- ptr_op_t texpr(parse_path_expr(in, tflags));
- if (! texpr)
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
-
- // A very quick optimization
- if (texpr->kind == op_t::VALUE) {
- texpr->as_value().in_place_negate();
- node = texpr;
- } else {
- node = new op_t(op_t::O_NEG);
- node->set_left(texpr);
- }
- break;
- }
-
- default:
- push_token(tok);
- node = parse_path_expr(in, tflags);
- break;
- }
-
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_union_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_unary_expr(in, tflags));
-
- if (node) {
- token_t& tok = next_token(in, tflags);
- if (tok.kind == token_t::PIPE || tok.kind == token_t::KW_UNION) {
- ptr_op_t prev(node);
- node = new op_t(op_t::O_UNION);
- node->set_left(prev);
- node->set_right(parse_union_expr(in, tflags));
- if (! node->right())
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
- } else {
- push_token(tok);
- }
- }
-
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_mul_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_union_expr(in, 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, tflags));
- if (! node->right())
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
-
- tok = next_token(in, tflags);
- }
- push_token(tok);
- }
-
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_add_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_mul_expr(in, 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, tflags));
- if (! node->right())
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
-
- tok = next_token(in, tflags);
- }
- push_token(tok);
- }
-
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_logic_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_add_expr(in, tflags));
-
- if (node) {
- op_t::kind_t kind = op_t::LAST;
- flags_t _flags = tflags;
- token_t& tok = next_token(in, tflags);
- switch (tok.kind) {
- case token_t::EQUAL:
- kind = op_t::O_EQ;
- break;
- case token_t::NEQUAL:
- kind = op_t::O_NEQ;
- break;
- case token_t::LESS:
- kind = op_t::O_LT;
- break;
- case token_t::LESSEQ:
- kind = op_t::O_LTE;
- break;
- case token_t::GREATER:
- kind = op_t::O_GT;
- break;
- case token_t::GREATEREQ:
- kind = op_t::O_GTE;
- break;
- default:
- push_token(tok);
- break;
- }
-
- if (kind != op_t::LAST) {
- ptr_op_t prev(node);
- node = new op_t(kind);
- node->set_left(prev);
- node->set_right(parse_add_expr(in, _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;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_and_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_logic_expr(in, 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, tflags));
- if (! node->right())
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
- } else {
- push_token(tok);
- }
- }
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_or_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_and_expr(in, 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, tflags));
- if (! node->right())
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
- } else {
- push_token(tok);
- }
- }
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_value_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_or_expr(in, 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, 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 & XPATH_PARSE_PARTIAL)
- push_token(tok);
- else
- tok.unexpected();
- }
- }
- else if (! (tflags & XPATH_PARSE_PARTIAL)) {
- throw_(parse_error, "Failed to parse value expression");
- }
-
- return node;
-}
-
-xpath_t::ptr_op_t
-xpath_t::parse_expr(std::istream& in, flags_t tflags) const
-{
- 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;
-}
-
-
-xpath_t::ptr_op_t xpath_t::op_t::compile(scope_t& scope)
-{
- switch (kind) {
- case VAR_NAME:
- case FUNC_NAME:
- if (ptr_op_t def = scope.lookup(as_string())) {
-#if 1
- return def;
-#else
- // Aren't definitions compiled when they go in? Would
- // recompiling here really add any benefit?
- return def->compile(scope);
-#endif
- }
- return this;
-
- 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 xpath_t::op_t::current_value(scope_t& scope)
-{
- xpath_t::context_scope_t& context(CONTEXT_SCOPE(scope));
- return context.value();
-}
-
-node_t& xpath_t::op_t::current_xml_node(scope_t& scope)
-{
- xpath_t::context_scope_t& context(CONTEXT_SCOPE(scope));
- return context.xml_node();
-}
-
-namespace {
- value_t select_nodes(xpath_t::scope_t& scope, const value_t& nodes,
- xpath_t::ptr_op_t selection_path, bool recurse);
-
- value_t select_recursively(xpath_t::scope_t& scope, node_t& xml_node,
- xpath_t::ptr_op_t selection_path)
- {
- value_t result;
-
- if (xml_node.is_parent_node()) {
- parent_node_t& parent_node(xml_node.as_parent_node());
- foreach (node_t * child, parent_node)
- result.push_back(select_nodes(scope, child, selection_path, true));
- }
- return result;
- }
-
- value_t select_nodes(xpath_t::scope_t& scope, const value_t& nodes,
- xpath_t::ptr_op_t selection_path, bool recurse)
- {
- if (nodes.is_null())
- return NULL_VALUE;
-
- value_t result;
-
- if (! nodes.is_sequence()) {
- xpath_t::context_scope_t node_scope(scope, nodes, 0, 1);
- result.push_back(selection_path->calc(node_scope));
-
- if (recurse && nodes.is_xml_node())
- result.push_back(select_recursively(scope, *nodes.as_xml_node(),
- selection_path));
- } else {
- std::size_t index = 0;
- std::size_t size = nodes.as_sequence().size();
-
- foreach (const value_t& node, nodes.as_sequence()) {
- xpath_t::context_scope_t node_scope(scope, node, index, size);
- result.push_back(selection_path->calc(node_scope));
-
- if (recurse && nodes.is_xml_node())
- result.push_back(select_recursively(scope, *node.as_xml_node(),
- selection_path));
-
- index++;
- }
- }
- return result;
- }
-}
-
-value_t xpath_t::op_t::calc(scope_t& scope)
-{
- bool find_all_nodes = false;
-
- switch (kind) {
- case VALUE:
- return as_value();
-
- 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;
-
- case FUNCTION:
- // This should never be evaluated directly; it only appears as the
- // left node of an O_CALL operator.
- assert(false);
- break;
-
- 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);
- }
-
- 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;
- }
-
- 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_node_t&> 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 &current_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_t&> value =
- kind == ATTR_ID ? current_xml_node(scope).get_attr(as_name()) :
- current_xml_node(scope).get_attr(as_string()))
- return *value;
-
- break;
-
- case O_NEQ:
- return left()->calc(scope) != right()->calc(scope);
- case O_EQ:
- return left()->calc(scope) == right()->calc(scope);
- case O_LT:
- return left()->calc(scope) < right()->calc(scope);
- case O_LTE:
- return left()->calc(scope) <= right()->calc(scope);
- case O_GT:
- return left()->calc(scope) > right()->calc(scope);
- case O_GTE:
- return left()->calc(scope) >= right()->calc(scope);
-
- case 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);
-
- case O_COMMA:
- case O_UNION: {
- value_t result(left()->calc(scope));
-
- ptr_op_t next = right();
- while (next) {
- ptr_op_t value_op;
- if (next->kind == O_COMMA || next->kind == O_UNION) {
- value_op = next->left();
- next = next->right();
- } else {
- value_op = next;
- next = NULL;
- }
-
- result.push_back(value_op->calc(scope));
- }
- return result;
- }
-
- case LAST:
- default:
- assert(false);
- break;
- }
-
- return NULL_VALUE;
-}
-
-
-bool xpath_t::op_t::print(std::ostream& out, print_context_t& context) const
-{
- bool found = false;
-
- if (context.start_pos && this == context.op_to_find) {
- *context.start_pos = (long)out.tellp() - 1;
- found = true;
- }
-
- string symbol;
-
- switch (kind) {
- case VALUE: {
- const value_t& value(as_value());
- switch (value.type()) {
- case value_t::VOID:
- out << "<VOID>";
- break;
- case value_t::BOOLEAN:
- if (value)
- out << "1";
- else
- out << "0";
- break;
- case value_t::INTEGER:
- out << value;
- break;
- case value_t::AMOUNT:
- if (! context.relaxed)
- out << '{';
- out << value;
- if (! context.relaxed)
- out << '}';
- break;
- case value_t::BALANCE:
- case value_t::BALANCE_PAIR:
- assert(false);
- break;
- case value_t::DATETIME:
- out << '[' << value << ']';
- break;
- case value_t::STRING:
- out << '"' << value << '"';
- break;
-
- case value_t::XML_NODE:
- out << '<' << value << '>';
- break;
- case value_t::POINTER:
- out << '&' << value;
- break;
- case value_t::SEQUENCE:
- out << '~' << value << '~';
- break;
- }
- break;
- }
-
- case ATTR_ID:
- out << '@';
- // fall through...
- case NODE_ID: {
- context_scope_t& node_scope(CONTEXT_SCOPE(context.scope));
- if (optional<const char *> name =
- node_scope.xml_node().document().lookup_name(as_name()))
- out << *name;
- else
- out << '#' << as_name();
- break;
- }
-
- case NODE_NAME:
- case FUNC_NAME:
- out << as_string();
- break;
-
- case ATTR_NAME:
- out << '@' << as_string();
- break;
-
- case VAR_NAME:
- out << '$' << as_string();
- break;
-
- case FUNCTION:
- out << "<FUNCTION>";
- break;
-
- case ARG_INDEX:
- out << '@' << as_long();
- break;
-
- case O_NOT:
- out << "!";
- if (left() && left()->print(out, context))
- found = true;
- break;
- case O_NEG:
- out << "-";
- if (left() && left()->print(out, context))
- found = true;
- break;
-
- case O_UNION:
- if (left() && left()->print(out, context))
- found = true;
- out << " | ";
- if (right() && right()->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;
-
- case O_CALL:
- if (left() && left()->print(out, context))
- found = true;
- out << "(";
- if (right() && right()->print(out, context))
- found = true;
- out << ")";
- break;
-
- case O_FIND:
- if (left() && left()->print(out, context))
- found = true;
- out << "/";
- if (right() && right()->print(out, context))
- found = true;
- break;
- case O_RFIND:
- if (left() && left()->print(out, context))
- found = true;
- out << "//";
- if (right() && right()->print(out, context))
- found = true;
- break;
- case O_PRED:
- if (left() && left()->print(out, context))
- found = true;
- out << "[";
- if (right() && right()->print(out, context))
- found = true;
- out << "]";
- break;
-
- 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 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 - " << as_value();
- break;
-
- case NODE_NAME:
- out << "NODE_NAME - " << as_string();
- break;
- case NODE_ID:
- out << "NODE_ID - " << as_name();
- break;
-
- case ATTR_NAME:
- out << "ATTR_NAME - " << as_string();
- break;
- case ATTR_ID:
- out << "ATTR_ID - " << as_name();
- break;
-
- case FUNC_NAME:
- out << "FUNC_NAME - " << as_string();
- break;
-
- case VAR_NAME:
- out << "VAR_NAME - " << as_string();
- break;
-
- case ARG_INDEX:
- out << "ARG_INDEX - " << as_long();
- break;
-
- case FUNCTION:
- out << "FUNCTION";
- break;
-
- case O_CALL: out << "O_CALL"; break;
-
- 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_COMMA: out << "O_COMMA"; break;
-
- case O_FIND: out << "O_FIND"; break;
- case O_RFIND: out << "O_RFIND"; break;
- case O_PRED: out << "O_PRED"; 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 xml
-
-
-value_t xml_command(xml::xpath_t::call_scope_t& args)
-{
- assert(args.size() == 0);
-
- value_t ostream = args.resolve("ostream");
- std::ostream& outs(ostream.as_ref_lval<std::ostream>());
-
- xml::xpath_t::context_scope_t& node_context(CONTEXT_SCOPE(args));
- node_context.xml_node().print(outs);
-
- return true;
-}
-
-} // namespace ledger
diff --git a/src/traversal/xpath.h b/src/traversal/xpath.h
deleted file mode 100644
index 26a887a1..00000000
--- a/src/traversal/xpath.h
+++ /dev/null
@@ -1,873 +0,0 @@
-/*
- * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of New Artisans LLC nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _XPATH_H
-#define _XPATH_H
-
-#include "document.h"
-
-namespace ledger {
-namespace xml {
-
-class xpath_t
-{
-public:
- struct op_t;
- typedef intrusive_ptr<op_t> ptr_op_t;
-
- static void initialize();
- static void shutdown();
-
- DECLARE_EXCEPTION(parse_error);
- DECLARE_EXCEPTION(compile_error);
- DECLARE_EXCEPTION(calc_error);
-
-public:
- class call_scope_t;
-
- typedef function<value_t (call_scope_t&)> function_t;
-
-#define MAKE_FUNCTOR(x) \
- xml::xpath_t::op_t::wrap_functor(bind(&x, this, _1))
-#define WRAP_FUNCTOR(x) \
- xml::xpath_t::op_t::wrap_functor(x)
-
-public:
- class scope_t : public noncopyable
- {
- public:
- enum type_t {
- CHILD_SCOPE,
- SYMBOL_SCOPE,
- CALL_SCOPE,
- CONTEXT_SCOPE
- } type_;
-
- explicit scope_t(type_t _type) : type_(_type) {
- TRACE_CTOR(xpath_t::scope_t, "type_t");
- }
- virtual ~scope_t() {
- TRACE_DTOR(xpath_t::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) {
- return lookup(name)->calc(*this);
- }
-
- virtual optional<scope_t&> find_scope(const type_t _type,
- bool skip_this = false) = 0;
- virtual optional<scope_t&> find_first_scope(const type_t _type1,
- const type_t _type2,
- bool skip_this = false) = 0;
-
- template <typename T>
- T& find_scope(bool skip_this = false) {
- assert(false);
- }
- template <typename T>
- optional<T&> maybe_find_scope(bool skip_this = false) {
- assert(false);
- }
- };
-
- class child_scope_t : public scope_t
- {
- scope_t * parent;
-
- public:
- explicit child_scope_t(type_t _type = CHILD_SCOPE)
- : scope_t(_type), parent(NULL) {
- TRACE_CTOR(xpath_t::child_scope_t, "type_t");
- }
- explicit child_scope_t(scope_t& _parent, type_t _type = CHILD_SCOPE)
- : scope_t(_type), parent(&_parent) {
- TRACE_CTOR(xpath_t::child_scope_t, "scope_t&, type_t");
- }
- virtual ~child_scope_t() {
- TRACE_DTOR(xpath_t::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<scope_t&> 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<child_scope_t *>(ptr)->parent;
- }
- return none;
- }
-
- virtual optional<scope_t&> 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<child_scope_t *>(ptr)->parent;
- }
- return none;
- }
- };
-
- class symbol_scope_t : public child_scope_t
- {
- typedef std::map<const string, ptr_op_t> symbol_map;
- symbol_map symbols;
-
- public:
- explicit symbol_scope_t()
- : child_scope_t(SYMBOL_SCOPE) {
- TRACE_CTOR(xpath_t::symbol_scope_t, "");
- }
- explicit symbol_scope_t(scope_t& _parent)
- : child_scope_t(_parent, SYMBOL_SCOPE) {
- TRACE_CTOR(xpath_t::symbol_scope_t, "scope_t&");
- }
- virtual ~symbol_scope_t() {
- TRACE_DTOR(xpath_t::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;
-
- public:
- explicit call_scope_t(scope_t& _parent)
- : child_scope_t(_parent, CALL_SCOPE) {
- TRACE_CTOR(xpath_t::call_scope_t, "scope_t&");
- }
- virtual ~call_scope_t() {
- TRACE_DTOR(xpath_t::call_scope_t);
- }
-
- void set_args(const value_t& _args) {
- args = _args;
- }
-
- value_t& value() {
- return args;
- }
-
- value_t& operator[](const int index) {
- return args[index];
- }
- const value_t& operator[](const int index) const {
- 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();
- }
- };
-
- 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(xpath_t::context_scope_t, "scope_t&, const value_t&, ...");
- }
- virtual ~context_scope_t() {
- TRACE_DTOR(xpath_t::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;
- }
- node_t& xml_node() {
- assert(current_element.is_xml_node());
- return *current_element.as_xml_node();
- }
- };
-
-#define XPATH_PARSE_NORMAL 0x00
-#define XPATH_PARSE_PARTIAL 0x01
-#define XPATH_PARSE_RELAXED 0x02
-#define XPATH_PARSE_NO_MIGRATE 0x04
-#define XPATH_PARSE_NO_REDUCE 0x08
-#define XPATH_PARSE_ALLOW_DATE 0x10
-
- typedef uint_least8_t flags_t;
-
-private:
- struct token_t
- {
- enum kind_t {
- VALUE, // any kind of literal value
-
- IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
- DOLLAR, // $
- AT_SYM, // @
-
- DOT, // .
- DOTDOT, // ..
- SLASH, // /
-
- LPAREN, // (
- RPAREN, // )
- LBRACKET, // [
- RBRACKET, // ]
-
- EQUAL, // =
- NEQUAL, // !=
- LESS, // <
- LESSEQ, // <=
- GREATER, // >
- GREATEREQ, // >=
-
- MINUS, // -
- PLUS, // +
- STAR, // *
- KW_DIV,
-
- EXCLAM, // !
- KW_AND,
- KW_OR,
- KW_MOD,
-
- PIPE, // |
- KW_UNION,
-
- COMMA, // ,
-
- TOK_EOF,
- UNKNOWN
- } kind;
-
- char symbol[3];
- value_t value;
- std::size_t length;
-
- explicit token_t() : kind(UNKNOWN), length(0) {
- TRACE_CTOR(xpath_t::token_t, "");
- }
- token_t(const token_t& other) {
- assert(false);
- TRACE_CTOR(xpath_t::token_t, "copy");
- *this = other;
- }
- ~token_t() {
- TRACE_DTOR(xpath_t::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');
- };
-
-public:
- class path_iterator_t
- {
- typedef node_t * pointer;
- typedef node_t& reference;
-
- xpath_t& path_expr;
- scope_t& scope;
-
- mutable value_t::sequence_t sequence;
- mutable bool searched;
-
- public:
- typedef value_t::sequence_t::iterator iterator;
- typedef value_t::sequence_t::const_iterator const_iterator;
-
- path_iterator_t(xpath_t& _path_expr, scope_t& _scope)
- : path_expr(_path_expr), scope(_scope), searched(false) {}
-
- iterator begin() {
- if (! searched) {
- sequence = path_expr.calc(scope).to_sequence();
- searched = true;
- }
- return sequence.begin();
- }
- const_iterator begin() const {
- return const_cast<path_iterator_t *>(this)->begin();
- }
-
- iterator end() { return sequence.end(); }
- const_iterator end() const { return sequence.end(); }
- };
-
- struct op_t : public noncopyable
- {
- enum kind_t {
- VALUE,
-
- FUNC_NAME,
- VAR_NAME,
- ARG_INDEX,
-
- NODE_ID,
- NODE_NAME,
- ATTR_ID,
- ATTR_NAME,
-
- CONSTANTS, // constants end here
-
- FUNCTION,
-
- TERMINALS, // terminals end here
-
- O_CALL,
- O_ARG,
-
- O_FIND,
- O_RFIND,
- O_PRED,
-
- O_NEQ,
- O_EQ,
- O_LT,
- O_LTE,
- O_GT,
- O_GTE,
-
- O_ADD,
- O_SUB,
- O_MUL,
- O_DIV,
- O_NEG,
-
- O_NOT,
- O_AND,
- O_OR,
-
- O_UNION,
-
- O_COMMA,
-
- LAST // operators end here
- };
-
- kind_t kind;
- mutable short refc;
- ptr_op_t left_;
-
- variant<unsigned int, // used by ARG_INDEX and O_ARG
- value_t, // used by constant VALUE
- string, // used by constants SYMBOL, *_NAME
- function_t, // used by terminal FUNCTION
- node_t::nameid_t, // used by NODE_ID and ATTR_ID
- ptr_op_t> // used by all binary operators
- data;
-
- explicit op_t(const kind_t _kind) : kind(_kind), refc(0){
- TRACE_CTOR(xpath_t::op_t, "const kind_t");
- }
- ~op_t() {
- TRACE_DTOR(xpath_t::op_t);
-
- DEBUG("ledger.xpath.memory", "Destroying " << this);
- assert(refc == 0);
- }
-
- bool is_long() const {
- return data.type() == typeid(unsigned int);
- }
- unsigned int& as_long() {
- assert(kind == ARG_INDEX || kind == O_ARG);
- return boost::get<unsigned int>(data);
- }
- const unsigned int& as_long() const {
- return const_cast<op_t *>(this)->as_long();
- }
- void set_long(unsigned int val) {
- data = val;
- }
-
- bool is_value() const {
- return kind == VALUE;
- }
- value_t& as_value() {
- assert(kind == VALUE);
- return boost::get<value_t>(data);
- }
- const value_t& as_value() const {
- return const_cast<op_t *>(this)->as_value();
- }
- void set_value(const value_t& val) {
- data = val;
- }
-
- bool is_string() const {
- return data.type() == typeid(string);
- }
- string& as_string() {
- assert(kind == NODE_NAME || kind == ATTR_NAME || kind == FUNC_NAME);
- return boost::get<string>(data);
- }
- const string& as_string() const {
- return const_cast<op_t *>(this)->as_string();
- }
- void set_string(const string& val) {
- data = val;
- }
-
- bool is_function() const {
- return kind == FUNCTION;
- }
- function_t& as_function() {
- assert(kind == FUNCTION);
- return boost::get<function_t>(data);
- }
- const function_t& as_function() const {
- return const_cast<op_t *>(this)->as_function();
- }
- void set_function(const function_t& val) {
- data = val;
- }
-
- bool is_name() const {
- return data.type() == typeid(node_t::nameid_t);
- }
- node_t::nameid_t& as_name() {
- assert(kind == NODE_ID || kind == ATTR_ID);
- return boost::get<node_t::nameid_t>(data);
- }
- const node_t::nameid_t& as_name() const {
- return const_cast<op_t *>(this)->as_name();
- }
- void set_name(const node_t::nameid_t& val) {
- data = val;
- }
-
- ptr_op_t& as_op() {
- assert(kind > TERMINALS);
- return boost::get<ptr_op_t>(data);
- }
- const ptr_op_t& as_op() const {
- return const_cast<op_t *>(this)->as_op();
- }
-
- 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();
- }
- 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);
- node_t& current_xml_node(scope_t& scope);
- value_t calc(scope_t& scope);
-
- 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(xpath_t::op_t * op) {
- op->acquire();
- }
- friend inline void intrusive_ptr_release(xpath_t::op_t * op) {
- op->release();
- }
- };
-
- class op_predicate {
- ptr_op_t op;
- public:
- explicit op_predicate(ptr_op_t _op) : op(_op) {}
- bool operator()(scope_t& scope) {
- return op->calc(scope).to_boolean();
- }
- };
-
-public:
- ptr_op_t ptr;
-
- xpath_t& operator=(ptr_op_t _expr) {
- expr = "";
- ptr = _expr;
- return *this;
- }
-
-#ifdef THREADSAFE
- mutable token_t lookahead;
-#else
- static token_t * lookahead;
-#endif
- mutable bool use_lookahead;
-
- token_t& next_token(std::istream& in, flags_t tflags) const {
- if (use_lookahead)
- use_lookahead = false;
- else
-#ifdef THREADSAFE
- lookahead.next(in, tflags);
-#else
- lookahead->next(in, tflags);
-#endif
-#ifdef THREADSAFE
- return lookahead;
-#else
- return *lookahead;
-#endif
- }
- void push_token(const token_t& tok) const {
-#ifdef THREADSAFE
- assert(&tok == &lookahead);
-#else
- assert(&tok == lookahead);
-#endif
- use_lookahead = true;
- }
- void push_token() const {
- use_lookahead = true;
- }
-
- ptr_op_t parse_value_term(std::istream& in, flags_t flags) const;
- ptr_op_t parse_predicate_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_path_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_unary_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_union_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_mul_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_add_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_logic_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_and_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_or_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_querycolon_expr(std::istream& in, flags_t flags) const;
- ptr_op_t parse_value_expr(std::istream& in, flags_t flags) const;
-
- ptr_op_t parse_expr(std::istream& in,
- flags_t flags = XPATH_PARSE_RELAXED) const;
-
- ptr_op_t parse_expr(const string& str,
- flags_t tflags = XPATH_PARSE_RELAXED) const
- {
- std::istringstream stream(str);
-#if 0
- try {
-#endif
- return parse_expr(stream, tflags);
-#if 0
- }
- catch (error * err) {
- err->context.push_back
- (new line_context(str, (long)stream.tellg() - 1,
- "While parsing value expression:"));
- throw err;
- }
-#endif
- }
-
- ptr_op_t parse_expr(const char * p,
- flags_t tflags = XPATH_PARSE_RELAXED) const {
- return parse_expr(string(p), tflags);
- }
-
- bool print(std::ostream& out, op_t::print_context_t& context) const {
- if (ptr)
- ptr->print(out, context);
- return true;
- }
-
-public:
- string expr;
- flags_t flags; // flags used to parse `expr'
-
- explicit xpath_t() : ptr(NULL), use_lookahead(false), flags(0) {
- TRACE_CTOR(xpath_t, "");
- }
- explicit xpath_t(ptr_op_t _ptr) : ptr(_ptr), use_lookahead(false) {
- TRACE_CTOR(xpath_t, "ptr_op_t");
- }
-
- explicit xpath_t(const string& _expr, flags_t _flags = XPATH_PARSE_RELAXED)
- : ptr(NULL), use_lookahead(false), flags(0) {
- TRACE_CTOR(xpath_t, "const string&, flags_t");
- if (! _expr.empty())
- parse(_expr, _flags);
- }
- explicit xpath_t(std::istream& in, flags_t _flags = XPATH_PARSE_RELAXED)
- : ptr(NULL), use_lookahead(false), flags(0) {
- TRACE_CTOR(xpath_t, "std::istream&, flags_t");
- parse(in, _flags);
- }
- xpath_t(const xpath_t& other)
- : ptr(other.ptr), use_lookahead(false),
- expr(other.expr), flags(other.flags) {
- TRACE_CTOR(xpath_t, "copy");
- }
- ~xpath_t() {
- TRACE_DTOR(xpath_t);
- }
-
-#if 0
- xpath_t& operator=(const string& _expr) {
- parse(_expr);
- return *this;
- }
-#endif
- xpath_t& operator=(const xpath_t& _expr);
-
-#if 0
- operator ptr_op_t() throw() {
- return ptr;
- }
- operator bool() const throw() {
- return ptr != NULL;
- }
- operator string() const throw() {
- return expr;
- }
-#endif
-
- void parse(const string& _expr, flags_t _flags = XPATH_PARSE_RELAXED) {
- expr = _expr;
- flags = _flags;
- ptr = parse_expr(_expr, _flags);
- }
- void parse(std::istream& in, flags_t _flags = XPATH_PARSE_RELAXED) {
- expr = "";
- flags = _flags;
- ptr = parse_expr(in, _flags);
- }
-
- void compile(scope_t& scope) {
- if (ptr.get())
- ptr = ptr->compile(scope);
- }
-
- value_t calc(scope_t& scope) const {
- if (ptr.get())
- return ptr->calc(scope);
- return NULL_VALUE;
- }
-
- static value_t eval(const string& _expr, scope_t& scope) {
- return xpath_t(_expr).calc(scope);
- }
-
- 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);
- }
-};
-
-inline xpath_t::ptr_op_t
-xpath_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 xpath_t::ptr_op_t
-xpath_t::op_t::wrap_value(const value_t& val) {
- xpath_t::ptr_op_t temp(new xpath_t::op_t(xpath_t::op_t::VALUE));
- temp->set_value(val);
- return temp;
-}
-
-inline xpath_t::ptr_op_t
-xpath_t::op_t::wrap_functor(const function_t& fobj) {
- xpath_t::ptr_op_t temp(new xpath_t::op_t(xpath_t::op_t::FUNCTION));
- temp->set_function(fobj);
- return temp;
-}
-
-template<>
-inline xpath_t::symbol_scope_t&
-xpath_t::scope_t::find_scope<xpath_t::symbol_scope_t>(bool skip_this) {
- optional<scope_t&> scope = find_scope(SYMBOL_SCOPE, skip_this);
- assert(scope);
- return downcast<symbol_scope_t>(*scope);
-}
-
-template<>
-inline xpath_t::call_scope_t&
-xpath_t::scope_t::find_scope<xpath_t::call_scope_t>(bool skip_this) {
- optional<scope_t&> scope = find_scope(CALL_SCOPE, skip_this);
- assert(scope);
- return downcast<call_scope_t>(*scope);
-}
-
-template<>
-inline xpath_t::context_scope_t&
-xpath_t::scope_t::find_scope<xpath_t::context_scope_t>(bool skip_this) {
- optional<scope_t&> scope = find_scope(CONTEXT_SCOPE, skip_this);
- assert(scope);
- return downcast<context_scope_t>(*scope);
-}
-
-#define FIND_SCOPE(scope_type, scope_ref) \
- downcast<xml::xpath_t::scope_t>(scope_ref).find_scope<scope_type>()
-
-#define CALL_SCOPE(scope_ref) \
- FIND_SCOPE(xml::xpath_t::call_scope_t, scope_ref)
-#define SYMBOL_SCOPE(scope_ref) \
- FIND_SCOPE(xml::xpath_t::symbol_scope_t, scope_ref)
-#define CONTEXT_SCOPE(scope_ref) \
- FIND_SCOPE(xml::xpath_t::context_scope_t, scope_ref)
-
-} // namespace xml
-
-value_t xml_command(xml::xpath_t::call_scope_t& args);
-
-} // namespace ledger
-
-#endif // _XPATH_H