Changes to the XPath code.

This commit is contained in:
John Wiegley 2007-05-19 03:07:42 +00:00
parent ede52a9625
commit f75789cf63
2 changed files with 72 additions and 232 deletions

View file

@ -1009,20 +1009,37 @@ node_t& xpath_t::op_t::current_xml_node(scope_t& scope)
} }
namespace { namespace {
value_t find_recursively(node_t& node, xpath_t::scope_t& scope, value_t select_nodes(xpath_t::scope_t& scope, const value_t& nodes,
xpath_t::selection_scope_t& sel_scope) xpath_t::ptr_op_t selection_path, bool recurse)
{ {
value_t result; value_t result;
if (node.is_parent_node()) { if (! nodes.is_sequence()) {
parent_node_t& parent_node(node.as_parent_node()); xpath_t::context_scope_t node_scope(scope, nodes, 0, 1);
result.push_back(selection_path->calc(node_scope));
} else {
std::size_t index = 0; std::size_t index = 0;
std::size_t size = parent_node.size(); std::size_t size = nodes.as_sequence().size();
foreach (node_t * child, parent_node) { foreach (const value_t& node, nodes.as_sequence()) {
xpath_t::context_scope_t node_scope(scope, child, index, size); xpath_t::context_scope_t node_scope(scope, node, index, size);
result.push_back(sel_scope.selection_path->calc(node_scope)); result.push_back(selection_path->calc(node_scope));
if (recurse && node.is_xml_node()) {
node_t& xml_node(*node.as_xml_node());
value_t child_nodes;
if (xml_node.is_parent_node()) {
parent_node_t& parent_node(xml_node.as_parent_node());
foreach (node_t * child, parent_node)
child_nodes.push_back(child);
}
result.push_back(select_nodes(scope, child_nodes,
selection_path, recurse));
}
index++;
} }
} }
return result; return result;
@ -1032,21 +1049,15 @@ namespace {
value_t xpath_t::op_t::calc(scope_t& scope) value_t xpath_t::op_t::calc(scope_t& scope)
{ {
bool find_all_nodes = false; bool find_all_nodes = false;
bool checked = false;
bool have_xml_nodes = false;
value_t result;
switch (kind) { switch (kind) {
case VALUE: case VALUE:
result = as_value(); return as_value();
break;
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)) {
result = reference->calc(scope); return reference->calc(scope);
checked = true;
} else { } else {
throw_(calc_error, "No " << (kind == VAR_NAME ? "variable" : "function") throw_(calc_error, "No " << (kind == VAR_NAME ? "variable" : "function")
<< " named '" << as_string() << "'"); << " named '" << as_string() << "'");
@ -1078,60 +1089,67 @@ 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 + "'"));
result = func->as_function()(call_args); return func->as_function()(call_args);
break;
} }
case ARG_INDEX: { case ARG_INDEX: {
call_scope_t& args(CALL_SCOPE(scope)); call_scope_t& args(CALL_SCOPE(scope));
if (as_long() >= 0 && as_long() < args.size()) if (as_long() >= 0 && as_long() < args.size())
result = args[as_long()]; return args[as_long()];
else else
throw_(calc_error, "Reference to non-existing argument"); throw_(calc_error, "Reference to non-existing argument");
break; break;
} }
case O_FIND: case O_FIND:
case O_RFIND: { case O_RFIND:
selection_scope_t find_scope(scope, right(), kind == O_RFIND); select_nodes(scope, left()->calc(scope), right(), kind == O_RFIND);
result = left()->calc(find_scope);
checked = true;
break; break;
}
case O_PRED: { case O_PRED: {
predicate_scope_t pred_scope(scope, op_predicate(right())); value_t values = left()->calc(scope);
result = left()->calc(pred_scope);
checked = true; 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; break;
} }
case NODE_ID: { case NODE_ID:
bool break_at_end = false;
switch (as_name()) { switch (as_name()) {
case document_t::CURRENT: case document_t::CURRENT:
result = current_value(scope); return current_value(scope);
have_xml_nodes = result.is_xml_node();
break_at_end = true;
break;
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())
result = &*parent; return &*parent;
else else
throw_(std::logic_error, "Attempt to access parent of root node"); throw_(std::logic_error, "Attempt to access parent of root node");
have_xml_nodes = true;
break_at_end = true;
break; break;
case document_t::ROOT: case document_t::ROOT:
result = &current_xml_node(scope).document(); return &current_xml_node(scope).document();
have_xml_nodes = true;
break_at_end = true;
break;
case document_t::ALL: case document_t::ALL:
find_all_nodes = true; find_all_nodes = true;
@ -1140,11 +1158,7 @@ value_t xpath_t::op_t::calc(scope_t& scope)
default: default:
break; // pass down to the NODE_NAME case break; // pass down to the NODE_NAME case
} }
if (break_at_end)
break;
// fall through... // fall through...
}
case NODE_NAME: { case NODE_NAME: {
node_t& current_node(current_xml_node(scope)); node_t& current_node(current_xml_node(scope));
@ -1152,31 +1166,16 @@ value_t xpath_t::op_t::calc(scope_t& scope)
if (current_node.is_parent_node()) { if (current_node.is_parent_node()) {
const bool have_name_id = kind == NODE_ID; const bool have_name_id = kind == NODE_ID;
optional<predicate_scope_t&> pred_scope(PREDICATE_SCOPE(scope));
parent_node_t& parent(current_node.as_parent_node()); parent_node_t& parent(current_node.as_parent_node());
std::size_t index = 0; value_t result;
std::size_t size = parent.size();
foreach (node_t * child, parent) { foreach (node_t * child, parent) {
if (find_all_nodes || if (find_all_nodes ||
( have_name_id && as_name() == child->name_id()) || ( have_name_id && as_name() == child->name_id()) ||
(! have_name_id && as_string() == child->name())) { (! have_name_id && as_string() == child->name()))
if (! pred_scope || ! pred_scope->predicate) { result.push_back(child);
result.push_back(child);
} else {
context_scope_t node_scope(scope, child, index, size);
if (pred_scope->predicate(node_scope))
result.push_back(child);
}
}
index++;
} }
return result;
checked = true;
have_xml_nodes = true;
} }
break; break;
} }
@ -1186,7 +1185,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_xml_node(scope).get_attr(as_long()) : kind == ATTR_ID ? current_xml_node(scope).get_attr(as_long()) :
current_xml_node(scope).get_attr(as_string())) current_xml_node(scope).get_attr(as_string()))
result = value_t(*value, true); return value_t(*value, true);
else else
throw_(calc_error, "Attribute '" throw_(calc_error, "Attribute '"
<< (kind == ATTR_ID ? << (kind == ATTR_ID ?
@ -1232,30 +1231,9 @@ value_t xpath_t::op_t::calc(scope_t& scope)
case O_COMMA: case O_COMMA:
case O_UNION: { case O_UNION: {
optional<predicate_scope_t&> pred_scope(PREDICATE_SCOPE(scope)); value_t result(left()->calc(scope));
{
value_t temp(left()->calc(scope));
context_scope_t value_scope(scope, temp, 0, 1);
if (! pred_scope || ! pred_scope->predicate ||
pred_scope->predicate(value_scope))
result.push_back(temp);
}
std::size_t index = 0;
std::size_t size = 1;
ptr_op_t next = right(); ptr_op_t next = right();
while (next) {
size++;
if (next->kind == O_COMMA || next->kind == O_UNION)
next = next->right();
else
next = NULL;
}
next = right();
while (next) { while (next) {
ptr_op_t value_op; ptr_op_t value_op;
if (next->kind == O_COMMA || next->kind == O_UNION) { if (next->kind == O_COMMA || next->kind == O_UNION) {
@ -1266,18 +1244,9 @@ value_t xpath_t::op_t::calc(scope_t& scope)
next = NULL; next = NULL;
} }
value_t inner_temp(value_op->calc(scope)); result.push_back(value_op->calc(scope));
context_scope_t value_scope(scope, inner_temp, index, size);
if (! pred_scope || ! pred_scope->predicate ||
pred_scope->predicate(value_scope))
result.push_back(inner_temp);
index++;
} }
return result;
checked = true;
break;
} }
case LAST: case LAST:
@ -1286,69 +1255,7 @@ value_t xpath_t::op_t::calc(scope_t& scope)
break; break;
} }
if (! result.is_null() && (! checked || have_xml_nodes)) { return NULL_VALUE;
if (optional<scope_t&> sel_or_pred_scope =
scope.find_first_scope(scope_t::SELECTION_SCOPE,
scope_t::PREDICATE_SCOPE)) {
value_t new_result;
if (sel_or_pred_scope->type() == scope_t::SELECTION_SCOPE) {
selection_scope_t& sel_scope(downcast<selection_scope_t>(*sel_or_pred_scope));
if (! result.is_sequence()) {
context_scope_t node_scope(scope, result, 0, 1);
new_result.push_back(sel_scope.selection_path->calc(node_scope));
if (sel_scope.recurse && result.is_xml_node())
new_result.push_back(find_recursively(*result.as_xml_node(),
scope, sel_scope));
} else {
std::size_t index = 0;
std::size_t size = result.as_sequence().size();
foreach (const value_t& value, result.as_sequence()) {
context_scope_t node_scope(scope, value, index, size);
value_t item = sel_scope.selection_path->calc(node_scope);
new_result.push_back(item);
if (sel_scope.recurse && item.is_xml_node())
new_result.push_back(find_recursively(*item.as_xml_node(),
scope, sel_scope));
index++;
}
}
}
else {
predicate_scope_t& pred_scope(downcast<predicate_scope_t>(*sel_or_pred_scope));
if (pred_scope.predicate) {
if (! result.is_sequence()) {
context_scope_t value_scope(scope, result, 0, 1);
if (! pred_scope.predicate(value_scope))
new_result = NULL_VALUE;
else
new_result = result;
} else {
std::size_t index = 0;
std::size_t size = result.as_sequence().size();
foreach (const value_t& value, result.as_sequence()) {
context_scope_t value_scope(scope, value, index, size);
if (pred_scope.predicate(value_scope))
new_result.push_back(value);
index++;
}
}
}
}
result = new_result;
}
}
return result;
} }

View file

@ -68,9 +68,7 @@ public:
CHILD_SCOPE, CHILD_SCOPE,
SYMBOL_SCOPE, SYMBOL_SCOPE,
CALL_SCOPE, CALL_SCOPE,
CONTEXT_SCOPE, CONTEXT_SCOPE
SELECTION_SCOPE,
PREDICATE_SCOPE
} type_; } type_;
explicit scope_t(type_t _type) : type_(_type) { explicit scope_t(type_t _type) : type_(_type) {
@ -259,44 +257,6 @@ public:
} }
}; };
class selection_scope_t : public child_scope_t
{
public:
ptr_op_t selection_path;
bool recurse;
explicit selection_scope_t(scope_t& _parent,
const ptr_op_t& _selection_path = NULL,
const bool _recurse = false)
: child_scope_t(_parent, SELECTION_SCOPE),
selection_path(_selection_path), recurse(_recurse)
{
TRACE_CTOR(xpath_t::selection_scope_t,
"scope_t&, const ptr_op_t&, const bool");
}
virtual ~selection_scope_t() {
TRACE_DTOR(xpath_t::selection_scope_t);
}
};
typedef function<bool (scope_t&)> predicate_t;
class predicate_scope_t : public child_scope_t
{
public:
predicate_t predicate;
explicit predicate_scope_t(scope_t& _parent,
const predicate_t& _predicate = predicate_t())
: child_scope_t(_parent, PREDICATE_SCOPE), predicate(_predicate)
{
TRACE_CTOR(xpath_t::predicate_scope_t, "scope_t&, const predicate_t&");
}
virtual ~predicate_scope_t() {
TRACE_DTOR(xpath_t::predicate_scope_t);
}
};
#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
@ -663,8 +623,7 @@ public:
public: public:
explicit op_predicate(ptr_op_t _op) : op(_op) {} explicit op_predicate(ptr_op_t _op) : op(_op) {}
bool operator()(scope_t& scope) { bool operator()(scope_t& scope) {
predicate_scope_t null_predicate(scope); return op->calc(scope).to_boolean();
return op->calc(null_predicate).to_boolean();
} }
}; };
@ -895,30 +854,8 @@ xpath_t::scope_t::find_scope<xpath_t::context_scope_t>(bool skip_this) {
return downcast<context_scope_t>(*scope); return downcast<context_scope_t>(*scope);
} }
template<>
inline optional<xpath_t::selection_scope_t&>
xpath_t::scope_t::maybe_find_scope<xpath_t::selection_scope_t>(bool skip_this) {
optional<scope_t&> scope = find_scope(SELECTION_SCOPE, skip_this);
if (scope)
return downcast<selection_scope_t>(*scope);
else
return none;
}
template<>
inline optional<xpath_t::predicate_scope_t&>
xpath_t::scope_t::maybe_find_scope<xpath_t::predicate_scope_t>(bool skip_this) {
optional<scope_t&> scope = find_scope(PREDICATE_SCOPE, skip_this);
if (scope)
return downcast<predicate_scope_t>(*scope);
else
return none;
}
#define FIND_SCOPE(scope_type, scope_ref) \ #define FIND_SCOPE(scope_type, scope_ref) \
downcast<xml::xpath_t::scope_t>(scope_ref).find_scope<scope_type>() downcast<xml::xpath_t::scope_t>(scope_ref).find_scope<scope_type>()
#define MAYBE_FIND_SCOPE(scope_type, scope_ref) \
downcast<xml::xpath_t::scope_t>(scope_ref).maybe_find_scope<scope_type>()
#define CALL_SCOPE(scope_ref) \ #define CALL_SCOPE(scope_ref) \
FIND_SCOPE(xml::xpath_t::call_scope_t, scope_ref) FIND_SCOPE(xml::xpath_t::call_scope_t, scope_ref)
@ -926,10 +863,6 @@ xpath_t::scope_t::maybe_find_scope<xpath_t::predicate_scope_t>(bool skip_this) {
FIND_SCOPE(xml::xpath_t::symbol_scope_t, scope_ref) FIND_SCOPE(xml::xpath_t::symbol_scope_t, scope_ref)
#define CONTEXT_SCOPE(scope_ref) \ #define CONTEXT_SCOPE(scope_ref) \
FIND_SCOPE(xml::xpath_t::context_scope_t, scope_ref) FIND_SCOPE(xml::xpath_t::context_scope_t, scope_ref)
#define SELECTION_SCOPE(scope_ref) \
MAYBE_FIND_SCOPE(xml::xpath_t::selection_scope_t, scope_ref)
#define PREDICATE_SCOPE(scope_ref) \
MAYBE_FIND_SCOPE(xml::xpath_t::predicate_scope_t, scope_ref)
} // namespace xml } // namespace xml