Started working on an XPath visitor class

This commit is contained in:
John Wiegley 2007-05-15 05:43:46 +00:00
parent 9e55655e0c
commit 3244c693f8
4 changed files with 218 additions and 30 deletions

View file

@ -46,6 +46,10 @@
#include <fdstream.hpp>
#endif
void print_node(ledger::xml::node_t& node) {
node.print(std::cout);
}
static int read_and_report(ledger::report_t * report, int argc, char * argv[],
char * envp[])
{
@ -269,12 +273,28 @@ static int read_and_report(ledger::report_t * report, int argc, char * argv[],
xpath.print(*out, xml_document);
*out << std::endl;
#if 1
try {
xml::xpath_t::path_t path_selection(xpath);
xml::xpath_t::path_t::element_t elem;
elem.ident = xml::document_t::ROOT;
path_selection.elements.push_back(elem);
elem.ident = xml::TRANSACTION_NODE;
elem.recurse = true;
path_selection.elements.push_back(elem);
path_selection.visit(xml_document, report, bind(print_node, _1));
}
catch (...) {
throw;
}
#else
value_t nodelist;
xpath.calc(nodelist, xml_document, report);
foreach (const value_t& node, nodelist.as_sequence())
node.as_xml_node()->print(*out);
#endif
return 0;
}

View file

@ -1440,9 +1440,12 @@ value_t value_t::strip_annotations(const bool keep_price,
case POINTER:
return *this;
case SEQUENCE:
assert(false); // jww (2006-09-28): strip them all!
break;
case SEQUENCE: {
sequence_t temp;
foreach (const value_t& value, as_sequence())
temp.push_back(value.strip_annotations(keep_price, keep_date, keep_tag));
return temp;
}
case AMOUNT:
return as_amount().strip_annotations

View file

@ -699,7 +699,7 @@ xpath_t::parse_path_expr(std::istream& in, flags_t tflags) const
while (tok.kind == token_t::SLASH) {
ptr_op_t prev(node);
tok = next_token(in, tflags);
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)
@ -1143,8 +1143,8 @@ void xpath_t::op_t::append_value(value_t& val,
result_seq.push_back(val);
}
xpath_t::ptr_op_t xpath_t::op_t::compile(value_t& context, scope_t * scope,
bool resolve)
xpath_t::ptr_op_t
xpath_t::op_t::compile(value_t& context, scope_t * scope, bool resolve)
{
#if 0
try {
@ -1661,7 +1661,6 @@ xpath_t::ptr_op_t xpath_t::op_t::compile(value_t& context, scope_t * scope,
throw_(compile_error, "Attempting to apply path selection "
"to non-node(s)");
}
return wrap_value(result_seq);
}
@ -2103,37 +2102,37 @@ void xpath_t::op_t::dump(std::ostream& out, const int depth) const
out << "FUNCTION - " << as_function();
break;
case O_NOT: out << "O_NOT"; break;
case O_NEG: out << "O_NEG"; break;
case O_NOT: out << "O_NOT"; break;
case O_NEG: out << "O_NEG"; break;
case O_UNION: out << "O_UNION"; 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_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_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_AND: out << "O_AND"; break;
case O_OR: out << "O_OR"; break;
case O_QUES: out << "O_QUES"; break;
case O_COLON: out << "O_COLON"; break;
case O_QUES: out << "O_QUES"; break;
case O_COLON: out << "O_COLON"; break;
case O_COMMA: out << "O_COMMA"; break;
case O_COMMA: out << "O_COMMA"; break;
case O_DEFINE: out << "O_DEFINE"; break;
case O_EVAL: out << "O_EVAL"; break;
case O_EVAL: out << "O_EVAL"; break;
case O_FIND: out << "O_FIND"; break;
case O_RFIND: out << "O_RFIND"; break;
case O_PRED: out << "O_PRED"; 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:
@ -2156,5 +2155,92 @@ void xpath_t::op_t::dump(std::ostream& out, const int depth) const
}
}
xpath_t::path_t::path_t(const xpath_t& path_expr)
{
ptr_op_t op = path_expr.ptr;
while (true) {
switch (op->kind) {
case op_t::O_FIND:
case op_t::O_RFIND:
case op_t::O_PRED:
break;
case op_t::NODE_ID:
case op_t::NODE_NAME:
case op_t::ATTR_ID:
case op_t::ATTR_NAME:
break;
default:
throw_(std::logic_error, "XPath expression is not strictly a path selection");
break;
}
break;
}
}
void xpath_t::path_t::check_element(node_t& start,
const element_iterator& element,
scope_t * scope,
const visitor_t& func)
{
if (! element->predicate || element->predicate(start, scope)) {
element_iterator next_element = next(element);
if (next_element == elements.end())
func(start);
else
walk_elements(start, next_element, scope, func);
}
}
void xpath_t::path_t::walk_elements(node_t& start,
const element_iterator& element,
scope_t * scope,
const visitor_t& func)
{
if (element->ident.type() == typeid(document_t::special_names_t)) {
switch (boost::get<document_t::special_names_t>(element->ident)) {
case document_t::CURRENT:
check_element(start, element, scope, func);
break;
case document_t::PARENT:
if (optional<parent_node_t&> parent = start.parent())
check_element(*parent, element, scope, func);
else
throw_(std::logic_error, "Attempt to access parent of root node");
break;
case document_t::ROOT:
check_element(start.document(), element, scope, func);
break;
case document_t::ALL:
if (! start.is_parent_node())
throw_(compile_error, "Referencing child nodes from a non-parent value");
foreach (node_t * node, start.as_parent_node())
check_element(*node, element, scope, func);
break;
}
}
else if (start.is_parent_node()) {
bool have_name_id = element->ident.type() == typeid(node_t::nameid_t);
foreach (node_t * child, start.as_parent_node()) {
if ((have_name_id &&
boost::get<node_t::nameid_t>(element->ident) == child->name_id()) ||
(! have_name_id &&
boost::get<string>(element->ident) == child->name()))
check_element(*child, element, scope, func);
}
}
if (element->recurse && start.is_parent_node())
foreach (node_t * child, start.as_parent_node())
walk_elements(*child, element, scope, func);
}
} // namespace xml
} // namespace ledger

