Revised xpath_t::path_t

This commit is contained in:
John Wiegley 2007-05-16 05:38:19 +00:00
parent e309cac9c1
commit 74ecceb2ba
4 changed files with 74 additions and 139 deletions

View file

@ -75,6 +75,7 @@ class python_interpreter_t : public xml::xpath_t::scope_t
boost::python::object func; boost::python::object func;
public: public:
functor_t(const string& name, boost::python::object _func) : func(_func) {} functor_t(const string& name, boost::python::object _func) : func(_func) {}
virtual ~functor_t() {}
virtual value_t operator()(xml::xpath_t::scope_t * locals); virtual value_t operator()(xml::xpath_t::scope_t * locals);
}; };

View file

@ -689,7 +689,7 @@ xpath_t::parse_path_expr(std::istream& in, flags_t tflags) const
if (node) { if (node) {
token_t& tok = next_token(in, tflags); token_t& tok = next_token(in, tflags);
while (tok.kind == token_t::SLASH) { if (tok.kind == token_t::SLASH) {
ptr_op_t prev(node); ptr_op_t prev(node);
tok = next_token(in, tflags); tok = next_token(in, tflags);
@ -699,14 +699,12 @@ xpath_t::parse_path_expr(std::istream& in, flags_t tflags) const
push_token(tok); push_token(tok);
node->set_left(prev); node->set_left(prev);
node->set_right(parse_predicate_expr(in, tflags)); node->set_right(parse_path_expr(in, tflags));
if (! node->right()) if (! node->right())
throw_(parse_error, "/ operator not followed by a valid term"); throw_(parse_error, "/ operator not followed by a valid term");
} else {
tok = next_token(in, tflags); push_token(tok);
} }
push_token(tok);
} }
return node; return node;
@ -2117,101 +2115,40 @@ void xpath_t::op_t::dump(std::ostream& out, const int depth) const
} }
} }
xpath_t::path_t::path_t(const xpath_t& path_expr) void xpath_t::path_t::check_element(node_t& start,
const ptr_op_t& element,
scope_t * scope,
std::size_t index,
std::size_t size,
const visitor_t& func)
{ {
ptr_op_t op = path_expr.ptr; if (element->kind > op_t::TERMINALS &&
element->left()->kind == op_t::O_PRED) {
while (true) {
element_t element;
switch (op->kind) {
case op_t::O_RFIND:
element.recurse = true;
// fall through...
case op_t::O_FIND: {
ptr_op_t name;
if (op->right()->kind == op_t::O_PRED) {
element.predicate = op_predicate(op->right()->right());
name = op->right()->left();
} else {
name = op->right();
}
switch (name->kind) {
case op_t::NODE_ID: {
//case op_t::ATTR_ID:
node_t::nameid_t name_id = name->as_name();
if (name_id < document_t::LAST_BUILTIN)
element.ident = document_t::special_names_t(name_id);
else
element.ident = name_id;
break;
}
case op_t::NODE_NAME:
//case op_t::ATTR_NAME:
element.ident = name->as_string();
break;
default:
break;
}
break;
}
case op_t::NODE_ID: {
//case op_t::ATTR_ID:
node_t::nameid_t name_id = op->as_name();
if (name_id < document_t::LAST_BUILTIN)
element.ident = document_t::special_names_t(name_id);
else
element.ident = name_id;
break;
}
case op_t::NODE_NAME:
//case op_t::ATTR_NAME:
element.ident = op->as_string();
break;
default:
throw_(std::logic_error, "XPath expression is not strictly a path selection");
break;
}
elements.push_front(element);
if (op->kind < op_t::TERMINALS)
break;
else
op = op->left();
}
}
void xpath_t::path_t::check_element(node_t& start,
const element_iterator& element,
scope_t * scope,
std::size_t index,
std::size_t size,
const visitor_t& func)
{
if (element->predicate) {
function_scope_t xpath_fscope(start, index, size, scope); function_scope_t xpath_fscope(start, index, size, scope);
if (! element->predicate(start, &xpath_fscope)) if (! op_predicate(element->left()->right())(start, &xpath_fscope))
return; return;
} }
element_iterator next_element = next(element); if (element->kind < op_t::TERMINALS)
if (next_element == elements.end())
func(start); func(start);
else else
walk_elements(start, next_element, scope, func); walk_elements(start, element->right(), element->kind == op_t::O_RFIND,
scope, func);
} }
void xpath_t::path_t::walk_elements(node_t& start, void xpath_t::path_t::walk_elements(node_t& start,
const element_iterator& element, const ptr_op_t& element,
scope_t * scope, const bool recurse,
const visitor_t& func) scope_t * scope,
const visitor_t& func)
{ {
if (element->ident.type() == typeid(document_t::special_names_t)) { ptr_op_t name(element->kind < op_t::TERMINALS ? element : element->left());
switch (boost::get<document_t::special_names_t>(element->ident)) { if (name->kind == op_t::O_PRED)
name = name->left();
switch (name->kind) {
case op_t::NODE_ID:
switch (name->as_name()) {
case document_t::CURRENT: case document_t::CURRENT:
check_element(start, element, scope, 0, 1, func); check_element(start, element, scope, 0, 1, func);
break; break;
@ -2239,24 +2176,32 @@ void xpath_t::path_t::walk_elements(node_t& start,
} }
default: default:
assert(false); break; // pass down to the NODE_NAME case
break;
} }
} // fall through...
else if (start.is_parent_node()) {
bool have_name_id = element->ident.type() == typeid(node_t::nameid_t);
std::size_t index = 0; case op_t::NODE_NAME:
std::size_t size = start.as_parent_node().size(); if (start.is_parent_node()) {
foreach (node_t * child, start.as_parent_node()) { bool have_name_id = name->kind == op_t::NODE_ID;
if ((have_name_id &&
boost::get<node_t::nameid_t>(element->ident) == child->name_id()) || std::size_t index = 0;
(! have_name_id && std::size_t size = start.as_parent_node().size();
boost::get<string>(element->ident) == child->name())) foreach (node_t * child, start.as_parent_node()) {
check_element(*child, element, scope, index++, size, func); if ((have_name_id && name->as_name() == child->name_id()) ||
else if (element->recurse) (! have_name_id && name->as_string() == child->name()))
walk_elements(*child, element, scope, func); check_element(*child, element, scope, index++, size, func);
else if (recurse)
walk_elements(*child, element, recurse, scope, func);
}
} }
break;
default:
// jww (2007-05-15): Instead of a name, this might be an
// expression resulting in a nodelist with respect to the current
// context.
throw_(std::logic_error, "XPath expression is not strictly a path selection");
break;
} }
} }

