[augeas-devel] [PATCH 4/6] Generic square lens v3

Francis Giraldeau francis.giraldeau at gmail.com
Sun Aug 12 21:37:20 UTC 2012


Lens square now takes lenses instead of regular expressions as arguments, as follow:

  square left body right

The left and right lenses must accept the same language. The left lens can be
either key or del, while the right lens must be del.

This patch changes the semantic of the square lens and is thus not backward
compatible. Modules must be updated accordingly. The definition of the square
lens should now be definitive, and later addition may add support for
additionnal lenses for left and right sides.

  * builtin.c: change definition of the square lens.
  * get.c: use the AST to recover the left and right strings matched by the
    square lens.Regular lenses are handled with regexp registers.
  * put.c: the variable state->override is used to indicate to a del part of a
    square lens that it should use the provided value instead of the saved
    string in the skel or the default value.
  * lens.c: change the lns_make_square to match the new structure.

Changes in v2:
  * protect arg with parenthesis in child_first and child_last macros
  * drop flag in_square, avoid lens copy, drop square_left/right strings
    replaced by ast for rec lens and regs for regular lens. This
    processing is more general than the previous approach, and thus
    will help to extends the square lens more in the future.
Changed in v3:
  * Revert white space crap caused by Eclipse auto-format
  * Split the patch into smaller units

Signed-off-by: Francis Giraldeau <francis.giraldeau at gmail.com>
---
 src/builtin.c |   15 +++---
 src/get.c     |  142 ++++++++++++++++++++++++++++++++++++++-------------------
 src/lens.c    |  115 ++++++++++++++++++++++++++++++++++++----------
 src/lens.h    |   12 +++--
 src/put.c     |   71 ++++++++++++++++++++++++-----
 5 files changed, 259 insertions(+), 96 deletions(-)

diff --git a/src/builtin.c b/src/builtin.c
index 792549e..2da7bbb 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -86,14 +86,15 @@ static struct value *lns_counter(struct info *info, struct value *str) {
     return lns_make_prim(L_COUNTER, ref(info), NULL, ref(str->string));
 }
 
