[augeas-devel] augeas: master - Add FilterExpr to path expression grammar
David Lutterkort
lutter at fedoraproject.org
Tue Mar 24 23:07:49 UTC 2009
Gitweb: http://git.fedorahosted.org/git/augeas.git?p=augeas.git;a=commitdiff;h=f4a39e7c2bac8fbb03fe4842a5d59cf7b3b5033c
Commit: f4a39e7c2bac8fbb03fe4842a5d59cf7b3b5033c
Parent: 378e75149d1bd5157ad7a7fff5aebb33dcd0ba65
Author: David Lutterkort <lutter at redhat.com>
AuthorDate: Sat Mar 21 22:40:01 2009 -0700
Committer: David Lutterkort <lutter at redhat.com>
CommitterDate: Sun Mar 22 22:54:40 2009 -0700
Add FilterExpr to path expression grammar
This allows using constructs like $foo/some/path and $foo[2]
---
doc/xpath.txt | 8 +++-
src/pathx.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++----
tests/test-xpath.c | 1 +
tests/xpath.tests | 22 +++++++++++
4 files changed, 128 insertions(+), 10 deletions(-)
diff --git a/doc/xpath.txt b/doc/xpath.txt
index 91381a6..b0b6a81 100644
--- a/doc/xpath.txt
+++ b/doc/xpath.txt
@@ -105,7 +105,13 @@ AxisName ::= 'ancestor'
NameTest ::= '*' | Name
Predicate ::= "[" Expr "]" *
-PathExpr ::= LocationPath | PrimaryExpr
+PathExpr ::= LocationPath
+ | FilterExpr
+ | FilterExpr '/' RelativeLocationPath
+ | FilterExpr '//' RelativeLocationPath
+
+FilterExpr ::= PrimaryExpr Predicate
+
PrimaryExpr ::= Literal
| Number
| FunctionCall
diff --git a/src/pathx.c b/src/pathx.c
index b4da423..83ff956 100644
--- a/src/pathx.c
+++ b/src/pathx.c
@@ -157,7 +157,11 @@ struct expr {
enum expr_tag tag;
enum type type;
union {
- struct locpath *locpath; /* E_FILTER */
+ struct { /* E_FILTER */
+ struct expr *primary;
+ struct pred *predicates;
+ struct locpath *locpath;
+ };
struct { /* E_BINARY */
enum binary_op op;
struct expr *left;
@@ -326,6 +330,8 @@ static void free_expr(struct expr *expr) {
return;
switch (expr->tag) {
case E_FILTER:
+ free_expr(expr->primary);
+ free_pred(expr->predicates);
free_locpath(expr->locpath);
break;
case E_BINARY:
@@ -803,6 +809,7 @@ static void ns_filter(struct nodeset *ns, struct pred *predicates,
*/
static void ns_from_locpath(struct locpath *lp, uint *maxns,
struct nodeset ***ns,
+ const struct nodeset *root,
struct state *state) {
struct tree *old_ctx = state->ctx;
@@ -819,7 +826,13 @@ static void ns_from_locpath(struct locpath *lp, uint *maxns,
if (HAS_ERROR(state))
goto error;
}
- ns_add((*ns)[0], state->ctx, state);
+ if (root == NULL) {
+ ns_add((*ns)[0], state->ctx, state);
+ } else {
+ for (int i=0; i < root->used; i++)
+ ns_add((*ns)[0], root->nodes[i], state);
+ }
+ CHECK_ERROR;
uint cur_ns = 0;
list_for_each(step, lp->steps) {
@@ -854,7 +867,16 @@ static void eval_filter(struct expr *expr, struct state *state) {
uint maxns;
state->locpath_trace = NULL;
- ns_from_locpath(lp, &maxns, &ns, state);
+ if (expr->primary == NULL) {
+ ns_from_locpath(lp, &maxns, &ns, NULL, state);
+ } else {
+ eval_expr(expr->primary, state);
+ CHECK_ERROR;
+ struct value *primary = pop_value(state);
+ assert(primary->tag == T_NODESET);
+ ns_filter(primary->nodeset, expr->predicates, state);
+ ns_from_locpath(lp, &maxns, &ns, primary->nodeset, state);
+ }
CHECK_ERROR;
value_ind_t vind = make_value(T_NODESET, state);
@@ -910,6 +932,8 @@ static void check_expr(struct expr *expr, struct state *state);
* T_BOOLEAN -> T_BOOLEAN
*/
static void check_preds(struct pred *pred, struct state *state) {
+ if (pred == NULL)
+ return;
for (int i=0; i < pred->nexpr; i++) {
struct expr *e = pred->exprs[i];
check_expr(e, state);
@@ -924,13 +948,20 @@ static void check_preds(struct pred *pred, struct state *state) {
static void check_filter(struct expr *expr, struct state *state) {
assert(expr->tag == E_FILTER);
-
struct locpath *locpath = expr->locpath;
- list_for_each(s, locpath->steps) {
- if (s->predicates != NULL) {
- check_preds(s->predicates, state);
- CHECK_ERROR;
+
+ if (expr->primary != NULL) {
+ check_expr(expr->primary, state);
+ if (expr->primary->type != T_NODESET) {
+ STATE_ERROR(state, PATHX_ETYPE);
+ return;
}
+ check_preds(expr->predicates, state);
+ CHECK_ERROR;
+ }
+ list_for_each(s, locpath->steps) {
+ check_preds(s->predicates, state);
+ CHECK_ERROR;
}
expr->type = T_NODESET;
}
@@ -1538,7 +1569,12 @@ static int looking_at_primary_expr(struct state *state) {
}
/*
- * PathExpr ::= LocationPath | PrimaryExpr
+ * PathExpr ::= LocationPath
+ * | FilterExpr
+ * | FilterExpr '/' RelativeLocationPath
+ * | FilterExpr '//' RelativeLocationPath
+ *
+ * FilterExpr ::= PrimaryExpr Predicate
*
* The grammar is ambiguous here: the expression '42' can either be the
* number 42 (a PrimaryExpr) or the RelativeLocationPath 'child::42'. The
@@ -1548,11 +1584,64 @@ static int looking_at_primary_expr(struct state *state) {
* RelativeLocationPath in a different form, e.g. 'child::42' or './42'.
*/
static void parse_path_expr(struct state *state) {
+ struct expr *expr = NULL;
+ struct pred *predicates = NULL;
+ struct locpath *locpath = NULL;
+
if (looking_at_primary_expr(state)) {
parse_primary_expr(state);
+ CHECK_ERROR;
+ predicates = parse_predicates(state);
+ CHECK_ERROR;
+ if (match(state, '/')) {
+ if (match(state, '/')) {
+ locpath = parse_relative_location_path(state);
+ if (HAS_ERROR(state))
+ goto error;
+
+ struct step *step = make_step(DESCENDANT_OR_SELF, state);
+ if (HAS_ERROR(state))
+ return;
+ list_cons(locpath->steps, step);
+ } else {
+ if (*state->pos == '\0') {
+ STATE_ERROR(state, PATHX_EEND);
+ goto error;
+ }
+ locpath = parse_relative_location_path(state);
+ }
+ }
+ /* A PathExpr without predicates and locpath is
+ * just a PrimaryExpr
+ */
+ if (predicates == NULL && locpath == NULL)
+ return;
+ /* To make evaluation easier, we parse something like
+ * $var[pred] as $var[pred]/.
+ */
+ if (locpath == NULL) {
+ if (ALLOC(locpath) < 0)
+ goto error;
+ if (ALLOC(locpath->steps) < 0)
+ goto error;
+ locpath->steps->axis = SELF;
+ }
+ if (ALLOC(expr) < 0)
+ goto error;
+ expr->tag = E_FILTER;
+ expr->predicates = predicates;
+ expr->primary = pop_expr(state);
+ expr->locpath = locpath;
+ push_expr(expr, state);
} else {
parse_location_path(state);
}
+ return;
+ error:
+ free_expr(expr);
+ free_pred(predicates);
+ free_locpath(locpath);
+ return;
}
/*
diff --git a/tests/test-xpath.c b/tests/test-xpath.c
index 391f9f8..444983e 100644
--- a/tests/test-xpath.c
+++ b/tests/test-xpath.c
@@ -205,6 +205,7 @@ static int run_tests(struct test *tests) {
aug = aug_init(root, lensdir, AUG_NO_STDINC|AUG_SAVE_NEWFILE);
if (aug == NULL)
die("aug_init");
+ aug_pathvar(aug, "hosts", "/files/etc/hosts/*");
list_for_each(t, tests) {
if (run_one_test(aug, t) < 0)
result = EXIT_FAILURE;
diff --git a/tests/xpath.tests b/tests/xpath.tests
index 70412d2..ab06851 100644
--- a/tests/xpath.tests
+++ b/tests/xpath.tests
@@ -14,28 +14,47 @@
# The MATCH XPath expression is matched against a fixed tree (the one from the
# root/ subdirectory) and the result of the aug_match is compared with the
# results listed in the test
+#
+# The test framework sets up variables:
+# hosts /files/etc/hosts/*
# Very simple to warm up
test wildcard /files/etc/hosts/*/ipaddr
/files/etc/hosts/1/ipaddr = 127.0.0.1
/files/etc/hosts/2/ipaddr = 172.31.122.14
+test wildcard-var $hosts/ipaddr
+ /files/etc/hosts/1/ipaddr = 127.0.0.1
+ /files/etc/hosts/2/ipaddr = 172.31.122.14
+
# Compare the value of the current node with a constant
test self-value /files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]
/files/etc/hosts/1/ipaddr = 127.0.0.1
+test self-value-var $hosts/ipaddr[ . = '127.0.0.1' ]
+ /files/etc/hosts/1/ipaddr = 127.0.0.1
+
# Find nodes that have a child named 'ipaddr' with a fixed value
test child-value /files/etc/hosts/*[ipaddr = '127.0.0.1']
/files/etc/hosts/1
+test child-value-var $hosts[ipaddr = '127.0.0.1']
+ /files/etc/hosts/1
+
# Find nodes that have a child 'ipaddr' that has no value
test child-nil-value /files/etc/hosts/*[ipaddr = '']
+test child-nil-value-var $hosts[ipaddr = '']
+
# Find nodes that have no value
test self-nil-value /files/etc/hosts/*[. = '']
/files/etc/hosts/1
/files/etc/hosts/2
+test self-nil-value-var $hosts[. = '']
+ /files/etc/hosts/1
+ /files/etc/hosts/2
+
# Match over two levels of the tree
test two-wildcards /files/etc/*/*[ipaddr='127.0.0.1']
/files/etc/hosts/1
@@ -67,6 +86,9 @@ test pam-two-preds-control /files/etc/pam.d/*/*[module = 'system-auth'][type = '
test last /files/etc/hosts/*[ipaddr = "127.0.0.1"]/alias[last()]
/files/etc/hosts/1/alias[3] = galia
+test last-var $hosts[ipaddr = "127.0.0.1"]/alias[last()]
+ /files/etc/hosts/1/alias[3] = galia
+
# We can get nodes counting from the right with 'last()-N'
test last-minus-one /files/etc/hosts/*[ipaddr = "127.0.0.1"]/alias[ last() - 1 ]
/files/etc/hosts/1/alias[2] = galia.watzmann.net
More information about the augeas-devel
mailing list