ledger/src/xpath.h

955 lines
23 KiB
C++

/*
* 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,
NODE_SCOPE,
PREDICATE_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;
template <typename T>
T& 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;
}
};
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 element;
optional<value_t> sequence;
explicit context_scope_t(scope_t& _parent,
const value_t& _element,
const optional<value_t>& _sequence = none)
: child_scope_t(_parent, CONTEXT_SCOPE),
element(_element), sequence(_sequence)
{
TRACE_CTOR(xpath_t::context_scope_t,
"scope_t&, const value_t&, const optional<value_t>&");
assert(! element.is_sequence());
if (DO_VERIFY() && sequence) {
if (sequence->is_sequence()) {
value_t::sequence_t seq(sequence->as_sequence());
value_t::iterator i = std::find(seq.begin(), seq.end(), element);
assert(i != seq.end());
} else {
assert(element == *sequence);
}
}
}
virtual ~context_scope_t() {
TRACE_DTOR(xpath_t::context_scope_t);
}
const std::size_t index() const {
if (! sequence) {
return 0;
} else {
value_t::sequence_t seq(sequence->as_sequence());
value_t::iterator i = std::find(seq.begin(), seq.end(), element);
assert(i != seq.end());
int_least16_t offset = i - seq.begin();
assert(offset >= 0);
return std::size_t(offset);
}
}
const std::size_t size() const {
return sequence ? sequence->size() : (element.is_null() ? 0 : 1);
}
value_t& value() {
return element;
}
node_t& xml_node() {
if (! element.is_xml_node())
throw_(calc_error, "The current context value is not an XML node");
return *element.as_xml_node();
}
};
class node_scope_t : public context_scope_t
{
public:
node_scope_t(scope_t& _parent, node_t& _node)
: context_scope_t(_parent, &_node) {
TRACE_CTOR(xpath_t::node_scope_t, "scope_t&, node_t&");
type_ = NODE_SCOPE;
}
virtual ~node_scope_t() {
TRACE_DTOR(xpath_t::node_scope_t);
}
};
typedef node_scope_t document_scope_t;
class predicate_scope_t : public child_scope_t
{
public:
ptr_op_t predicate;
explicit predicate_scope_t(scope_t& _parent,
const ptr_op_t& _predicate)
: child_scope_t(_parent, PREDICATE_SCOPE), predicate(_predicate)
{
TRACE_CTOR(xpath_t::predicate_scope_t, "scope_t&, const ptr_op_t&");
}
virtual ~predicate_scope_t() {
TRACE_DTOR(xpath_t::predicate_scope_t);
}
bool test(scope_t& scope,
const value_t& val,
const optional<value_t>& sequence = none) const {
context_scope_t context_scope(scope, val, sequence);
if (predicate->is_value()) {
value_t& predicate_value(predicate->as_value());
if (predicate_value.is_long())
return predicate_value.as_long() == (long)context_scope.index() + 1;
}
return predicate->calc(context_scope).to_boolean();
}
};
#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:
#if 0
class path_iterator_t
{
typedef NodeType * pointer;
typedef NodeType& reference;
path_t path;
node_t& start;
scope_t& scope;
mutable value_t::sequence_t sequence;
mutable bool searched;
struct node_appender_t {
value_t::sequence_t& sequence;
node_appender_t(value_t::sequence_t& _sequence)
: sequence(_sequence) {}
void operator()(const value_t& node) {
sequence.push_back(node);
}
};
public:
typedef value_t::sequence_t::iterator iterator;
typedef value_t::sequence_t::const_iterator const_iterator;
path_iterator_t(const xpath_t& path_expr,
node_t& _start, scope_t& _scope)
: path(path_expr), start(_start), scope(_scope),
searched(false) {
}
iterator begin() {
if (! searched) {
path.visit(start, scope, node_appender_t(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(); }
};
#endif
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 : public noncopyable {
ptr_op_t op;
public:
explicit op_predicate(ptr_op_t _op) : op(_op) {}
bool operator()(scope_t& scope) const {
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);
}
#if 0
path_iterator_t<node_t>
find_all(node_t& start, scope_t& scope) {
return path_iterator_t<node_t>(*this, start, scope);
}
path_iterator_t<const node_t>
find_all(const node_t& start, scope_t& scope) {
return path_iterator_t<const node_t>(*this, start, scope);
}
void visit(node_t& start, scope_t& scope, const path_t::visitor_t& func) {
path_t(*this).visit(start, scope, func);
}
void visit(const node_t& start, scope_t& scope, const
path_t::visitor_t& func) {
path_t(*this).visit(start, scope, func);
}
#endif
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));
if (_left)
node->set_left(_left);
if (_right)
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);
}
template<>
inline xpath_t::node_scope_t&
xpath_t::scope_t::find_scope<xpath_t::node_scope_t>(bool skip_this) {
optional<scope_t&> scope = find_scope(NODE_SCOPE, skip_this);
assert(scope);
return downcast<node_scope_t>(*scope);
}
#define FIND_SCOPE(scope_type, scope_ref) \
downcast<xml::xpath_t::scope_t>(scope_ref).find_scope<scope_type>()
} // namespace xml
value_t xml_command(xml::xpath_t::call_scope_t& args);
} // namespace ledger
#endif // _XPATH_H