View file

@ -219,19 +219,6 @@ public:
typedef function<void (node_t&)> visitor_t; typedef function<void (node_t&)> visitor_t;
typedef function<bool (node_t&, scope_t *)> predicate_t; typedef function<bool (node_t&, scope_t *)> predicate_t;
struct element_t
{
variant<node_t::nameid_t, string,
document_t::special_names_t> ident;
bool recurse;
predicate_t predicate;
};
std::list<element_t> elements;
typedef std::list<element_t>::const_iterator element_iterator;
struct value_node_appender_t { struct value_node_appender_t {
value_t::sequence_t& sequence; value_t::sequence_t& sequence;
value_node_appender_t(value_t::sequence_t& _sequence) value_node_appender_t(value_t::sequence_t& _sequence)
@ -241,34 +228,36 @@ public:
} }
}; };
ptr_op_t path_expr;
public: public:
path_t(const xpath_t& path_expr); path_t(const xpath_t& xpath) : path_expr(xpath.ptr) {}
value_t find_all(node_t& start, scope_t * scope) { value_t find_all(node_t& start, scope_t * scope) {
value_t result = value_t::sequence_t(); value_t result = value_t::sequence_t();
visit(start, scope, visit(start, scope, value_node_appender_t(result.as_sequence_lval()));
value_node_appender_t(result.as_sequence_lval()));
return result; return result;
} }
void visit(node_t& start, scope_t * scope, void visit(node_t& start, scope_t * scope,
const function<void (node_t&)>& func) { const function<void (node_t&)>& func) {
if (elements.begin() != elements.end()) if (path_expr)
walk_elements(start, elements.begin(), scope, func); walk_elements(start, path_expr, false, scope, func);
} }
private: private:
void walk_elements(node_t& start, void walk_elements(node_t& start,
const element_iterator& element, const ptr_op_t& element,
scope_t * scope, const bool recurse,
const visitor_t& func); scope_t * scope,
const visitor_t& func);
void check_element(node_t& start, void check_element(node_t& start,
const element_iterator& element, const ptr_op_t& element,
scope_t * scope, scope_t * scope,
std::size_t index, std::size_t index,
std::size_t size, std::size_t size,
const visitor_t& func); const visitor_t& func);
}; };
class path_iterator_t class path_iterator_t

View file

@ -357,10 +357,10 @@ void AmountTestCase::testCommodityEquality()
assertTrue(x0.is_null()); assertTrue(x0.is_null());
assertThrow(x0.is_zero(), amount_error); assertThrow(x0.is_zero(), amount_error);
assertThrow(x0.is_realzero(), amount_error); assertThrow(x0.is_realzero(), amount_error);
assertThrow(x0.sign() == 0, amount_error); assertThrow(assert(x0.sign() == 0), amount_error);
assertThrow(x0.compare(x1) < 0, amount_error); assertThrow(assert(x0.compare(x1) < 0), amount_error);
assertThrow(x0.compare(x2) > 0, amount_error); assertThrow(assert(x0.compare(x2) > 0), amount_error);
assertThrow(x0.compare(x0) == 0, amount_error); assertThrow(assert(x0.compare(x0) == 0), amount_error);
assertTrue(x1 != x2); assertTrue(x1 != x2);
assertTrue(x1 != x4); assertTrue(x1 != x4);
@ -1311,7 +1311,7 @@ void AmountTestCase::testTruth()
amount_t x1("1234"); amount_t x1("1234");
amount_t x2("1234.56"); amount_t x2("1234.56");
assertThrow(x0 ? 1 : 0, amount_error); assertThrow(assert(x0 ? 1 : 0), amount_error);
assertTrue(x1); assertTrue(x1);
assertTrue(x2); assertTrue(x2);