View file

@ -212,6 +212,55 @@ private:
};
public:
class path_t : public noncopyable
{
public: // jww (2007-05-14): for testing
typedef function<void (node_t&)> visitor_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 node_appender_t {
value_t::sequence_t& sequence;
node_appender_t(value_t::sequence_t& _sequence)
: sequence(_sequence) {}
void operator()(node_t& node) {
sequence.push_back(&node);
}
};
public:
path_t(const xpath_t& path_expr);
void find_all(value_t::sequence_t& result,
node_t& start, scope_t * scope) {
visit(start, scope, node_appender_t(result));
}
void visit(node_t& start, scope_t * scope,
const function<void (node_t&)>& func) {
if (elements.begin() != elements.end())
walk_elements(start, elements.begin(), scope, func);
}
private:
void walk_elements(node_t& start, const element_iterator& element,
scope_t * scope, const function<void (node_t&)>& func);
void check_element(node_t& start, const element_iterator& element,
scope_t * scope, const function<void (node_t&)>& func);
};
struct op_t : public noncopyable
{
enum kind_t {
@ -366,6 +415,22 @@ public:
data = val;
}
#if 0
bool is_path() const {
return kind == PATH;
}
path_t& as_path() {
assert(kind == PATH);
return boost::get<path_t>(data);
}
const path_t& as_path() const {
return const_cast<op_t *>(this)->as_path();
}
void set_path(const path_t& val) {
data = val;
}
#endif
ptr_op_t& as_op() {
assert(kind > TERMINALS);
return boost::get<ptr_op_t>(data);
@ -437,6 +502,20 @@ public:
void dump(std::ostream& out, const int depth) const;
};
class op_predicate
{
ptr_op_t op;
public:
op_predicate(ptr_op_t _op) : op(_op) {}
bool operator()(node_t& node, scope_t * scope) {
value_t context_node(&node);
xpath_t result(op->compile(context_node, scope, true));
return result.ptr->as_value().to_boolean();
}
};
public:
ptr_op_t ptr;