-/* V_REGEXP -> V_LENS -> V_LENS */
-static struct value *lns_square(struct info *info, struct value *rxp,
-                                struct value *lns) {
-    assert(rxp->tag == V_REGEXP);
-    assert(lns->tag == V_LENS);
+/* V_LENS -> V_LENS -> V_LENS -> V_LENS */
+static struct value *lns_square(struct info *info, struct value *l1,
+                                struct value *l2, struct value *l3) {
+    assert(l1->tag == V_LENS);
+    assert(l2->tag == V_LENS);
+    assert(l3->tag == V_LENS);
     int check = info->error->aug->flags & AUG_TYPE_CHECK;
 
-    return lns_make_square(ref(info), ref(rxp->regexp), ref(lns->lens), check);
+    return lns_make_square(ref(info), ref(l1->lens), ref(l2->lens), ref(l3->lens), check);
 }
 
 static struct value *make_exn_lns_error(struct info *info,
@@ -589,7 +590,7 @@ struct module *builtin_init(struct error *error) {
     DEFINE_NATIVE(modl, "label",   1, lns_label, T_STRING, T_LENS);
     DEFINE_NATIVE(modl, "seq",     1, lns_seq, T_STRING, T_LENS);
     DEFINE_NATIVE(modl, "counter", 1, lns_counter, T_STRING, T_LENS);
-    DEFINE_NATIVE(modl, "square",  2, lns_square, T_REGEXP, T_LENS, T_LENS);
+    DEFINE_NATIVE(modl, "square",  3, lns_square, T_LENS, T_LENS, T_LENS, T_LENS);
     /* Applying lenses (mostly for tests) */
     DEFINE_NATIVE(modl, "get",     2, lens_get, T_LENS, T_STRING, T_TREE);
     DEFINE_NATIVE(modl, "put",     3, lens_put, T_LENS, T_TREE, T_STRING,
diff --git a/src/get.c b/src/get.c
index 05663a4..431f3e5 100644
--- a/src/get.c
+++ b/src/get.c
@@ -50,7 +50,6 @@ struct state {
     struct seq       *seqs;
     char             *key;
     char             *value;     /* GET_STORE leaves a value here */
-    char             *square;    /* last L_DEL from L_SQUARE */
     struct lns_error *error;
     /* We use the registers from a regular expression match to keep track
      * of the substring we are currently looking at. REGS are the registers
@@ -73,7 +72,6 @@ struct state {
 struct frame {
     struct lens     *lens;
     char            *key;
-    char            *square;
     struct span     *span;
     union {
         struct { /* MGET */
@@ -365,6 +363,10 @@ static char *token(struct state *state) {
     return strndup(REG_POS(state), REG_SIZE(state));
 }
 
+static char *token_range(const char *text, uint start, uint end) {
+    return strndup(text + start, end - start);
+}
+
 static void regexp_match_error(struct state *state, struct lens *lens,
                                int count, struct regexp *r) {
     char *text = NULL;
@@ -501,9 +503,6 @@ static struct tree *get_del(struct lens *lens, struct state *state) {
         get_error(state, lens, "no match for del /%s/", pat);
         free(pat);
     }
-    if (lens->string == NULL) {
-        state->square = token(state);
-    }
     update_span(state->span, REG_START(state), REG_END(state));
     return NULL;
 }
@@ -831,28 +830,83 @@ static struct skel *parse_subtree(struct lens *lens, struct state *state,
     return make_skel(lens);
 }
 
+/* Check if left and right strings matches according to the square lens
+ * definition.
+ *
+ * Returns 1 if strings matches, 0 otherwise
+ */
+static int square_match(struct lens *lens, char *left, char *right) {
+    int cmp = 0;
+    struct lens *concat = NULL;
+
+    // if one of the argument is NULL, returns no match
+    if (left == NULL || right == NULL || lens == NULL)
+        return cmp;
+
+    concat = lens->child;
+    /* If either right or left lens is nocase, then ignore case */
+    if (child_first(concat)->ctype->nocase ||
+            child_last(concat)->ctype->nocase) {
+        cmp = STRCASEEQ(left, right);
+    } else {
+        cmp = STREQ(left, right);
+    }
+    return cmp;
+}
+
+/*
+ * This function applies only for non-recursive lens, handling of recursive
+ * square is done in visit_exit().
+ */
 static struct tree *get_square(struct lens *lens, struct state *state) {
     ensure0(lens->tag == L_SQUARE, state->info);
 
+    struct lens *concat = lens->child;
     struct tree *tree = NULL;
-    char *key = NULL, *square = NULL;
-
-    // get the child lens
-    tree = get_concat(lens->child, state);
-
-    key = state->key;
-    square = state->square;
-    ensure0(key != NULL, state->info);
-    ensure0(square != NULL, state->info);
+    struct lens *curr;
+    uint old_nreg = state->nreg;
+    uint nreg = state->nreg;
+    uint nsub, i;
+    char *rsqr = NULL, *lsqr = NULL;
+    uint start, end;
+
+    tree = get_lens(lens->child, state);
+
+    /* retrieve left component */
+    nreg = old_nreg + 1;
+    start = state->regs->start[nreg];
+    end = state->regs->end[nreg];
+    lsqr = token_range(state->text, start, end);
+
+    /* retrieve right component */
+    /* compute nreg for the last children */
+    nreg = old_nreg + 1;
+    for (i = 0; i < concat->nchildren - 1; i++) {
+        curr = concat->children[i];
+        nsub = regexp_nsub(curr->ctype);
+        nreg += 1 + nsub;
+    }
+    start = state->regs->start[nreg];
+    end = state->regs->end[nreg];
+    rsqr = token_range(state->text, start, end);
 
-    if (STRCASENEQ(key, square)) {
+    if (!square_match(lens, lsqr, rsqr)) {
         get_error(state, lens, "%s \"%s\" %s \"%s\"",
-                "Parse error: mismatched key in square lens, expecting", key,
-                "but got", square);
+            "Parse error: mismatched in square lens, expecting", lsqr,
+            "but got", rsqr);
+        goto error;
     }
 
-    FREE(state->square);
+ done:
+    state->nreg = old_nreg;
+    FREE(lsqr);
+    FREE(rsqr);
     return tree;
+
+ error:
+    free_tree(tree);
+    tree = NULL;
+    goto done;
 }
 
 static struct skel *parse_square(struct lens *lens, struct state *state,
@@ -861,6 +915,8 @@ static struct skel *parse_square(struct lens *lens, struct state *state,
     struct skel *skel, *sk;
 
     skel = parse_concat(lens->child, state, dict);
+    if (skel == NULL)
+        return NULL;
     sk = make_skel(lens);
     sk->skels = skel;
 
@@ -876,7 +932,7 @@ static void print_frames(struct rec_state *state) {
     for (int j = state->fused - 1; j >=0; j--) {
         struct frame *f = state->frames + j;
         for (int i=0; i < state->lvl; i++) fputc(' ', stderr);
-        fprintf(stderr, "%2d %s %s %s", j, f->key, f->value, f->square);
+        fprintf(stderr, "%2d %s %s", j, f->key, f->value);
         if (f->tree == NULL) {
             fprintf(stderr, " - ");
         } else {
@@ -947,10 +1003,8 @@ static void get_terminal(struct frame *top, struct lens *lens,
     top->tree = get_lens(lens, state);
     top->key = state->key;
     top->value = state->value;
-    top->square = state->square;
     state->key = NULL;
     state->value = NULL;
-    state->square = NULL;
 }
 
 static void parse_terminal(struct frame *top, struct lens *lens,
@@ -1024,7 +1078,7 @@ static void visit_enter(struct lens *lens,
 static void get_combine(struct rec_state *rec_state,
                         struct lens *lens, uint n) {
     struct tree *tree = NULL, *tail = NULL;
-    char *key = NULL, *value = NULL, *square = NULL;
+    char *key = NULL, *value = NULL;
     struct frame *top = NULL;
 
     if (n > 0)
@@ -1044,16 +1098,11 @@ static void get_combine(struct rec_state *rec_state,
             ensure(value == NULL, rec_state->state->info);
             value = top->value;
         }
-        if (top->square != NULL) {
-            ensure(square == NULL, rec_state->state->info);
-            square = top->square;
-        }
     }
     top = push_frame(rec_state, lens);
     top->tree = tree;
     top->key = key;
     top->value = value;
-    top->square = square;
  error:
     return;
 }
@@ -1170,24 +1219,26 @@ static void visit_exit(struct lens *lens,
             parse_combine(rec_state, lens, n);
     } else if (lens->tag == L_SQUARE) {
         if (rec_state->mode == M_GET) {
-            char *key, *square;
-
-            key = top_frame(rec_state)->key;
-            square = top_frame(rec_state)->square;
-
-            ensure(key != NULL, state->info);
-            ensure(square != NULL, state->info);
-
-            // raise syntax error if they are not equals
-            if (STRCASENEQ(key, square)){
+            struct ast *square, *concat, *right, *left;
+            char *rsqr, *lsqr;
+            int ret;
+
+            square = rec_state->ast;
+            concat = child_first(square);
+            right = child_first(concat);
+            left = child_last(concat);
+            lsqr = token_range(state->text, left->start, left->end);
+            rsqr = token_range(state->text, right->start, right->end);
+            ret = square_match(lens, lsqr, rsqr);
+            if (! ret) {
                 get_error(state, lens, "%s \"%s\" %s \"%s\"",
-                        "Parse error: mismatched key in square lens, expecting",
-                        key, "but got", square);
-                state->error->pos = end - strlen(square);
-                goto error;
+                        "Parse error: mismatched in square lens, expecting", lsqr,
+                        "but got", rsqr);
             }
-
-            FREE(square);
+            FREE(lsqr);
+            FREE(rsqr);
+            if (! ret)
+                goto error;
             get_combine(rec_state, lens, 1);
         } else {
             parse_combine(rec_state, lens, 1);
@@ -1278,7 +1329,6 @@ static struct frame *rec_process(enum mode_t mode, struct lens *lens,
     for(i = 0; i < rec_state.fused; i++) {
         f = nth_frame(&rec_state, i);
         FREE(f->key);
-        FREE(f->square);
         if (mode == M_GET) {
             FREE(f->value);
             free_tree(f->tree);
@@ -1438,10 +1488,6 @@ struct tree *lns_get(struct info *info, struct lens *lens, const char *text,
         get_error(&state, lens, "get left unused value %s", state.value);
         free(state.value);
     }
-    if (state.square != NULL) {
-        get_error(&state, lens, "get left unused square %s", state.square);
-        free(state.square);
-    }
     if (partial && state.error == NULL) {
         get_error(&state, lens, "Get did not match entire input");
     }
diff --git a/src/lens.c b/src/lens.c
index 0134996..3e99159 100644
--- a/src/lens.c
+++ b/src/lens.c
@@ -50,6 +50,8 @@ static struct value * typecheck_union(struct info *,
                                       struct lens *l1, struct lens *l2);
 static struct value *typecheck_concat(struct info *,
                                       struct lens *l1, struct lens *l2);
+static struct value *typecheck_square(struct info *,
+                                      struct lens *l1, struct lens *l2);
 static struct value *typecheck_iter(struct info *info, struct lens *l);
 static struct value *typecheck_maybe(struct info *info, struct lens *l);
 
@@ -399,34 +401,29 @@ struct value *lns_make_maybe(struct info *info, struct lens *l, int check) {
 }
 
 /* Build a square lens as
- *   key REG . lns . del REG MATCHED
- * where MATCHED is whatever the key lens matched (the inability to express
- * this with other lenses makes the square primitve necessary
+ *    left . body . right
+ * where left and right accepts the same language and
+ * captured strings must match. The inability to express this with other
+ * lenses makes the square primitive necessary.
  */
-struct value *lns_make_square(struct info *info,
-                              struct regexp *reg,
-                              struct lens *lns, int check) {
-    struct value *key = NULL, *del = NULL;
+struct value * lns_make_square(struct info *info, struct lens *l1,
+                               struct lens *l2, struct lens *l3, int check) {
     struct value *cnt1 = NULL, *cnt2 = NULL, *res = NULL;
     struct lens *sqr = NULL;
 
-    res = lns_make_prim(L_KEY, ref(info), ref(reg), NULL);
-    if (EXN(res))
-        goto error;
-    key = res;
+    /* supported types: L_KEY . body . L_DEL or L_DEL . body . L_DEL */
+    if (l3->tag != L_DEL || (l1->tag != L_DEL && l1->tag != L_KEY))
+        return make_exn_value(info, "Supported types: (key lns del) or (del lns del)");
 
-    res = lns_make_prim(L_DEL, ref(info), ref(reg), NULL);
-    if (EXN(res))
+    res = typecheck_square(info, l1, l3);
+    if (res != NULL)
         goto error;
-    del = res;
 
-    // typechecking is handled when concatenating lenses
-    res = lns_make_concat(ref(info), ref(key->lens), ref(lns), check);
+    res = lns_make_concat(ref(info), ref(l1), ref(l2), check);
     if (EXN(res))
         goto error;
     cnt1 = res;
-
-    res = lns_make_concat(ref(info), ref(cnt1->lens), ref(del->lens), check);
+    res = lns_make_concat(ref(info), ref(cnt1->lens), ref(l3), check);
     if (EXN(res))
         goto error;
     cnt2 = res;
@@ -446,11 +443,9 @@ struct value *lns_make_square(struct info *info,
 
  error:
     unref(info, info);
-    unref(reg, regexp);
-    unref(lns, lens);
-
-    unref(key, value);
-    unref(del, value);
+    unref(l1, lens);
+    unref(l2, lens);
+    unref(l3, lens);
     unref(cnt1, value);
     unref(cnt2, value);
     unref(sqr, lens);
@@ -788,6 +783,74 @@ static struct value *typecheck_concat(struct info *info,
     return result;
 }
 
+static struct value *make_exn_square(struct info *info, struct lens *l1,
+                                     struct lens *l2, const char *msg) {
+
+    char *fi;
+    struct value *exn = make_exn_value(ref(info), "%s",
+            "Inconsistency in lens square");
+    exn_printf_line(exn, "%s", msg);
+    fi = format_info(l1->info);
+    exn_printf_line(exn, "Left lens: %s", fi);
+    free(fi);
+    fi = format_info(l2->info);
+    exn_printf_line(exn, "Right lens: %s", fi);
+    free(fi);
+    return exn;
+}
+
+static struct value *typecheck_square(struct info *info, struct lens *l1,
+                                      struct lens *l2) {
+    int r;
+    struct value *exn = NULL;
+    struct fa *fa1 = NULL, *fa2 = NULL;
+    struct regexp *r1 = ltype(l1, CTYPE);
+    struct regexp *r2 = ltype(l2, CTYPE);
+
+    if (r1 == NULL || r2 == NULL)
+        return NULL;
+
+    exn = regexp_to_fa(r1, &fa1);
+    if (exn != NULL)
+        goto done;
+
+    exn = regexp_to_fa(r2, &fa2);
+    if (exn != NULL)
+        goto done;
+
+    r = fa_equals(fa1, fa2);
+
+    if (r < 0) {
+        exn = make_exn_value(ref(info), "not enough memory");
+        if (exn != NULL) {
+            return exn;
+        } else {
+            ERR_REPORT(info, AUG_ENOMEM, NULL);
+            return info->error->exn;;
+        }
+    }
+
+    if (r == 0) {
+        exn = make_exn_square(info, l1, l2,
+                "Left and right lenses must accept the same language");
+        goto done;
+    }
+
+    /* check del create consistency */
+    if (l1->tag == L_DEL && l2->tag == L_DEL) {
+        if (!STREQ(l1->string->str, l2->string->str)) {
+            exn = make_exn_square(info, l1, l2,
+                    "Left and right lenses must have the same default value");
+            goto done;
+        }
+    }
+
+ done:
+    fa_free(fa1);
+    fa_free(fa2);
+    return exn;
+}
+
 static struct value *
 ambig_iter_check(struct info *info, const char *msg,
                  enum lens_type typ, struct lens *l) {
@@ -917,7 +980,7 @@ void lens_release(struct lens *lens) {
         regexp_release(lens->regexp);
 
     if (lens->tag == L_SUBTREE || lens->tag == L_STAR
-        || lens->tag == L_MAYBE) {
+        || lens->tag == L_MAYBE || lens->tag == L_SQUARE) {
         lens_release(lens->child);
     }
 
@@ -1536,6 +1599,10 @@ static void rtn_rules(struct rtn *rtn, struct lens *l) {
         rtn_rules(rtn, l->body);
         RTN_BAIL(rtn);
         break;
+    case L_SQUARE:
+        add_trans(rtn, start, prod->end, l->child);
+        RTN_BAIL(rtn);
+        break;
     default:
         BUG_LENS_TAG(l);
         break;
diff --git a/src/lens.h b/src/lens.h
index 62c2f77..b8cb202 100644
--- a/src/lens.h
+++ b/src/lens.h
@@ -90,9 +90,6 @@ struct lens {
     union {
         /* Primitive lenses */
         struct {                   /* L_DEL uses both */
-            /* L_DEL string set to NULL means it belongs to parent L_SQUARE lens
-             * and the put and create copy the current key
-             */
             struct regexp *regexp; /* L_STORE, L_KEY */
             struct string *string; /* L_VALUE, L_LABEL, L_SEQ, L_COUNTER */
         };
@@ -143,8 +140,9 @@ struct value *lns_make_plus(struct info *, struct lens *,
                             int check);
 struct value *lns_make_maybe(struct info *, struct lens *,
                              int check);
-struct value *lns_make_square(struct info *, struct regexp *, struct lens *,
-                              int check);
+struct value *lns_make_square(struct info *, struct lens *, struct lens *,
+                              struct lens *lens, int check);
+
 
 /* Pretty-print a lens */
 char *format_lens(struct lens *l);
@@ -241,6 +239,10 @@ void free_lens(struct lens *lens);
 #define ENCLEN(s) ((s) == NULL ? strlen(ENC_NULL) : strlen(s))
 #define ENCSTR(s) ((s) == NULL ? ENC_NULL : s)
 
+/* helper to access first and last child */
+#define child_first(l) (l)->children[0]
+#define child_last(l) (l)->children[(l)->nchildren - 1]
+
 /* Format an encoded level as
  *    { key1 = value1 } { key2 = value2 } .. { keyN = valueN }
  */
diff --git a/src/put.c b/src/put.c
index 56732d2..5e6212e 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       *override;
     struct dict      *dict;
     struct skel      *skel;
     char             *path;   /* Position in the tree, for errors */
@@ -468,11 +469,10 @@ static void put_del(ATTRIBUTE_UNUSED struct lens *lens, struct state *state) {
     assert(lens->tag == L_DEL);
     assert(state->skel != NULL);
     assert(state->skel->tag == L_DEL);
-    if (lens->string != NULL) {
-    fprintf(state->out, "%s", state->skel->text);
+    if (state->override != NULL) {
+        fprintf(state->out, "%s", state->override);
     } else {
-    /* L_DEL with NULL string: replicate the current key */
-        fprintf(state->out, "%s", state->key);
+        fprintf(state->out, "%s", state->skel->text);
     }
 }
 
@@ -598,9 +598,32 @@ static void put_rec(struct lens *lens, struct state *state) {
 }
 
 static void put_square(struct lens *lens, struct state *state) {
+    assert(lens->tag == L_SQUARE);
     struct skel *oldskel = state->skel;
-    state->skel = state->skel->skels;
-    put_lens(lens->child, state);
+    struct split *oldsplit = state->split;
+    struct lens *concat = lens->child;
+    struct lens *left = concat->children[0];
+    struct split *split = split_concat(state, concat);
+
+    /* skels of concat is one depth more */
+    state->skel = state->skel->skels->skels;
+    set_split(state, split);
+    for (int i=0; i < concat->nchildren; i++) {
+        if (state->split == NULL) {
+            put_error(state, concat, "Not enough components in square");
+            list_free(split);
+            return;
+        }
+        struct lens *curr = concat->children[i];
+        if (i == (concat->nchildren - 1) && left->tag == L_KEY)
+            state->override = state->key;
+        put_lens(curr, state);
+        state->override = NULL;
+        state->skel = state->skel->next;
+        next_split(state);
+    }
+    list_free(split);
+    set_split(state, oldsplit);
     state->skel = oldskel;
 }
 
@@ -661,13 +684,11 @@ static void create_subtree(struct lens *lens, struct state *state) {
 
 static void create_del(struct lens *lens, struct state *state) {
     assert(lens->tag == L_DEL);
-    if (lens->string != NULL) {
-        print_escaped_chars(state->out, lens->string->str);
+    if (state->override != NULL) {
+        print_escaped_chars(state->out, state->override);
     } else {
-        /* L_DEL with NULL string: replicate the current key */
-        print_escaped_chars(state->out, state->key);
+        print_escaped_chars(state->out, lens->string->str);
     }
-
 }
 
 static void create_union(struct lens *lens, struct state *state) {
@@ -703,6 +724,32 @@ static void create_concat(struct lens *lens, struct state *state) {
     set_split(state, oldsplit);
 }
 
+static void create_square(struct lens *lens, struct state *state) {
+    assert(lens->tag == L_SQUARE);
+    struct lens *concat = lens->child;
+
+    struct split *oldsplit = state->split;
+    struct split *split = split_concat(state, concat);
+    struct lens *left = concat->children[0];
+
+    set_split(state, split);
+    for (int i=0; i < concat->nchildren; i++) {
+        if (state->split == NULL) {
+            put_error(state, concat, "Not enough components in square");
+            list_free(split);
+            return;
+        }
+        struct lens *curr = concat->children[i];
+        if (i == (concat->nchildren - 1) && left->tag == L_KEY)
+            state->override = state->key;
+        create_lens(curr, state);
+        state->override = NULL;
+        next_split(state);
+    }
+    list_free(split);
+    set_split(state, oldsplit);
+}
+
 static void create_quant_star(struct lens *lens, struct state *state) {
     assert(lens->tag == L_STAR);
     struct split *oldsplit = state->split;
@@ -777,7 +824,7 @@ static void create_lens(struct lens *lens, struct state *state) {
         create_rec(lens, state);
         break;
     case L_SQUARE:
-        create_concat(lens->child, state);
+        create_square(lens, state);
         break;
     default:
         assert(0);
-- 
1.7.9.5




More information about the augeas-devel mailing list