Started working on an XPath visitor class
This commit is contained in:
parent
9e55655e0c
commit
3244c693f8
4 changed files with 218 additions and 30 deletions
22
src/main.cc
22
src/main.cc
|
|
@ -46,6 +46,10 @@
|
||||||
#include <fdstream.hpp>
|
#include <fdstream.hpp>
|
||||||
#endif
|
#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[],
|
static int read_and_report(ledger::report_t * report, int argc, char * argv[],
|
||||||
char * envp[])
|
char * envp[])
|
||||||
{
|
{
|
||||||
|
|
@ -269,12 +273,28 @@ static int read_and_report(ledger::report_t * report, int argc, char * argv[],
|
||||||
xpath.print(*out, xml_document);
|
xpath.print(*out, xml_document);
|
||||||
*out << std::endl;
|
*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;
|
value_t nodelist;
|
||||||
xpath.calc(nodelist, xml_document, report);
|
xpath.calc(nodelist, xml_document, report);
|
||||||
|
|
||||||
foreach (const value_t& node, nodelist.as_sequence())
|
foreach (const value_t& node, nodelist.as_sequence())
|
||||||
node.as_xml_node()->print(*out);
|
node.as_xml_node()->print(*out);
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1440,9 +1440,12 @@ value_t value_t::strip_annotations(const bool keep_price,
|
||||||
case POINTER:
|
case POINTER:
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
case SEQUENCE:
|
case SEQUENCE: {
|
||||||
assert(false); // jww (2006-09-28): strip them all!
|
sequence_t temp;
|
||||||
break;
|
foreach (const value_t& value, as_sequence())
|
||||||
|
temp.push_back(value.strip_annotations(keep_price, keep_date, keep_tag));
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
case AMOUNT:
|
case AMOUNT:
|
||||||
return as_amount().strip_annotations
|
return as_amount().strip_annotations
|
||||||
|
|
|
||||||
138
src/xpath.cc
138
src/xpath.cc
|
|
@ -699,7 +699,7 @@ xpath_t::parse_path_expr(std::istream& in, flags_t tflags) const
|
||||||
while (tok.kind == token_t::SLASH) {
|
while (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);
|
||||||
node = new op_t(tok.kind == token_t::SLASH ?
|
node = new op_t(tok.kind == token_t::SLASH ?
|
||||||
op_t::O_RFIND : op_t::O_FIND);
|
op_t::O_RFIND : op_t::O_FIND);
|
||||||
if (tok.kind != token_t::SLASH)
|
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);
|
result_seq.push_back(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
xpath_t::ptr_op_t xpath_t::op_t::compile(value_t& context, scope_t * scope,
|
xpath_t::ptr_op_t
|
||||||
bool resolve)
|
xpath_t::op_t::compile(value_t& context, scope_t * scope, bool resolve)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
try {
|
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 "
|
throw_(compile_error, "Attempting to apply path selection "
|
||||||
"to non-node(s)");
|
"to non-node(s)");
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrap_value(result_seq);
|
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();
|
out << "FUNCTION - " << as_function();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_NOT: out << "O_NOT"; break;
|
case O_NOT: out << "O_NOT"; break;
|
||||||
case O_NEG: out << "O_NEG"; 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_ADD: out << "O_ADD"; break;
|
||||||
case O_SUB: out << "O_SUB"; break;
|
case O_SUB: out << "O_SUB"; break;
|
||||||
case O_MUL: out << "O_MUL"; break;
|
case O_MUL: out << "O_MUL"; break;
|
||||||
case O_DIV: out << "O_DIV"; break;
|
case O_DIV: out << "O_DIV"; break;
|
||||||
|
|
||||||
case O_NEQ: out << "O_NEQ"; break;
|
case O_NEQ: out << "O_NEQ"; break;
|
||||||
case O_EQ: out << "O_EQ"; break;
|
case O_EQ: out << "O_EQ"; break;
|
||||||
case O_LT: out << "O_LT"; break;
|
case O_LT: out << "O_LT"; break;
|
||||||
case O_LTE: out << "O_LTE"; break;
|
case O_LTE: out << "O_LTE"; break;
|
||||||
case O_GT: out << "O_GT"; break;
|
case O_GT: out << "O_GT"; break;
|
||||||
case O_GTE: out << "O_GTE"; break;
|
case O_GTE: out << "O_GTE"; break;
|
||||||
|
|
||||||
case O_AND: out << "O_AND"; break;
|
case O_AND: out << "O_AND"; break;
|
||||||
case O_OR: out << "O_OR"; break;
|
case O_OR: out << "O_OR"; break;
|
||||||
|
|
||||||
case O_QUES: out << "O_QUES"; break;
|
case O_QUES: out << "O_QUES"; break;
|
||||||
case O_COLON: out << "O_COLON"; 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_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_FIND: out << "O_FIND"; break;
|
||||||
case O_RFIND: out << "O_RFIND"; break;
|
case O_RFIND: out << "O_RFIND"; break;
|
||||||
case O_PRED: out << "O_PRED"; break;
|
case O_PRED: out << "O_PRED"; break;
|
||||||
|
|
||||||
case LAST:
|
case LAST:
|
||||||
default:
|
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 xml
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
79
src/xpath.h
79
src/xpath.h
|
|
@ -212,6 +212,55 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
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
|
struct op_t : public noncopyable
|
||||||
{
|
{
|
||||||
enum kind_t {
|
enum kind_t {
|
||||||
|
|
@ -366,6 +415,22 @@ public:
|
||||||
data = val;
|
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() {
|
ptr_op_t& as_op() {
|
||||||
assert(kind > TERMINALS);
|
assert(kind > TERMINALS);
|
||||||
return boost::get<ptr_op_t>(data);
|
return boost::get<ptr_op_t>(data);
|
||||||
|
|
@ -437,6 +502,20 @@ public:
|
||||||
void dump(std::ostream& out, const int depth) const;
|
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:
|
public:
|
||||||
ptr_op_t ptr;
|
ptr_op_t ptr;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue