Hi,

To avoid put ambiguity in put direction for similar node, I tried to prefix a key with a label. In XML lens, element names and attributes have the same keys, so we have to put attributes in a store to avoid put ambiguity, and it produce a less pretty tree structure. Here is an example of a put ambiguity.

module Put_ambig =

let dir = [ key /[a]+/ . del "x" "x" ]
let sec = [ key /[a]+/ . del "y" "y" ]
let foo = (dir|sec)*
test foo get "axaay" = ?

$ augparse -I lenses/ --nostdinc put_ambig.aug
Syntax error in lens definition
put_ambig.aug:5.0-.20:Failed to compile foo
put_ambig.aug:5.10-.18:exception: overlapping lenses in tree union.put
    Example matched by both:  { "a" }
    First lens: put_ambig.aug:3.10-.38:
    Second lens: put_ambig.aug:4.10-.38:

Here is the actual way to fix this ambiguity, but moving keys into a store.

module Put_unambig =

let dir = [ label "dir" . store /[a]+/ . del "x" "x" ]
let sec = [ label "sec" . store /[a]+/ . del "y" "y" ]
let foo = (dir|sec)*
test foo get "axaay" = ?
test foo put "ax" after set "/sec" "aa" = ?

$ augparse -I lenses/ --nostdinc put_unambig.aug
Test result: put_unambig.aug:6.0-.24:
  { "dir" = "a" }
  { "sec" = "aa" }

Test result: put_unambig.aug:7.0-.43:
"axaay"

Now, let's add a prefix for each tree node labels:

module Multipart =

let dir = [ label "@" . key /[a]+/ . del "x" "x" ]
let sec = [ label "#" . key /[a]+/ . del "y" "y" ]
let foo = (dir|sec)*
test foo get "axaay" = ?
test foo put "ax" after clear "/#aa" = ?

$ augparse -I lenses/ --nostdinc multipart.aug
Test result: multipart.aug:6.0-.24:
  { "@a" }
  { "#aa" }
Test result: multipart.aug:7.0-.40:
"axaay"

Then the put ambiguity is avoided and the tree structure is more natural, because the key is on the tree path.

Attached is a patch for basic support of multipart key. There is a memory leak and it fail with some recursive lens, but it shows the idea.

Cheer,

Francis
>From 5113df2f28cfdbacba57c1755c565fdcc2d73a4e Mon Sep 17 00:00:00 2001
From: Francis Giraldeau <[email protected]>
Date: Mon, 18 Oct 2010 15:38:56 -0400
Subject: [PATCH] Basic support for multipart keys

Multipart key is useful to avoid put ambiguity between two similar keys. In the
get direction, it adds a prefix to the key, and removes it in the put
direction.

  * src/get.c: fix get_key to concatenate previously existing label
  * src/lens.c: accept one label and one key in a concat, the label must be
    before the key, and concatenate the label to the key to form the tree
    node label
  * src/lens.h: distinguish between key and label lens
  * src/put.c: remove prefix from the tree node label
  * tests/modules/fail_multi_label_concat.aug: avoid multiple labels in subtree
  * tests/modules/fail_multipart_ambig.aug: avoid multiple keys in subtree
  * tests/modules/fail_multipart_order.aug: avoir multiple labels in subtree
  * tests/modules/pass_multipart_key.aug: unittests for correct behavior

The test pass_multipart_key.aug is known to fail in this version on a recursive
lens. It triggers a segfault from bug_on that should be diagnose. Recursive
lens is not yet properly handled.
---
 src/get.c                                 |   37 +++++++++++++++++++++++++---
 src/lens.c                                |   17 ++++++++++---
 src/lens.h                                |    1 +
 src/put.c                                 |   35 +++++++++++++++++++++++++-
 tests/modules/fail_multi_label_concat.aug |    3 ++
 tests/modules/fail_multipart_ambig.aug    |    3 ++
 tests/modules/fail_multipart_order.aug    |    3 ++
 tests/modules/pass_multipart_key.aug      |   26 ++++++++++++++++++++
 8 files changed, 115 insertions(+), 10 deletions(-)
 create mode 100644 tests/modules/fail_multi_label_concat.aug
 create mode 100644 tests/modules/fail_multipart_ambig.aug
 create mode 100644 tests/modules/fail_multipart_order.aug
 create mode 100644 tests/modules/pass_multipart_key.aug

diff --git a/src/get.c b/src/get.c
index 11ea5de..1bea57b 100644
--- a/src/get.c
+++ b/src/get.c
@@ -433,10 +433,26 @@ static struct skel *parse_value(struct lens *lens,
 
 static struct tree *get_key(struct lens *lens, struct state *state) {
     ensure0(lens->tag == L_KEY, state->info);
-    if (! REG_MATCHED(state))
+    char *tok, *key;
+    int len = 0;
+    if (! REG_MATCHED(state)) {
         no_match_error(state, lens);
-    else
-        state->key = token(state);
+        return NULL;
+    }
+    tok = token(state);
+    if (state->key != NULL) {
+        len = strlen(state->key) + strlen(tok) + 1;
+        key = malloc(len);
+        // FIXME: handle no memory error
+        //if (key < 0)
+        //    return ENOMEM;
+        snprintf(key, len, "%s%s", state->key, tok);
+        free(state->key);
+        free(tok);
+        state->key = key;
+    } else {
+        state->key = tok;
+    }
     return NULL;
 }
 
@@ -447,7 +463,20 @@ static struct skel *parse_key(struct lens *lens, struct state *state) {
 
 static struct tree *get_label(struct lens *lens, struct state *state) {
     ensure0(lens->tag == L_LABEL, state->info);
-    state->key = strdup(lens->string->str);
+    int len;
+    char *key;
+    if (state->key != NULL) {
+        len = strlen(state->key) + strlen(lens->string->str) + 1;
+        key = malloc(len);
+        // FIXME: handle no memory error
+        //if (key < 0)
+        //    return ENOMEM;
+        snprintf(key, len, "%s%s", state->key, lens->string->str);
+        free(state->key);
+        state->key = key;
+    } else {
+        state->key = strdup(lens->string->str);
+    }
     return NULL;
 }
 
diff --git a/src/lens.c b/src/lens.c
index 29859f5..d73ac7e 100644
--- a/src/lens.c
+++ b/src/lens.c
@@ -198,6 +198,7 @@ static struct lens *make_lens_binop(enum lens_tag tag, struct info *info,
     for (int i=0; i < lens->nchildren; i++) {
         lens->value = lens->value || lens->children[i]->value;
         lens->key = lens->key || lens->children[i]->key;
+        lens->label = lens->label || lens->children[i]->label;
     }
 
     if (ALLOC_N(types, lens->nchildren) < 0)
@@ -269,9 +270,14 @@ struct value *lns_make_concat(struct info *info,
         return make_exn_value(info, "Multiple stores in concat");
     }
     if (l1->key && l2->key) {
-        return make_exn_value(info, "Multiple keys/labels in concat");
+        return make_exn_value(info, "Multiple keys in concat");
+    }
+    if (l1->label && l2->label) {
+        return make_exn_value(info, "Multiple labels in concat");
+    }
+    if (l1->key && l2->label) {
+        return make_exn_value(info, "Label lens following a key lens is not allowed");
     }
-
     lens = make_lens_binop(L_CONCAT, info, l1, l2, regexp_concat_n);
     lens->consumes_value = consumes_value;
     if (! recursive)
@@ -494,7 +500,8 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info,
     lens = make_lens(tag, info);
     lens->regexp = regexp;
     lens->string = string;
-    lens->key = (tag == L_KEY || tag == L_LABEL || tag == L_SEQ);
+    lens->key = (tag == L_KEY || tag == L_SEQ);
+    lens->label = (tag == L_LABEL);
     lens->value = (tag == L_STORE || tag == L_VALUE);
     lens->consumes_value = (tag == L_STORE || tag == L_VALUE);
     lens->atype = regexp_make_empty(info);
@@ -1886,7 +1893,9 @@ static struct value *typecheck(struct lens *l, int check) {
         if (exn == NULL && l->value)
             exn = make_exn_value(l->info, "Multiple stores in iteration");
         if (exn == NULL && l->key)
-            exn = make_exn_value(l->info, "Multiple keys/labels in iteration");
+            exn = make_exn_value(l->info, "Multiple keys in iteration");
+        if (exn == NULL && l->label)
+            exn = make_exn_value(l->info, "Multiple labels in iteration");
         break;
     case L_MAYBE:
         if (check)
diff --git a/src/lens.h b/src/lens.h
index 7c2b6ce..3e5b988 100644
--- a/src/lens.h
+++ b/src/lens.h
@@ -80,6 +80,7 @@ struct lens {
     struct jmt               *jmt;    /* NULL when recursive == 0 */
     unsigned int              value : 1;
     unsigned int              key : 1;
+    unsigned int              label : 1;
     unsigned int              recursive : 1;
     unsigned int              consumes_value : 1;
     /* Whether we are inside a recursive lens or outside */
diff --git a/src/put.c b/src/put.c
index d592420..3782ef6 100644
--- a/src/put.c
+++ b/src/put.c
@@ -59,6 +59,7 @@ struct state {
     struct split     *split;
     const char       *key;
     const char       *value;
+    const char       *label;
     struct dict      *dict;
     struct skel      *skel;
     char             *path;   /* Position in the tree, for errors */
@@ -438,6 +439,11 @@ static void put_subtree(struct lens *lens, struct state *state) {
 
     struct tree *tree = state->split->tree;
     struct split *split = NULL;
+    const char *oldlabel = NULL;
+    if (state->label != NULL) {
+        oldlabel = strdup(state->label);
+        state->label = NULL;
+    }
 
     state->key = tree->label;
     state->value = tree->value;
@@ -458,6 +464,10 @@ static void put_subtree(struct lens *lens, struct state *state) {
     oldstate.path = state->path;
     *state = oldstate;
     *state->split= oldsplit;
+    if (state->label != NULL) {
+        free(state->label);
+        state->label = oldlabel;
+    }
     free_split(split);
     state->path[oldpathlen] = '\0';
 }
@@ -586,6 +596,22 @@ static void put_store(struct lens *lens, struct state *state) {
     }
 }
 
+static void put_key(struct lens *lens, struct state *state) {
+    assert(lens->tag == L_KEY);
+    // remove the prefix part of state->key if any
+    int offset = 0;
+    if (state->label != NULL) {
+        offset = strlen(state->label);
+    }
+    assert(strlen(state->key) >= offset);
+    fprintf(state->out, "%s", state->key + offset);
+}
+
+static void put_label(struct lens *lens, struct state *state) {
+    assert(lens->tag == L_LABEL);
+    state->label = strdup(lens->string->str);
+}
+
 static void put_rec(struct lens *lens, struct state *state) {
     put_lens(lens->body, state);
 }
@@ -602,9 +628,11 @@ static void put_lens(struct lens *lens, struct state *state) {
         put_store(lens, state);
         break;
     case L_KEY:
-        fprintf(state->out, "%s", state->key);
+        put_key(lens, state);
         break;
     case L_LABEL:
+        put_label(lens, state);
+        break;
     case L_VALUE:
         /* Nothing to do */
         break;
@@ -724,9 +752,11 @@ static void create_lens(struct lens *lens, struct state *state) {
         put_store(lens, state);
         break;
     case L_KEY:
-        fprintf(state->out, "%s", state->key);
+        put_key(lens, state);
         break;
     case L_LABEL:
+        put_label(lens, state);
+        break;
     case L_VALUE:
         /* Nothing to do */
         break;
@@ -787,6 +817,7 @@ void lns_put(FILE *out, struct lens *lens, struct tree *tree,
     put_lens(lens, &state);
 
     free(state.path);
+    free(state.label);
     free_split(state.split);
     free_skel(state.skel);
     free_dict(state.dict);
diff --git a/tests/modules/fail_multi_label_concat.aug b/tests/modules/fail_multi_label_concat.aug
new file mode 100644
index 0000000..2b782bf
--- /dev/null
+++ b/tests/modules/fail_multi_label_concat.aug
@@ -0,0 +1,3 @@
+module Fail_multi_label_concat =
+
+  let lns = label "a" . label "b"
diff --git a/tests/modules/fail_multipart_ambig.aug b/tests/modules/fail_multipart_ambig.aug
new file mode 100644
index 0000000..34289a4
--- /dev/null
+++ b/tests/modules/fail_multipart_ambig.aug
@@ -0,0 +1,3 @@
+module Fail_multipart_ambig = 
+
+    let k1 = [ label "a" . key /[b]*/ ] | [ key /(a)[b]*/ ]
diff --git a/tests/modules/fail_multipart_order.aug b/tests/modules/fail_multipart_order.aug
new file mode 100644
index 0000000..97ba828
--- /dev/null
+++ b/tests/modules/fail_multipart_order.aug
@@ -0,0 +1,3 @@
+module Fail_multipart_order = 
+
+    let k1 = [ key /[a]*/ . label "b" ]
\ No newline at end of file
diff --git a/tests/modules/pass_multipart_key.aug b/tests/modules/pass_multipart_key.aug
new file mode 100644
index 0000000..9ec7f15
--- /dev/null
+++ b/tests/modules/pass_multipart_key.aug
@@ -0,0 +1,26 @@
+module Pass_multipart_key = 
+
+    (* Multipart key adds a static label to a key in the get direction       *)
+    (* and removes it in the put direction. The main purpose is to avoid put *) 
+    (* ambiguity for similar keys.                                           *)
+
+(* 
+    let k1 = [ label "a" . key /[b]*/ ]
+    test k1 get "b" = { "ab" }
+    test k1 put "b" after rm "x" = "b"
+  
+    let k2 = [ label "a" . [ key /[c]+/ ] . key /[b]+/ ]
+    test k2 get "cb" = { "ab" { "c" } }
+    test k2 put "cb" after rm "x" = "cb"
+    
+    let k3 = [ label "a" . key /[b]+/ . del "c" "c" ] *
+    test k3 get "bcbbc" = { "ab" } { "abb" }
+    test k3 put "bc" after clear "/abb" = "bcbbc"
+
+    let rec k4 = [ label "a" . key /[b]+/ . del "c" "c" ] . k4?
+    test k4 get "bcbbc" = { "ab" } { "abb" }
+    test k4 put "bc" after clear "/abb" = "bcbbc"
+*)
+    let rec k5 = [ label "a" . key "b" . k5? . del "c" "c" ]
+    test k5 get "bbcc" = { "ab" { "ab" } }
+    (* test k5 put "bc" after clear "/ab/ab" = "bcbc" *)
\ No newline at end of file
-- 
1.7.0.4

_______________________________________________
augeas-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/augeas-devel

Reply via email to