Predicates are not working yet.
This commit is contained in:
parent
bf2c8c0f48
commit
7645ca389a
2 changed files with 94 additions and 92 deletions
136
src/xpath.cc
136
src/xpath.cc
|
|
@ -439,7 +439,7 @@ value_t xpath_fn_position(xpath_t::call_scope_t& scope)
|
||||||
{
|
{
|
||||||
xpath_t::context_scope_t& context(CONTEXT_SCOPE(scope));
|
xpath_t::context_scope_t& context(CONTEXT_SCOPE(scope));
|
||||||
|
|
||||||
return context.index();
|
return context.index() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t xpath_fn_text(xpath_t::call_scope_t& scope)
|
value_t xpath_fn_text(xpath_t::call_scope_t& scope)
|
||||||
|
|
@ -1000,7 +1000,9 @@ node_t& xpath_t::op_t::current_xml_node(scope_t& scope)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
value_t search_nodes(value_t nodes, xpath_t::ptr_op_t find_op,
|
value_t search_nodes(value_t nodes, xpath_t::ptr_op_t find_op,
|
||||||
xpath_t::scope_t& scope, bool recurse)
|
xpath_t::scope_t& scope,
|
||||||
|
const xpath_t::op_t::predicate_t& predicate,
|
||||||
|
const bool recurse)
|
||||||
{
|
{
|
||||||
value_t node_sequence = nodes.to_sequence();
|
value_t node_sequence = nodes.to_sequence();
|
||||||
value_t result;
|
value_t result;
|
||||||
|
|
@ -1014,30 +1016,75 @@ namespace {
|
||||||
node_t& node(*node_value.as_xml_node());
|
node_t& node(*node_value.as_xml_node());
|
||||||
xpath_t::context_scope_t node_scope(scope, node, node_sequence);
|
xpath_t::context_scope_t node_scope(scope, node, node_sequence);
|
||||||
|
|
||||||
result.push_back(find_op->calc(node_scope));
|
result.push_back(find_op->calc(node_scope, predicate));
|
||||||
|
|
||||||
if (recurse && node.is_parent_node()) {
|
if (recurse && node.is_parent_node()) {
|
||||||
value_t children;
|
// jww (2007-05-17): This is horrible
|
||||||
foreach (node_t * child, node.as_parent_node())
|
parent_node_t& parent(node.as_parent_node());
|
||||||
children.push_back(child);
|
value_t::sequence_t children;
|
||||||
|
std::copy(parent.begin(), parent.end(), back_inserter(children));
|
||||||
|
|
||||||
result.push_back(search_nodes(children, find_op, scope, recurse));
|
result.push_back(search_nodes(children, find_op, scope, predicate,
|
||||||
|
recurse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value_t prune_values(const value_t& values, xpath_t::scope_t& scope,
|
||||||
|
const xpath_t::op_t::predicate_t& predicate)
|
||||||
|
{
|
||||||
|
if (! predicate)
|
||||||
|
return values;
|
||||||
|
|
||||||
|
value_t values_seq = values.to_sequence();
|
||||||
|
value_t result;
|
||||||
|
std::size_t index = 0;
|
||||||
|
|
||||||
|
foreach (const value_t& value, values_seq.as_sequence()) {
|
||||||
|
xpath_t::context_scope_t value_scope(scope, value, values_seq);
|
||||||
|
value_t predval = predicate(value_scope);
|
||||||
|
if ((predval.is_long() &&
|
||||||
|
predval.as_long() == (long)index + 1) || predval.to_boolean())
|
||||||
|
result.push_back(value);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_t prune_value(const value_t& value, xpath_t::scope_t& scope,
|
||||||
|
const xpath_t::op_t::predicate_t& predicate,
|
||||||
|
const optional<value_t>& sequence = none)
|
||||||
|
{
|
||||||
|
if (! predicate)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
if (value.is_sequence())
|
||||||
|
return prune_values(value, scope, predicate);
|
||||||
|
|
||||||
|
xpath_t::context_scope_t value_scope(scope, value, sequence);
|
||||||
|
value_t predval = predicate(value_scope);
|
||||||
|
if ((predval.is_long() &&
|
||||||
|
predval.as_long() == 1) || predval.to_boolean())
|
||||||
|
return value;
|
||||||
|
|
||||||
|
return NULL_VALUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t xpath_t::op_t::calc(scope_t& scope)
|
value_t xpath_t::op_t::calc(scope_t& scope, const predicate_t& predicate)
|
||||||
{
|
{
|
||||||
|
bool all_nodes = false;
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case VALUE:
|
case VALUE:
|
||||||
return as_value();
|
return prune_values(as_value(), scope, predicate);
|
||||||
|
|
||||||
case VAR_NAME:
|
case VAR_NAME:
|
||||||
case FUNC_NAME:
|
case FUNC_NAME:
|
||||||
if (ptr_op_t reference = compile(scope))
|
if (ptr_op_t reference = compile(scope))
|
||||||
return reference->calc(scope);
|
return reference->calc(scope, predicate);
|
||||||
else
|
else
|
||||||
throw_(calc_error, "Failed to lookup variable or function named '"
|
throw_(calc_error, "Failed to lookup variable or function named '"
|
||||||
<< as_string() << "'");
|
<< as_string() << "'");
|
||||||
|
|
@ -1068,14 +1115,14 @@ value_t xpath_t::op_t::calc(scope_t& scope)
|
||||||
name.empty() ? string("Attempt to call non-function") :
|
name.empty() ? string("Attempt to call non-function") :
|
||||||
(string("Attempt to call unknown function '") + name + "'"));
|
(string("Attempt to call unknown function '") + name + "'"));
|
||||||
|
|
||||||
return func->as_function()(call_args);
|
return prune_values(func->as_function()(call_args), scope, predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
case ARG_INDEX: {
|
case ARG_INDEX: {
|
||||||
call_scope_t& args(scope.find_scope<call_scope_t>());
|
call_scope_t& args(scope.find_scope<call_scope_t>());
|
||||||
|
|
||||||
if (as_long() >= 0 && as_long() < args.size())
|
if (as_long() >= 0 && as_long() < args.size())
|
||||||
return args[as_long()];
|
return prune_values(args[as_long()], scope, predicate);
|
||||||
else
|
else
|
||||||
throw_(calc_error, "Reference to a non-existing argument");
|
throw_(calc_error, "Reference to a non-existing argument");
|
||||||
break;
|
break;
|
||||||
|
|
@ -1083,33 +1130,27 @@ value_t xpath_t::op_t::calc(scope_t& scope)
|
||||||
|
|
||||||
case O_FIND:
|
case O_FIND:
|
||||||
case O_RFIND:
|
case O_RFIND:
|
||||||
return search_nodes(left()->calc(scope), right(), scope, kind == O_RFIND);
|
return search_nodes(left()->calc(scope), right(), scope, predicate,
|
||||||
|
kind == O_RFIND);
|
||||||
|
|
||||||
case NODE_ID:
|
case NODE_ID:
|
||||||
switch (as_name()) {
|
switch (as_name()) {
|
||||||
case document_t::CURRENT:
|
case document_t::CURRENT:
|
||||||
return current_value(scope);
|
return prune_value(current_value(scope), scope, predicate);
|
||||||
|
|
||||||
case document_t::PARENT:
|
case document_t::PARENT:
|
||||||
if (optional<parent_node_t&> parent = current_xml_node(scope).parent())
|
if (optional<parent_node_t&> parent = current_xml_node(scope).parent())
|
||||||
return &*parent;
|
return prune_value(&*parent, scope, predicate);
|
||||||
else
|
else
|
||||||
throw_(std::logic_error, "Attempt to access parent of root node");
|
throw_(std::logic_error, "Attempt to access parent of root node");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case document_t::ROOT:
|
case document_t::ROOT:
|
||||||
return ¤t_xml_node(scope).document();
|
return prune_value(¤t_xml_node(scope).document(), scope, predicate);
|
||||||
|
|
||||||
case document_t::ALL: {
|
case document_t::ALL:
|
||||||
node_t& current_node(current_xml_node(scope));
|
all_nodes = true;
|
||||||
if (! current_node.is_parent_node())
|
break;
|
||||||
throw_(calc_error, "Referencing child nodes from a non-parent value");
|
|
||||||
|
|
||||||
value_t result;
|
|
||||||
foreach (node_t * child, current_node.as_parent_node())
|
|
||||||
result.push_back(child);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break; // pass down to the NODE_NAME case
|
break; // pass down to the NODE_NAME case
|
||||||
|
|
@ -1121,35 +1162,28 @@ value_t xpath_t::op_t::calc(scope_t& scope)
|
||||||
if (current_node.is_parent_node()) {
|
if (current_node.is_parent_node()) {
|
||||||
bool have_name_id = kind == NODE_ID;
|
bool have_name_id = kind == NODE_ID;
|
||||||
|
|
||||||
|
// jww (2007-05-17): This is horrible
|
||||||
|
value_t children = value_t::sequence_t();
|
||||||
|
foreach (node_t * child, current_node.as_parent_node())
|
||||||
|
children.push_back(child);
|
||||||
|
|
||||||
value_t result;
|
value_t result;
|
||||||
foreach (node_t * child, current_node.as_parent_node()) {
|
foreach (value_t& child, children.as_sequence_lval()) {
|
||||||
if (( have_name_id && as_name() == child->name_id()) ||
|
if (all_nodes ||
|
||||||
(! have_name_id && as_string() == child->name()))
|
( have_name_id && as_name() == child.as_xml_node()->name_id()) ||
|
||||||
result.push_back(child);
|
(! have_name_id && as_string() == child.as_xml_node()->name())) {
|
||||||
|
value_t child_value = prune_value(child, scope, predicate, children);
|
||||||
|
if (! child_value.is_null())
|
||||||
|
result.push_back(child_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return NULL_VALUE;
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_PRED: {
|
case O_PRED:
|
||||||
value_t values = left()->calc(scope).to_sequence();
|
return left()->calc(scope, op_functor(right()));
|
||||||
value_t result;
|
|
||||||
|
|
||||||
std::size_t index = 0;
|
|
||||||
foreach (const value_t& value, values.as_sequence()) {
|
|
||||||
xpath_t::context_scope_t value_scope(scope, value);
|
|
||||||
|
|
||||||
value_t predval = right()->calc(value_scope);
|
|
||||||
if ((predval.is_long() &&
|
|
||||||
predval.as_long() == (long)index + 1) ||
|
|
||||||
predval.to_boolean())
|
|
||||||
result.push_back(value);
|
|
||||||
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ATTR_ID:
|
case ATTR_ID:
|
||||||
case ATTR_NAME: {
|
case ATTR_NAME: {
|
||||||
|
|
@ -1158,7 +1192,7 @@ value_t xpath_t::op_t::calc(scope_t& scope)
|
||||||
if (optional<const string&> value =
|
if (optional<const string&> value =
|
||||||
kind == ATTR_ID ? current_node.get_attr(as_long()) :
|
kind == ATTR_ID ? current_node.get_attr(as_long()) :
|
||||||
current_node.get_attr(as_string()))
|
current_node.get_attr(as_string()))
|
||||||
return value_t(*value, true);
|
return prune_value(value_t(*value, true), scope, predicate);
|
||||||
else
|
else
|
||||||
throw_(calc_error, "Attribute '"
|
throw_(calc_error, "Attribute '"
|
||||||
<< (kind == ATTR_ID ?
|
<< (kind == ATTR_ID ?
|
||||||
|
|
@ -1205,12 +1239,12 @@ value_t xpath_t::op_t::calc(scope_t& scope)
|
||||||
|
|
||||||
case O_COMMA:
|
case O_COMMA:
|
||||||
case O_UNION: {
|
case O_UNION: {
|
||||||
value_t result = left()->calc(scope);
|
value_t result = left()->calc(scope, predicate);
|
||||||
|
|
||||||
ptr_op_t next = right();
|
ptr_op_t next = right();
|
||||||
while (next && (next->kind == O_COMMA ||
|
while (next && (next->kind == O_COMMA ||
|
||||||
next->kind == O_UNION)) {
|
next->kind == O_UNION)) {
|
||||||
result.push_back(next->left()->calc(scope));
|
result.push_back(next->left()->calc(scope, predicate));
|
||||||
next = next->right();
|
next = next->right();
|
||||||
}
|
}
|
||||||
assert(! next);
|
assert(! next);
|
||||||
|
|
|
||||||
48
src/xpath.h
48
src/xpath.h
|
|
@ -68,10 +68,7 @@ public:
|
||||||
CHILD_SCOPE,
|
CHILD_SCOPE,
|
||||||
SYMBOL_SCOPE,
|
SYMBOL_SCOPE,
|
||||||
CALL_SCOPE,
|
CALL_SCOPE,
|
||||||
CONTEXT_SCOPE,
|
CONTEXT_SCOPE
|
||||||
#if 0
|
|
||||||
PREDICATE_SCOPE
|
|
||||||
#endif
|
|
||||||
} type_;
|
} type_;
|
||||||
|
|
||||||
explicit scope_t(type_t _type) : type_(_type) {
|
explicit scope_t(type_t _type) : type_(_type) {
|
||||||
|
|
@ -279,37 +276,6 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if 0
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define XPATH_PARSE_NORMAL 0x00
|
#define XPATH_PARSE_NORMAL 0x00
|
||||||
#define XPATH_PARSE_PARTIAL 0x01
|
#define XPATH_PARSE_PARTIAL 0x01
|
||||||
#define XPATH_PARSE_RELAXED 0x02
|
#define XPATH_PARSE_RELAXED 0x02
|
||||||
|
|
@ -638,10 +604,12 @@ public:
|
||||||
static ptr_op_t wrap_value(const value_t& val);
|
static ptr_op_t wrap_value(const value_t& val);
|
||||||
static ptr_op_t wrap_functor(const function_t& fobj);
|
static ptr_op_t wrap_functor(const function_t& fobj);
|
||||||
|
|
||||||
|
typedef function<value_t (scope_t&)> predicate_t;
|
||||||
|
|
||||||
ptr_op_t compile(scope_t& scope);
|
ptr_op_t compile(scope_t& scope);
|
||||||
value_t current_value(scope_t& scope);
|
value_t current_value(scope_t& scope);
|
||||||
node_t& current_xml_node(scope_t& scope);
|
node_t& current_xml_node(scope_t& scope);
|
||||||
value_t calc(scope_t& scope);
|
value_t calc(scope_t& scope, const predicate_t& = predicate_t());
|
||||||
|
|
||||||
struct print_context_t
|
struct print_context_t
|
||||||
{
|
{
|
||||||
|
|
@ -671,12 +639,12 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class op_predicate : public noncopyable {
|
class op_functor {
|
||||||
ptr_op_t op;
|
ptr_op_t op;
|
||||||
public:
|
public:
|
||||||
explicit op_predicate(ptr_op_t _op) : op(_op) {}
|
explicit op_functor(ptr_op_t _op) : op(_op) {}
|
||||||
bool operator()(scope_t& scope) const {
|
value_t operator()(scope_t& scope) const {
|
||||||
return op->calc(scope).to_boolean();
|
return op->calc(scope);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue