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