[augeas-devel] [PATCH] Generic square lens v2

Francis Giraldeau francis.giraldeau at gmail.com
Thu Aug 9 12:18:56 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: adds a simple AST to keep track of the parse to match left and right
    side of the square. 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.
  * info.c: dump details of error if available.
  * Update httpd.aug and xml.aug witht the new square lens definition.
  * Update unit tests according to the new definition.
  * Add new debugging option cf.get.ast to dump the AST on stdout

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.

Signed-off-by: Francis Giraldeau <francis.giraldeau at gmail.com>
---
 lenses/httpd.aug                              |    6 +-
 lenses/xml.aug                                |    8 +-
 src/builtin.c                                 |   13 +-
 src/get.c                                     |  284 +++++++--
 src/info.c                                    |    2 +
 src/info.h                                    |    1 +
 src/lens.c                                    |  805 ++++++++++++++-----------
 src/lens.h                                    |   14 +-
 src/put.c                                     |   72 ++-
 tests/modules/fail_square_consistency.aug     |    6 +
 tests/modules/fail_square_consistency_del.aug |    6 +
 tests/modules/fail_square_dup_key.aug         |    6 +
 tests/modules/fail_square_lens_type.aug       |    6 +
 tests/modules/pass_square.aug                 |  111 ++--
 tests/modules/pass_square_rec.aug             |  139 +++++
 15 files changed, 966 insertions(+), 513 deletions(-)
 create mode 100644 tests/modules/fail_square_consistency.aug
 create mode 100644 tests/modules/fail_square_consistency_del.aug
 create mode 100644 tests/modules/fail_square_dup_key.aug
 create mode 100644 tests/modules/fail_square_lens_type.aug
 create mode 100644 tests/modules/pass_square_rec.aug

diff --git a/lenses/httpd.aug b/lenses/httpd.aug
index 49456a1..caea9b6 100644
--- a/lenses/httpd.aug
+++ b/lenses/httpd.aug
@@ -77,9 +77,11 @@ let directive = [ indent . label "directive" . store word .
                   (sep_spc . argv arg_dir)? . eol ]
 
 let section (body:lens) =
-    let h = (sep_spc . argv arg_sec)? . sep_osp .
+    let inner = (sep_spc . argv arg_sec)? . sep_osp .
              dels ">" . eol . body* . indent . dels "</" in
-        [ indent . dels "<" . square word h . del ">" ">" . eol ]
+    let kword = key word in
+    let dword = del word "a" in
+        [ indent . dels "<" . square kword inner dword . del ">" ">" . eol ]
 
 let rec content = section (content|directive|comment|empty)
 
diff --git a/lenses/xml.aug b/lenses/xml.aug
index 6bcbb54..50b1712 100644
--- a/lenses/xml.aug
+++ b/lenses/xml.aug
@@ -109,11 +109,15 @@ let text      = [ label "#text" . store text_re ]
 let cdata     = [ label "#CDATA" . dels "<![CDATA[" .
                   store (char* - (char* . "]]>" . char*)) . dels "]]>" ]
 
+(* the value of nmtoken_del is always the nmtoken_key string *)
+let nmtoken_key = key nmtoken
+let nmtoken_del = del nmtoken "a"
+
 let element (body:lens) =
     let h = attributes? . sep_osp . dels ">" . body* . dels "</" in
-        [ dels "<" . square nmtoken h . sep_osp . del_end ]
+        [ dels "<" . square nmtoken_key h nmtoken_del . sep_osp . del_end ]
 
-let empty_element = [ dels "<" . key nmtoken . value "#empty" .
+let empty_element = [ dels "<" . nmtoken_key . value "#empty" .
                       attributes? . sep_osp . del /\/>[\r?\n]?/ "/>\n" ]
 
 let pi_instruction = [ dels "<?" . label "#pi" .
diff --git a/src/builtin.c b/src/builtin.c
index 792549e..a78ef29 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -87,13 +87,14 @@ static struct value *lns_counter(struct info *info, struct value *str) {
 }
 
 /* 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);
+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 c1e206c..508c4f4 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 */
@@ -90,6 +88,17 @@ struct frame {
 /* Used by recursive lenses in get_rec and parse_rec */
 enum mode_t { M_GET, M_PARSE };
 
+/* Abstract Syntax Tree for recursive parse */
+struct ast {
+    struct ast         *parent;
+    struct ast        **children;
+    uint                nchildren;
+    uint                capacity;
+    struct lens        *lens;
+    uint                start;
+    uint                end;
+};
+
 struct rec_state {
     enum mode_t          mode;
     struct state        *state;
@@ -98,6 +107,7 @@ struct rec_state {
     struct frame        *frames;
     size_t               start;
     uint                 lvl;  /* Debug only */
+    struct ast          *ast;
 };
 
 #define REG_START(state) ((state)->regs->start[(state)->nreg])
@@ -109,6 +119,74 @@ struct rec_state {
 #define REG_MATCHED(state) (REG_VALID(state)                            \
                             && (state)->regs->start[(state)->nreg] >= 0)
 
+/*
+ * AST utils
+ */
+static struct ast *make_ast(struct lens *lens) {
+    struct ast *ast = NULL;
+
+    CALLOC(ast, 1);
+    if (ast == NULL)
+        return NULL;
+    ast->lens = lens;
+    ast->capacity = 4;
+    CALLOC(ast->children, ast->capacity);
+    if (ast->children == NULL)
+        goto error;
+ done:
+    return ast;
+ error:
+    FREE(ast);
+    goto done;
+}
+
+/* recursively free the node and all it's descendants */
+static void free_ast(struct ast *ast) {
+    int i;
+    if (ast == NULL)
+        return;
+    for (i = 0; i < ast->nchildren; i++) {
+        free_ast(ast->children[i]);
+    }
+    if (ast->children != NULL)
+        FREE(ast->children);
+    FREE(ast);
+}
+
+/* append a child to the parent ast */
+ATTRIBUTE_RETURN_CHECK
+static int ast_append(struct ast *parent, struct ast *child) {
+    if (parent == NULL)
+        return 0;
+    if (parent->nchildren >= parent->capacity) {
+        int ret = REALLOC_N(parent->children, parent->capacity * 2);
+        if (ret < 0)
+            return -1;
+        parent->capacity = parent->capacity * 2;
+    }
+    parent->children[parent->nchildren++] = child;
+    child->parent = parent;
+    return 0;
+}
+
+static void ast_set(struct ast *ast, uint start, uint end) {
+    ast->start = start;
+    ast->end = end;
+}
+
+static void print_ast(struct ast *ast, int lvl) {
+    int i;
+    if (ast == NULL)
+        return;
+    char *lns = format_lens(ast->lens);
+    for (i = 0; i < lvl; i++) fputs(" ", stdout);
+    printf("%d..%d %s\n", ast->start, ast->end, lns);
+    free(lns);
+    for (i = 0; i < ast->nchildren; i++) {
+        print_ast(ast->children[i], lvl + 1);
+    }
+}
+
 static void get_error(struct state *state, struct lens *lens,
                       const char *format, ...)
     ATTRIBUTE_FORMAT(printf, 3, 4);
@@ -160,14 +238,12 @@ static struct skel *make_skel(struct lens *lens) {
 void free_skel(struct skel *skel) {
     if (skel == NULL)
         return;
-    if (skel->tag == L_CONCAT || skel->tag == L_STAR || skel->tag == L_MAYBE ||
-        skel->tag == L_SQUARE) {
-        while (skel->skels != NULL) {
-            struct skel *del = skel->skels;
-            skel->skels = del->next;
-            free_skel(del);
-        }
-    } else if (skel->tag == L_DEL) {
+    while (skel->skels != NULL) {
+        struct skel *del = skel->skels;
+        skel->skels = del->next;
+        free_skel(del);
+    }
+    if (skel->text != NULL) {
         free(skel->text);
     }
     free(skel);
@@ -257,6 +333,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;
@@ -393,9 +473,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;
 }
@@ -723,28 +800,81 @@ static struct skel *parse_subtree(struct lens *lens, struct state *state,
     return make_skel(lens);
 }
 
+static int square_match(struct lens *lens, char *left, char *right) {
+    int cmp = 0;
+    struct lens *concat = NULL;
+
+    concat = lens->child;
+    if (left == NULL || right == NULL)
+        return cmp;
+    /*
+     * If either right or left lens is nocase, then ignore case when checking
+     * if both are matching
+     */
+    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;
+    if (lsqr != NULL)
+        FREE(lsqr);
+    if (rsqr != NULL)
+        FREE(rsqr);
     return tree;
+
+ error:
+    free_tree(tree);
+    tree = NULL;
+    goto done;
 }
 
 static struct skel *parse_square(struct lens *lens, struct state *state,
@@ -753,6 +883,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;
 
@@ -768,7 +900,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 {
@@ -828,8 +960,10 @@ static void dbg_visit(struct lens *lens, char action, size_t start, size_t end,
 
     for (int i=0; i < lvl; i++)
         fputc(' ', stderr);
+    char *lns = format_lens(lens);
     fprintf(stderr, "%c %zd..%zd %d %s\n", action, start, end,
-            fused, format_lens(lens));
+            fused, lns);
+    free(lns);
 }
 
 static void get_terminal(struct frame *top, struct lens *lens,
@@ -837,10 +971,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,
@@ -856,6 +988,8 @@ static void visit_terminal(struct lens *lens, size_t start, size_t end,
     struct rec_state *rec_state = data;
     struct state *state = rec_state->state;
     struct re_registers *old_regs = state->regs;
+    struct ast *child;
+    int ret = 0;
     uint old_nreg = state->nreg;
 
     if (state->error != NULL)
@@ -869,6 +1003,15 @@ static void visit_terminal(struct lens *lens, size_t start, size_t end,
         get_terminal(top, lens, state);
     else
         parse_terminal(top, lens, state);
+
+    /* append a node to the ast for this terminal */
+    child = make_ast(lens);
+    ERR_NOMEM(child == NULL, state->info);
+    ast_set(child, start, end);
+    ret = ast_append(rec_state->ast, child);
+    ERR_NOMEM(ret < 0, state->info);
+
+ error:
     free_regs(state);
     state->regs = old_regs;
     state->nreg = old_nreg;
@@ -880,6 +1023,8 @@ static void visit_enter(struct lens *lens,
                         void *data) {
     struct rec_state *rec_state = data;
     struct state *state = rec_state->state;
+    struct ast *child;
+    int ret;
 
     if (state->error != NULL)
         return;
@@ -902,14 +1047,31 @@ static void visit_enter(struct lens *lens,
             ERR_NOMEM(state->span == NULL, state->info);
         }
     }
+
+    /* append a new ast node */
+    child = make_ast(lens);
+    ERR_NOMEM(child == NULL, state->info);
+    ast_set(child, start, end);
+    ret = ast_append(rec_state->ast, child);
+    ERR_NOMEM(ret < 0, state->info);
+    rec_state->ast = child;
+
  error:
     return;
 }
 
+#define combine_assign(src, dst, rec_state)                            \
+    do {                                                               \
+        if (src != NULL) {                                             \
+            ensure(dst == NULL, rec_state->state->info);               \
+            dst = src;                                                 \
+        } \
+    } while (0)
+
 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)
@@ -921,24 +1083,13 @@ static void get_combine(struct rec_state *rec_state,
         if (tail != NULL)
             while (tail->next != NULL) tail = tail->next;
 
-        if (top->key != NULL) {
-            ensure(key == NULL, rec_state->state->info);
-            key = top->key;
-        }
-        if (top->value != NULL) {
-            ensure(value == NULL, rec_state->state->info);
-            value = top->value;
-        }
-        if (top->square != NULL) {
-            ensure(square == NULL, rec_state->state->info);
-            square = top->square;
-        }
+        combine_assign(top->key, key, rec_state);
+        combine_assign(top->value, value, rec_state);
     }
     top = push_frame(rec_state, lens);
     top->tree = tree;
     top->key = key;
     top->value = value;
-    top->square = square;
  error:
     return;
 }
@@ -978,6 +1129,7 @@ static void visit_exit(struct lens *lens,
                        void *data) {
     struct rec_state *rec_state = data;
     struct state *state = rec_state->state;
+    char *rsqr = NULL, *lsqr = NULL;
 
     if (state->error != NULL)
         return;
@@ -1055,24 +1207,22 @@ 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;
+            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);
+                        "Parse error: mismatched in square lens, expecting", lsqr,
+                        "but got", rsqr);
                 goto error;
             }
-
-            FREE(square);
             get_combine(rec_state, lens, 1);
         } else {
             parse_combine(rec_state, lens, 1);
@@ -1080,7 +1230,14 @@ static void visit_exit(struct lens *lens,
     } else {
         top_frame(rec_state)->lens = lens;
     }
+    /* pop the ast from one level if parent is not null */
+    if (rec_state->ast != NULL && rec_state->ast->parent != NULL)
+        rec_state->ast = rec_state->ast->parent;
  error:
+    if (rsqr != NULL)
+        FREE(rsqr);
+    if (lsqr != NULL)
+        FREE(lsqr);
     return;
 }
 
@@ -1148,17 +1305,20 @@ static struct frame *rec_process(enum mode_t mode, struct lens *lens,
         goto error;
     }
 
+    if (debugging("cf.get.ast"))
+        print_ast(rec_state.ast, 0);
+
  done:
     state->regs = old_regs;
     state->nreg = old_nreg;
     jmt_free_parse(visitor.parse);
+    free_ast(rec_state.ast);
     return rec_state.frames;
  error:
 
     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);
@@ -1318,10 +1478,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/info.c b/src/info.c
index fc5a17d..1095967 100644
--- a/src/info.c
+++ b/src/info.c
@@ -104,6 +104,8 @@ void print_info(FILE *out, struct info *info) {
                     info->last_line, info->last_column);
         }
     }
+    if (info->error != NULL && info->error->details != NULL)
+        fprintf(out, "\n%s\n", info->error->details);
 }
 
 void free_info(struct info *info) {
diff --git a/src/info.h b/src/info.h
index 1f38cdc..a08c15c 100644
--- a/src/info.h
+++ b/src/info.h
@@ -26,6 +26,7 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include "errcode.h"
 #include "ref.h"
 
 /* Reference-counted strings */
diff --git a/src/lens.c b/src/lens.c
index 0134996..c43d4ad 100644
--- a/src/lens.c
+++ b/src/lens.c
@@ -39,53 +39,56 @@ static const int const type_offs[] = {
     offsetof(struct lens, ktype),
     offsetof(struct lens, vtype)
 };
-static const int ntypes = sizeof(type_offs)/sizeof(type_offs[0]);
+static const int ntypes = sizeof(type_offs) / sizeof(type_offs[0]);
 
-static const char *lens_type_names[] =
-    { "ctype", "atype", "ktype", "vtype" };
+static const char *lens_type_names[] = { "ctype", "atype", "ktype", "vtype" };
 
 #define ltype(lns, t) *((struct regexp **) ((char *) lns + type_offs[t]))
 
-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_iter(struct info *info, struct lens *l);
-static struct value *typecheck_maybe(struct info *info, struct lens *l);
+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);
 
 /* Lens names for pretty printing */
 /* keep order in sync with enum type */
-static const char *const tags[] = {
-    "del", "store", "value", "key", "label", "seq", "counter",
-    "concat", "union",
-    "subtree", "star", "maybe", "rec", "square"
-};
+static const char * const tags[] = { "del", "store", "value", "key", "label",
+        "seq", "counter", "concat", "union", "subtree", "star", "maybe", "rec",
+        "square" };
 
 #define ltag(lens) (tags[lens->tag - L_DEL])
 
-static const struct string digits_string = {
-    .ref = REF_MAX, .str = (char *) "[0123456789]+"
-};
-static const struct string *const digits_pat = &digits_string;
+static const struct string digits_string = { .ref = REF_MAX, .str =
+        (char *) "[0123456789]+" };
+static const struct string * const digits_pat = &digits_string;
 
-char *format_lens(struct lens *l) {
+char *
+format_lens(struct lens *l) {
     char *inf = format_info(l->info);
     char *result;
 
     xasprintf(&result, "%s[%s]%s", tags[l->tag - L_DEL], inf,
-              l->recursive ? "R" : "r");
+            l->recursive ? "R" : "r");
     free(inf);
     return result;
 }
 
 #define BUG_LENS_TAG(lns)  bug_lens_tag(lns, __FILE__, __LINE__)
 
-static void bug_lens_tag(struct lens *lens, const char *file, int lineno) {
+static void
+bug_lens_tag(struct lens *lens, const char *file, int lineno) {
     char *s = format_lens(lens);
 
     if (lens != NULL && lens->info != NULL && lens->info->error != NULL) {
         bug_on(lens->info->error, file, lineno, "Unexpected lens tag %s", s);
-    } else {
+    }
+    else {
         /* We are really screwed */
         assert(0);
     }
@@ -98,8 +101,8 @@ static void bug_lens_tag(struct lens *lens, const char *file, int lineno) {
  * Return NULL if REGEXP is valid, if the regexp REGEXP has syntax errors,
  * return an exception.
  */
-static struct value *str_to_fa(struct info *info, const char *pattern,
-                               struct fa **fa, int nocase) {
+static struct value *
+str_to_fa(struct info *info, const char *pattern, struct fa **fa, int nocase) {
     int error;
     struct value *exn = NULL;
     size_t re_err_len;
@@ -127,22 +130,22 @@ static struct value *str_to_fa(struct info *info, const char *pattern,
     regerror(error, NULL, re_err, re_err_len);
     exn_printf_line(exn, "%s", re_err);
 
- done:
-    free(re_str);
+    done: free(re_str);
     free(re_err);
     return exn;
- error:
-    fa_free(*fa);
+    error: fa_free(*fa);
     *fa = NULL;
     exn = info->error->exn;
     goto done;
 }
 
-static struct value *regexp_to_fa(struct regexp *regexp, struct fa **fa) {
+static struct value *
+regexp_to_fa(struct regexp *regexp, struct fa **fa) {
     return str_to_fa(regexp->info, regexp->pattern->str, fa, regexp->nocase);
 }
 
-static struct lens *make_lens(enum lens_tag tag, struct info *info) {
+static struct lens *
+make_lens(enum lens_tag tag, struct info *info) {
     struct lens *lens;
     make_ref(lens);
     lens->tag = tag;
@@ -151,8 +154,8 @@ static struct lens *make_lens(enum lens_tag tag, struct info *info) {
     return lens;
 }
 
-static struct lens *make_lens_unop(enum lens_tag tag, struct info *info,
-                                  struct lens *child) {
+static struct lens *
+make_lens_unop(enum lens_tag tag, struct info *info, struct lens *child) {
     struct lens *lens = make_lens(tag, info);
     lens->child = child;
     lens->value = child->value;
@@ -160,11 +163,12 @@ static struct lens *make_lens_unop(enum lens_tag tag, struct info *info,
     return lens;
 }
 
-typedef struct regexp *regexp_combinator(struct info *, int, struct regexp **);
+typedef struct regexp *
+regexp_combinator(struct info *, int, struct regexp **);
 
-static struct lens *make_lens_binop(enum lens_tag tag, struct info *info,
-                                    struct lens *l1, struct lens *l2,
-                                    regexp_combinator *combinator) {
+static struct lens *
+make_lens_binop(enum lens_tag tag, struct info *info, struct lens *l1,
+        struct lens *l2, regexp_combinator *combinator) {
     struct lens *lens = make_lens(tag, info);
     int n1 = (l1->tag == tag) ? l1->nchildren : 1;
     struct regexp **types = NULL;
@@ -184,22 +188,24 @@ static struct lens *make_lens_binop(enum lens_tag tag, struct info *info,
     }
 
     if (l1->tag == tag) {
-        for (int i=0; i < l1->nchildren; i++)
+        for (int i = 0; i < l1->nchildren; i++)
             lens->children[i] = ref(l1->children[i]);
         unref(l1, lens);
-    } else {
+    }
+    else {
         lens->children[0] = l1;
     }
 
     if (l2->tag == tag) {
-        for (int i=0; i < l2->nchildren; i++)
+        for (int i = 0; i < l2->nchildren; i++)
             lens->children[n1 + i] = ref(l2->children[i]);
         unref(l2, lens);
-    } else {
+    }
+    else {
         lens->children[n1] = l2;
     }
 
-    for (int i=0; i < lens->nchildren; i++) {
+    for (int i = 0; i < lens->nchildren; i++) {
         lens->value = lens->value || lens->children[i]->value;
         lens->key = lens->key || lens->children[i]->key;
     }
@@ -207,38 +213,39 @@ static struct lens *make_lens_binop(enum lens_tag tag, struct info *info,
     if (ALLOC_N(types, lens->nchildren) < 0)
         goto error;
 
-    if (! lens->rec_internal) {
+    if (!lens->rec_internal) {
         /* Inside a recursive lens, we assign types with lns_check_rec
          * once we know the entire lens */
-        for (int t=0; t < ntypes; t++) {
+        for (int t = 0; t < ntypes; t++) {
             if (lens->recursive && t == CTYPE)
                 continue;
-            for (int i=0; i < lens->nchildren; i++)
+            for (int i = 0; i < lens->nchildren; i++)
                 types[i] = ltype(lens->children[i], t);
             ltype(lens, t) = (*combinator)(info, lens->nchildren, types);
         }
     }
     FREE(types);
 
-    for (int i=0; i < lens->nchildren; i++)
+    for (int i = 0; i < lens->nchildren; i++)
         ensure(tag != lens->children[i]->tag, lens->info);
 
     return lens;
- error:
+    error:
     unref(lens, lens);
     FREE(types);
     return NULL;
 }
 
-static struct value *make_lens_value(struct lens *lens) {
+static struct value *
+make_lens_value(struct lens *lens) {
     struct value *v;
     v = make_value(V_LENS, ref(lens->info));
     v->lens = lens;
     return v;
 }
 
-struct value *lns_make_union(struct info *info,
-                             struct lens *l1, struct lens *l2, int check) {
+struct value *
+lns_make_union(struct info *info, struct lens *l1, struct lens *l2, int check) {
     struct lens *lens = NULL;
     int consumes_value = l1->consumes_value && l2->consumes_value;
     int recursive = l1->recursive || l2->recursive;
@@ -252,13 +259,13 @@ struct value *lns_make_union(struct info *info,
 
     lens = make_lens_binop(L_UNION, info, l1, l2, regexp_union_n);
     lens->consumes_value = consumes_value;
-    if (! recursive)
+    if (!recursive)
         lens->ctype_nullable = ctype_nullable;
     return make_lens_value(lens);
 }
 
-struct value *lns_make_concat(struct info *info,
-                              struct lens *l1, struct lens *l2, int check) {
+struct value *
+lns_make_concat(struct info *info, struct lens *l1, struct lens *l2, int check) {
     struct lens *lens = NULL;
     int consumes_value = l1->consumes_value || l2->consumes_value;
     int recursive = l1->recursive || l2->recursive;
@@ -278,14 +285,13 @@ struct value *lns_make_concat(struct info *info,
 
     lens = make_lens_binop(L_CONCAT, info, l1, l2, regexp_concat_n);
     lens->consumes_value = consumes_value;
-    if (! recursive)
+    if (!recursive)
         lens->ctype_nullable = ctype_nullable;
     return make_lens_value(lens);
 }
 
-static struct regexp *subtree_atype(struct info *info,
-                                    struct regexp *ktype,
-                                    struct regexp *vtype) {
+static struct regexp *
+subtree_atype(struct info *info, struct regexp *ktype, struct regexp *vtype) {
     const char *kpat = (ktype == NULL) ? ENC_NULL : ktype->pattern->str;
     const char *vpat = (vtype == NULL) ? ENC_NULL : vtype->pattern->str;
     char *pat;
@@ -300,7 +306,8 @@ static struct regexp *subtree_atype(struct info *info,
         if (asprintf(&pat, "(%s)%s(%s)%s", ks, ENC_EQ, vs, ENC_SLASH) < 0)
             ERR_NOMEM(true, info);
         nocase = 0;
-    } else {
+    }
+    else {
         if (asprintf(&pat, "(%s)%s(%s)%s", kpat, ENC_EQ, vpat, ENC_SLASH) < 0)
             ERR_NOMEM(pat == NULL, info);
 
@@ -311,8 +318,7 @@ static struct regexp *subtree_atype(struct info *info,
             nocase = vtype->nocase;
     }
     result = make_regexp(info, pat, nocase);
- error:
-    free(ks);
+    error: free(ks);
     free(vs);
     return result;
 }
@@ -327,22 +333,24 @@ static struct regexp *subtree_atype(struct info *info,
  * l1->ktype = NULL
  * l1->vtype = NULL
  */
-struct value *lns_make_subtree(struct info *info, struct lens *l) {
+struct value *
+lns_make_subtree(struct info *info, struct lens *l) {
     struct lens *lens;
 
     lens = make_lens_unop(L_SUBTREE, info, l);
     lens->ctype = ref(l->ctype);
-    if (! l->recursive)
+    if (!l->recursive)
         lens->atype = subtree_atype(info, l->ktype, l->vtype);
     lens->value = lens->key = 0;
     lens->recursive = l->recursive;
     lens->rec_internal = l->rec_internal;
-    if (! l->recursive)
+    if (!l->recursive)
         lens->ctype_nullable = l->ctype_nullable;
     return make_lens_value(lens);
 }
 
-struct value *lns_make_star(struct info *info, struct lens *l, int check) {
+struct value *
+lns_make_star(struct info *info, struct lens *l, int check) {
     struct lens *lens;
 
     if (check) {
@@ -367,7 +375,8 @@ struct value *lns_make_star(struct info *info, struct lens *l, int check) {
     return make_lens_value(lens);
 }
 
-struct value *lns_make_plus(struct info *info, struct lens *l, int check) {
+struct value *
+lns_make_plus(struct info *info, struct lens *l, int check) {
     struct value *star, *conc;
 
     star = lns_make_star(info, l, check);
@@ -379,7 +388,8 @@ struct value *lns_make_plus(struct info *info, struct lens *l, int check) {
     return conc;
 }
 
-struct value *lns_make_maybe(struct info *info, struct lens *l, int check) {
+struct value *
+lns_make_maybe(struct info *info, struct lens *l, int check) {
     struct lens *lens;
 
     if (check) {
@@ -388,7 +398,7 @@ struct value *lns_make_maybe(struct info *info, struct lens *l, int check) {
             return exn;
     }
     lens = make_lens_unop(L_MAYBE, info, l);
-    for (int t=0; t < ntypes; t++)
+    for (int t = 0; t < ntypes; t++)
         ltype(lens, t) = regexp_maybe(info, ltype(l, t));
     lens->value = l->value;
     lens->key = l->key;
@@ -403,30 +413,28 @@ struct value *lns_make_maybe(struct info *info, struct lens *l, int check) {
  * where MATCHED is whatever the key lens matched (the inability to express
  * this with other lenses makes the square primitve 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);
+    /* do not increase ref of copies */
+    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;
@@ -434,7 +442,7 @@ struct value *lns_make_square(struct info *info,
     sqr = make_lens_unop(L_SQUARE, ref(info), ref(cnt2->lens));
     ERR_NOMEM(sqr == NULL, info);
 
-    for (int t=0; t < ntypes; t++)
+    for (int t = 0; t < ntypes; t++)
         ltype(sqr, t) = ref(ltype(cnt2->lens, t));
     sqr->recursive = cnt2->lens->recursive;
     sqr->rec_internal = cnt2->lens->rec_internal;
@@ -444,13 +452,11 @@ struct value *lns_make_square(struct info *info,
     ERR_NOMEM(res == NULL, info);
     sqr = NULL;
 
- error:
+    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);
@@ -461,8 +467,8 @@ struct value *lns_make_square(struct info *info,
  * Lens primitives
  */
 
-static struct regexp *make_regexp_from_string(struct info *info,
-                                              struct string *string) {
+static struct regexp *
+make_regexp_from_string(struct info *info, struct string *string) {
     struct regexp *r;
     make_ref(r);
     if (r != NULL) {
@@ -473,15 +479,15 @@ static struct regexp *make_regexp_from_string(struct info *info,
     return r;
 }
 
-static struct regexp *restrict_regexp(struct regexp *r) {
+static struct regexp *
+restrict_regexp(struct regexp *r) {
     char *nre = NULL;
     struct regexp *result = NULL;
     size_t nre_len;
     int ret;
 
-    ret = fa_restrict_alphabet(r->pattern->str, strlen(r->pattern->str),
-                               &nre, &nre_len,
-                               RESERVED_FROM, RESERVED_TO);
+    ret = fa_restrict_alphabet(r->pattern->str, strlen(r->pattern->str), &nre,
+            &nre_len, RESERVED_FROM, RESERVED_TO);
     ERR_NOMEM(ret == REG_ESPACE || ret < 0, r->info);
     BUG_ON(ret != 0, r->info, NULL);
     ensure(nre_len == strlen(nre), r->info);
@@ -492,17 +498,17 @@ static struct regexp *restrict_regexp(struct regexp *r) {
     result = make_regexp(r->info, nre, r->nocase);
     nre = NULL;
     BUG_ON(regexp_compile(result) != 0, r->info,
-           "Could not compile restricted regexp");
- done:
-    free(nre);
+            "Could not compile restricted regexp");
+    done: free(nre);
     return result;
- error:
+    error:
     unref(result, regexp);
     goto done;
 }
 
-struct value *lns_make_prim(enum lens_tag tag, struct info *info,
-                            struct regexp *regexp, struct string *string) {
+struct value *
+lns_make_prim(enum lens_tag tag, struct info *info, struct regexp *regexp,
+        struct string *string) {
     struct lens *lens = NULL;
     struct value *exn = NULL;
     struct fa *fa_slash = NULL;
@@ -520,24 +526,24 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info,
             goto error;
 
         fa_isect = fa_intersect(fa_slash, fa_key);
-        if (! fa_is_basic(fa_isect, FA_EMPTY)) {
-            exn = make_exn_value(info,
-                                 "The key regexp /%s/ matches a '/'",
-                                 regexp->pattern->str);
+        if (!fa_is_basic(fa_isect, FA_EMPTY)) {
+            exn = make_exn_value(info, "The key regexp /%s/ matches a '/'",
+                    regexp->pattern->str);
             goto error;
         }
         fa_free(fa_isect);
         fa_free(fa_key);
         fa_free(fa_slash);
         fa_isect = fa_key = fa_slash = NULL;
-    } else if (tag == L_LABEL) {
+    }
+    else if (tag == L_LABEL) {
         if (strchr(string->str, SEP) != NULL) {
-            exn = make_exn_value(info,
-                                 "The label string \"%s\" contains a '/'",
-                                 string->str);
+            exn = make_exn_value(info, "The label string \"%s\" contains a '/'",
+                    string->str);
             goto error;
         }
-    } else if (tag == L_DEL && string != NULL) {
+    }
+    else if (tag == L_DEL && string != NULL) {
         int cnt;
         const char *dflt = string->str;
         cnt = regexp_match(regexp, dflt, strlen(dflt), 0, NULL);
@@ -545,8 +551,7 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info,
             char *s = escape(dflt, -1, RX_ESCAPES);
             char *r = regexp_escape(regexp);
             exn = make_exn_value(info,
-                   "del: the default value '%s' does not match /%s/",
-                   s, r);
+                    "del: the default value '%s' does not match /%s/", s, r);
             FREE(s);
             FREE(r);
             goto error;
@@ -565,25 +570,28 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info,
     if (tag == L_DEL || tag == L_STORE || tag == L_KEY) {
         lens->ctype = ref(regexp);
         lens->ctype_nullable = regexp_matches_empty(lens->ctype);
-    } else if (tag == L_LABEL || tag == L_VALUE
-               || tag == L_SEQ || tag == L_COUNTER) {
+    }
+    else if (tag == L_LABEL || tag == L_VALUE || tag == L_SEQ
+            || tag == L_COUNTER) {
         lens->ctype = regexp_make_empty(info);
         lens->ctype_nullable = 1;
-    } else {
+    }
+    else {
         BUG_LENS_TAG(lens);
         goto error;
     }
 
-
     /* Set the ktype */
     if (tag == L_SEQ) {
-        lens->ktype =
-            make_regexp_from_string(info, (struct string *) digits_pat);
+        lens->ktype = make_regexp_from_string(info,
+                (struct string *) digits_pat);
         if (lens->ktype == NULL)
             goto error;
-    } else if (tag == L_KEY) {
+    }
+    else if (tag == L_KEY) {
         lens->ktype = restrict_regexp(lens->regexp);
-    } else if (tag == L_LABEL) {
+    }
+    else if (tag == L_LABEL) {
         lens->ktype = make_regexp_literal(info, lens->string->str);
         if (lens->ktype == NULL)
             goto error;
@@ -592,13 +600,13 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info,
     /* Set the vtype */
     if (tag == L_STORE) {
         lens->vtype = restrict_regexp(lens->regexp);
-    } else if (tag == L_VALUE) {
+    }
+    else if (tag == L_VALUE) {
         lens->vtype = make_regexp_literal(info, lens->string->str);
     }
 
     return make_lens_value(lens);
- error:
-    fa_free(fa_isect);
+    error: fa_free(fa_isect);
     fa_free(fa_key);
     fa_free(fa_slash);
     return exn;
@@ -607,13 +615,14 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info,
 /*
  * Typechecking of lenses
  */
-static struct value *disjoint_check(struct info *info, bool is_get,
-                                    struct regexp *r1, struct regexp *r2) {
+static struct value *
+disjoint_check(struct info *info, bool is_get, struct regexp *r1,
+        struct regexp *r2) {
     struct fa *fa1 = NULL;
     struct fa *fa2 = NULL;
     struct fa *fa = NULL;
     struct value *exn = NULL;
-    const char *const msg = is_get ? "union.get" : "tree union.put";
+    const char * const msg = is_get ? "union.get" : "tree union.put";
 
     if (r1 == NULL || r2 == NULL)
         return NULL;
@@ -627,19 +636,18 @@ static struct value *disjoint_check(struct info *info, bool is_get,
         goto done;
 
     fa = fa_intersect(fa1, fa2);
-    if (! fa_is_basic(fa, FA_EMPTY)) {
+    if (!fa_is_basic(fa, FA_EMPTY)) {
         size_t xmpl_len;
         char *xmpl;
         fa_example(fa, &xmpl, &xmpl_len);
-        if (! is_get) {
+        if (!is_get) {
             char *fmt = enc_format(xmpl, xmpl_len);
             if (fmt != NULL) {
                 FREE(xmpl);
                 xmpl = fmt;
             }
         }
-        exn = make_exn_value(ref(info),
-                             "overlapping lenses in %s", msg);
+        exn = make_exn_value(ref(info), "overlapping lenses in %s", msg);
 
         if (is_get)
             exn_printf_line(exn, "Example matched by both: '%s'", xmpl);
@@ -648,16 +656,15 @@ static struct value *disjoint_check(struct info *info, bool is_get,
         free(xmpl);
     }
 
- done:
-    fa_free(fa);
+    done: fa_free(fa);
     fa_free(fa1);
     fa_free(fa2);
 
     return exn;
 }
 
-static struct value *typecheck_union(struct info *info,
-                                     struct lens *l1, struct lens *l2) {
+static struct value *
+typecheck_union(struct info *info, struct lens *l1, struct lens *l2) {
     struct value *exn = NULL;
 
     exn = disjoint_check(info, true, l1->ctype, l2->ctype);
@@ -678,8 +685,8 @@ static struct value *typecheck_union(struct info *info,
 
 static struct value *
 ambig_check(struct info *info, struct fa *fa1, struct fa *fa2,
-            enum lens_type typ,  struct lens *l1, struct lens *l2,
-            const char *msg, bool iterated) {
+        enum lens_type typ, struct lens *l1, struct lens *l2, const char *msg,
+        bool iterated) {
     char *upv, *pv, *v;
     size_t upv_len;
     struct value *exn = NULL;
@@ -690,7 +697,8 @@ ambig_check(struct info *info, struct fa *fa1, struct fa *fa2,
         exn = make_exn_value(ref(info), "not enough memory");
         if (exn != NULL) {
             return exn;
-        } else {
+        }
+        else {
             ERR_REPORT(info, AUG_ENOMEM, NULL);
             return info->error->exn;
         }
@@ -708,7 +716,8 @@ ambig_check(struct info *info, struct fa *fa1, struct fa *fa2,
             e_v = enc_format(v, strlen(v));
             lns_format_atype(l1, &s1);
             lns_format_atype(l2, &s2);
-        } else {
+        }
+        else {
             e_u = escape(upv, pv - upv, RX_ESCAPES);
             e_up = escape(upv, v - upv, RX_ESCAPES);
             e_upv = escape(upv, -1, RX_ESCAPES);
@@ -720,7 +729,8 @@ ambig_check(struct info *info, struct fa *fa1, struct fa *fa2,
         exn = make_exn_value(ref(info), "%s", msg);
         if (iterated) {
             exn_printf_line(exn, "  Iterated regexp: /%s/", s1);
-        } else {
+        }
+        else {
             exn_printf_line(exn, "  First regexp: /%s/", s1);
             exn_printf_line(exn, "  Second regexp: /%s/", s2);
         }
@@ -741,8 +751,8 @@ ambig_check(struct info *info, struct fa *fa1, struct fa *fa2,
 }
 
 static struct value *
-ambig_concat_check(struct info *info, const char *msg,
-                   enum lens_type typ, struct lens *l1, struct lens *l2) {
+ambig_concat_check(struct info *info, const char *msg, enum lens_type typ,
+        struct lens *l1, struct lens *l2) {
     struct fa *fa1 = NULL;
     struct fa *fa2 = NULL;
     struct value *result = NULL;
@@ -761,21 +771,19 @@ ambig_concat_check(struct info *info, const char *msg,
         goto done;
 
     result = ambig_check(info, fa1, fa2, typ, l1, l2, msg, false);
- done:
-    fa_free(fa1);
+    done: fa_free(fa1);
     fa_free(fa2);
     return result;
 }
 
-static struct value *typecheck_concat(struct info *info,
-                                      struct lens *l1, struct lens *l2) {
+static struct value *
+typecheck_concat(struct info *info, struct lens *l1, struct lens *l2) {
     struct value *result = NULL;
 
-    result = ambig_concat_check(info, "ambiguous concatenation",
-                                CTYPE, l1, l2);
+    result = ambig_concat_check(info, "ambiguous concatenation", CTYPE, l1, l2);
     if (result == NULL) {
-        result = ambig_concat_check(info, "ambiguous tree concatenation",
-                                    ATYPE, l1, l2);
+        result = ambig_concat_check(info, "ambiguous tree concatenation", ATYPE,
+                l1, l2);
     }
     if (result != NULL) {
         char *fi = format_info(l1->info);
@@ -789,8 +797,76 @@ static struct value *typecheck_concat(struct info *info,
 }
 
 static struct value *
-ambig_iter_check(struct info *info, const char *msg,
-                 enum lens_type typ, struct lens *l) {
+make_exn_square(struct info *info, struct lens *l1, struct lens *l2,
+        const char *msg) {
+
+    struct value *exn = make_exn_value(ref(info), "%s",
+            "Inconsistency in lens square");
+    exn_printf_line(exn, "%s", msg);
+    char *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) {
     struct fa *fas = NULL, *fa = NULL;
     struct value *result = NULL;
     struct regexp *r = ltype(l, typ);
@@ -806,13 +882,13 @@ ambig_iter_check(struct info *info, const char *msg,
 
     result = ambig_check(info, fa, fas, typ, l, l, msg, true);
 
- done:
-    fa_free(fa);
+    done: fa_free(fa);
     fa_free(fas);
     return result;
 }
 
-static struct value *typecheck_iter(struct info *info, struct lens *l) {
+static struct value *
+typecheck_iter(struct info *info, struct lens *l) {
     struct value *result = NULL;
 
     result = ambig_iter_check(info, "ambiguous iteration", CTYPE, l);
@@ -827,7 +903,8 @@ static struct value *typecheck_iter(struct info *info, struct lens *l) {
     return result;
 }
 
-static struct value *typecheck_maybe(struct info *info, struct lens *l) {
+static struct value *
+typecheck_maybe(struct info *info, struct lens *l) {
     /* Check (r)? as (<e>|r) where <e> is the empty language */
     struct value *exn = NULL;
 
@@ -838,21 +915,23 @@ static struct value *typecheck_maybe(struct info *info, struct lens *l) {
     }
 
     /* Typecheck the put direction; the check passes if
-       (1) the atype does not match the empty string, because we can tell
-           from looking at tree nodes whether L should be applied or not
-       (2) L handles a value; with that, we know whether to apply L or not
-           depending on whether the current node has a non NULL value or not
-    */
-    if (exn == NULL && ! l->consumes_value) {
+     (1) the atype does not match the empty string, because we can tell
+     from looking at tree nodes whether L should be applied or not
+     (2) L handles a value; with that, we know whether to apply L or not
+     depending on whether the current node has a non NULL value or not
+     */
+    if (exn == NULL && !l->consumes_value) {
         if (l->atype != NULL && regexp_matches_empty(l->atype)) {
-            exn = make_exn_value(ref(info),
-               "optional expression matches the empty tree but does not consume a value");
+            exn =
+                    make_exn_value(ref(info),
+                            "optional expression matches the empty tree but does not consume a value");
         }
     }
     return exn;
 }
 
-void free_lens(struct lens *lens) {
+void
+free_lens(struct lens *lens) {
     if (lens == NULL)
         return;
     ensure(lens->ref == 0, lens->info);
@@ -882,7 +961,7 @@ void free_lens(struct lens *lens) {
         break;
     case L_CONCAT:
     case L_UNION:
-        for (int i=0; i < lens->nchildren; i++)
+        for (int i = 0; i < lens->nchildren; i++)
             unref(lens->children[i], lens);
         free(lens->children);
         break;
@@ -896,33 +975,32 @@ void free_lens(struct lens *lens) {
         break;
     }
 
-    for (int t=0; t < ntypes; t++)
+    for (int t = 0; t < ntypes; t++)
         unref(ltype(lens, t), regexp);
 
     unref(lens->info, info);
     jmt_free(lens->jmt);
     free(lens);
- error:
-    return;
+    error: return;
 }
 
-void lens_release(struct lens *lens) {
+void
+lens_release(struct lens *lens) {
     if (lens == NULL)
         return;
 
-    for (int t=0; t < ntypes; t++)
+    for (int t = 0; t < ntypes; t++)
         regexp_release(ltype(lens, t));
 
     if (lens->tag == L_KEY || lens->tag == L_STORE)
         regexp_release(lens->regexp);
 
-    if (lens->tag == L_SUBTREE || lens->tag == L_STAR
-        || lens->tag == L_MAYBE) {
+    if (lens->tag == L_SUBTREE || lens->tag == L_STAR || lens->tag == L_MAYBE) {
         lens_release(lens->child);
     }
 
     if (lens->tag == L_UNION || lens->tag == L_CONCAT) {
-        for (int i=0; i < lens->nchildren; i++) {
+        for (int i = 0; i < lens->nchildren; i++) {
             lens_release(lens->children[i]);
         }
     }
@@ -934,20 +1012,21 @@ void lens_release(struct lens *lens) {
 /*
  * Encoding of tree levels
  */
-char *enc_format(const char *e, size_t len) {
+char *
+enc_format(const char *e, size_t len) {
     size_t size = 0;
     char *result = NULL, *r;
     const char *k = e;
 
     while (*k && k - e < len) {
-        char *eq,  *slash, *v;
+        char *eq, *slash, *v;
         eq = strchr(k, ENC_EQ_CH);
         assert(eq != NULL);
         slash = strchr(eq, ENC_SLASH_CH);
         assert(slash != NULL);
         v = eq + 1;
 
-        size += 6;     /* Surrounding braces */
+        size += 6; /* Surrounding braces */
         if (k != eq)
             size += 1 + (eq - k) + 1;
         if (v != slash)
@@ -960,7 +1039,7 @@ char *enc_format(const char *e, size_t len) {
     k = e;
     r = result;
     while (*k && k - e < len) {
-        char *eq,  *slash, *v;
+        char *eq, *slash, *v;
         eq = strchr(k, ENC_EQ_CH);
         slash = strchr(eq, ENC_SLASH_CH);
         assert(eq != NULL && slash != NULL);
@@ -973,7 +1052,7 @@ char *enc_format(const char *e, size_t len) {
             r = stpcpy(r, "\"");
         }
         if (v != slash) {
-            r = stpcpy (r, " = \"");
+            r = stpcpy(r, " = \"");
             r = stpncpy(r, v, slash - v);
             r = stpcpy(r, "\"");
         }
@@ -983,7 +1062,8 @@ char *enc_format(const char *e, size_t len) {
     return result;
 }
 
-static int lns_format_subtree_atype(struct lens *l, char **buf) {
+static int
+lns_format_subtree_atype(struct lens *l, char **buf) {
     char *k = NULL, *v = NULL;
     const struct regexp *ktype = l->child->ktype;
     const struct regexp *vtype = l->child->vtype;
@@ -1002,7 +1082,8 @@ static int lns_format_subtree_atype(struct lens *l, char **buf) {
             r = xasprintf(buf, "{ = /%s/ }", k, v);
         else
             r = xasprintf(buf, "{ /%s/ = /%s/ }", k, v);
-    } else {
+    }
+    else {
         if (k == NULL)
             r = xasprintf(buf, "{ }", k);
         else
@@ -1012,13 +1093,14 @@ static int lns_format_subtree_atype(struct lens *l, char **buf) {
         goto done;
 
     result = 0;
- done:
+    done:
     FREE(v);
     FREE(k);
     return result;
 }
 
-static int lns_format_rep_atype(struct lens *l, char **buf, char quant) {
+static int
+lns_format_rep_atype(struct lens *l, char **buf, char quant) {
     char *a = NULL;
     int r, result = -1;
 
@@ -1041,12 +1123,13 @@ static int lns_format_rep_atype(struct lens *l, char **buf, char quant) {
         goto done;
 
     result = 0;
- done:
+    done:
     FREE(a);
     return result;
 }
 
-static int lns_format_concat_atype(struct lens *l, char **buf) {
+static int
+lns_format_concat_atype(struct lens *l, char **buf) {
     char **c = NULL, *s = NULL, *p;
     int r, result = -1;
     size_t len = 0, nconc = 0;
@@ -1054,8 +1137,8 @@ static int lns_format_concat_atype(struct lens *l, char **buf) {
     if (ALLOC_N(c, l->nchildren) < 0)
         goto done;
 
-    for (int i=0; i < l->nchildren; i++) {
-        r = lns_format_atype(l->children[i], c+i);
+    for (int i = 0; i < l->nchildren; i++) {
+        r = lns_format_atype(l->children[i], c + i);
         if (r < 0)
             goto done;
         len += strlen(c[i]) + 2;
@@ -1068,7 +1151,7 @@ static int lns_format_concat_atype(struct lens *l, char **buf) {
     if (ALLOC_N(s, len+1) < 0)
         goto done;
     p = s;
-    for (int i=0; i < l->nchildren; i++) {
+    for (int i = 0; i < l->nchildren; i++) {
         bool needs_parens = nconc > 1 && l->children[i]->tag == L_UNION;
         if (strlen(c[i]) == 0)
             continue;
@@ -1082,16 +1165,16 @@ static int lns_format_concat_atype(struct lens *l, char **buf) {
     *buf = s;
     s = NULL;
     result = 0;
- done:
-    if (c != NULL)
-        for (int i=0; i < l->nchildren; i++)
+    done: if (c != NULL)
+        for (int i = 0; i < l->nchildren; i++)
             FREE(c[i]);
     FREE(c);
     FREE(s);
     return result;
 }
 
-static int lns_format_union_atype(struct lens *l, char **buf) {
+static int
+lns_format_union_atype(struct lens *l, char **buf) {
     char **c = NULL, *s = NULL, *p;
     int r, result = -1;
     size_t len = 0;
@@ -1099,8 +1182,8 @@ static int lns_format_union_atype(struct lens *l, char **buf) {
     if (ALLOC_N(c, l->nchildren) < 0)
         goto done;
 
-    for (int i=0; i < l->nchildren; i++) {
-        r = lns_format_atype(l->children[i], c+i);
+    for (int i = 0; i < l->nchildren; i++) {
+        r = lns_format_atype(l->children[i], c + i);
         if (r < 0)
             goto done;
         len += strlen(c[i]) + 2;
@@ -1111,7 +1194,7 @@ static int lns_format_union_atype(struct lens *l, char **buf) {
         goto done;
 
     p = s;
-    for (int i=0; i < l->nchildren; i++) {
+    for (int i = 0; i < l->nchildren; i++) {
         if (i > 0)
             p = stpcpy(p, " | ");
         if (strlen(c[i]) == 0)
@@ -1122,16 +1205,16 @@ static int lns_format_union_atype(struct lens *l, char **buf) {
     *buf = s;
     s = NULL;
     result = 0;
- done:
-    if (c != NULL)
-        for (int i=0; i < l->nchildren; i++)
+    done: if (c != NULL)
+        for (int i = 0; i < l->nchildren; i++)
             FREE(c[i]);
     FREE(c);
     FREE(s);
     return result;
 }
 
-static int lns_format_rec_atype(struct lens *l, char **buf) {
+static int
+lns_format_rec_atype(struct lens *l, char **buf) {
     int r;
 
     if (l->rec_internal) {
@@ -1148,10 +1231,11 @@ static int lns_format_rec_atype(struct lens *l, char **buf) {
     return (r < 0) ? -1 : 0;
 }
 
-int lns_format_atype(struct lens *l, char **buf) {
+int
+lns_format_atype(struct lens *l, char **buf) {
     *buf = NULL;
 
-    switch(l->tag) {
+    switch (l->tag) {
     case L_DEL:
     case L_STORE:
     case L_KEY:
@@ -1193,7 +1277,8 @@ int lns_format_atype(struct lens *l, char **buf) {
 /*
  * Recursive lenses
  */
-struct value *lns_make_rec(struct info *info) {
+struct value *
+lns_make_rec(struct info *info) {
     struct lens *l = make_lens(L_REC, info);
     l->recursive = 1;
     l->rec_internal = 1;
@@ -1254,21 +1339,21 @@ struct value *lns_make_rec(struct info *info) {
  * nonterminals in, we remember the production for the nonterminal in PROD.
  */
 struct trans {
-    struct state  *to;
-    struct lens   *lens;
+    struct state *to;
+    struct lens *lens;
     struct regexp *re;
 };
 
 struct state {
-    struct state  *next;   /* Linked list for memory management */
-    size_t         ntrans;
-    struct trans  *trans;
+    struct state *next; /* Linked list for memory management */
+    size_t ntrans;
+    struct trans *trans;
 };
 
 /* Productions for lens LENS. Start state START and end state END. If we
-   start with START, END is the only accepting state. */
+ start with START, END is the only accepting state. */
 struct prod {
-    struct lens  *lens;
+    struct lens *lens;
     struct state *start;
     struct state *end;
 };
@@ -1277,48 +1362,52 @@ struct prod {
  * to the types */
 struct rtn {
     struct info *info;
-    size_t        nprod;
+    size_t nprod;
     struct prod **prod;
-    struct state *states;  /* Linked list through next of all states in all
-                              prods; the states for each production are on
-                              the part of the list from prod->start to
-                              prod->end */
+    struct state *states; /* Linked list through next of all states in all
+     prods; the states for each production are on
+     the part of the list from prod->start to
+     prod->end */
     struct value *exn;
     enum lens_type lens_type;
-    unsigned int check : 1;
+    unsigned int check :1;
 };
 
 #define RTN_BAIL(rtn) if ((rtn)->exn != NULL ||                     \
                           (rtn)->info->error->code != AUG_NOERROR)  \
                          goto error;
 
-static void free_prod(struct prod *prod) {
+static void
+free_prod(struct prod *prod) {
     if (prod == NULL)
         return;
     unref(prod->lens, lens);
     free(prod);
 }
 
-static void free_rtn(struct rtn *rtn) {
+static void
+free_rtn(struct rtn *rtn) {
     if (rtn == NULL)
         return;
-    for (int i=0; i < rtn->nprod; i++)
+    for (int i = 0; i < rtn->nprod; i++)
         free_prod(rtn->prod[i]);
     free(rtn->prod);
-    list_for_each(s, rtn->states) {
+    list_for_each(s, rtn->states)
+{
         for (int i=0; i < s->ntrans; i++) {
             unref(s->trans[i].lens, lens);
             unref(s->trans[i].re, regexp);
         }
         free(s->trans);
     }
-    list_free(rtn->states);
+        list_free(rtn->states);
     unref(rtn->info, info);
     unref(rtn->exn, value);
     free(rtn);
 }
 
-static struct state *add_state(struct prod *prod) {
+static struct state *
+add_state(struct prod *prod) {
     struct state *result = NULL;
     int r;
 
@@ -1326,16 +1415,16 @@ static struct state *add_state(struct prod *prod) {
     ERR_NOMEM(r < 0, prod->lens->info);
 
     list_cons(prod->start->next, result);
- error:
-    return result;
+    error: return result;
 }
 
-static struct trans *add_trans(struct rtn *rtn, struct state *state,
-                               struct state *to, struct lens *l) {
+static struct trans *
+add_trans(struct rtn *rtn, struct state *state, struct state *to,
+        struct lens *l) {
     int r;
     struct trans *result = NULL;
 
-    for (int i=0; i < state->ntrans; i++)
+    for (int i = 0; i < state->ntrans; i++)
         if (state->trans[i].to == to && state->trans[i].lens == l)
             return state->trans + i;
 
@@ -1351,11 +1440,11 @@ static struct trans *add_trans(struct rtn *rtn, struct state *state,
         result->lens = ref(l);
         result->re = ref(ltype(l, rtn->lens_type));
     }
- error:
-    return result;
+    error: return result;
 }
 
-static struct prod *make_prod(struct rtn *rtn, struct lens *l) {
+static struct prod *
+make_prod(struct rtn *rtn, struct lens *l) {
     struct prod *result = NULL;
     int r;
 
@@ -1373,22 +1462,23 @@ static struct prod *make_prod(struct rtn *rtn, struct lens *l) {
     rtn->states = result->start;
 
     return result;
- error:
-    free_prod(result);
+    error: free_prod(result);
     return NULL;
 }
 
-static struct prod *prod_for_lens(struct rtn *rtn, struct lens *l) {
+static struct prod *
+prod_for_lens(struct rtn *rtn, struct lens *l) {
     if (l == NULL)
         return NULL;
-    for (int i=0; i < rtn->nprod; i++) {
+    for (int i = 0; i < rtn->nprod; i++) {
         if (rtn->prod[i]->lens == l)
             return rtn->prod[i];
     }
     return NULL;
 }
 
-static void rtn_dot(struct rtn *rtn, const char *stage) {
+static void
+rtn_dot(struct rtn *rtn, const char *stage) {
     FILE *fp;
     int r = 0;
 
@@ -1397,12 +1487,14 @@ static void rtn_dot(struct rtn *rtn, const char *stage) {
         return;
 
     fprintf(fp, "digraph \"l1\" {\n  rankdir=LR;\n");
-    list_for_each(s, rtn->states) {
+    list_for_each(s, rtn->states)
+    {
         char *label = NULL;
         for (int p=0; p < rtn->nprod; p++) {
             if (s == rtn->prod[p]->start) {
                 r = xasprintf(&label, "s%d", p);
-            } else if (s == rtn->prod[p]->end) {
+            }
+            else if (s == rtn->prod[p]->end) {
                 r = xasprintf(&label, "e%d", p);
             }
             ERR_NOMEM(r < 0, rtn->info);
@@ -1418,22 +1510,22 @@ static void rtn_dot(struct rtn *rtn, const char *stage) {
             if (s->trans[i].re != NULL) {
                 label = regexp_escape(s->trans[i].re);
                 for (char *t = label; *t; t++)
-                    if (*t == '\\')
-                        *t = '~';
+                if (*t == '\\')
+                *t = '~';
                 fprintf(fp, " [ label = \"%s\" ]", label);
                 FREE(label);
             }
             fprintf(fp, ";\n");
         }
     }
- error:
-    fprintf(fp, "}\n");
+    error: fprintf(fp, "}\n");
     fclose(fp);
 }
 
 /* Add transitions to RTN corresponding to cfg(l, N) */
-static void rtn_rules(struct rtn *rtn, struct lens *l) {
-    if (! l->recursive)
+static void
+rtn_rules(struct rtn *rtn, struct lens *l) {
+    if (!l->recursive)
         return;
 
     struct prod *prod = prod_for_lens(rtn, l);
@@ -1443,7 +1535,7 @@ static void rtn_rules(struct rtn *rtn, struct lens *l) {
     int r = REALLOC_N(rtn->prod, rtn->nprod+1);
     ERR_NOMEM(r < 0, l->info);
 
-    prod =  make_prod(rtn, l);
+    prod = make_prod(rtn, l);
     rtn->prod[rtn->nprod] = prod;
     RTN_BAIL(rtn);
     rtn->nprod += 1;
@@ -1453,7 +1545,7 @@ static void rtn_rules(struct rtn *rtn, struct lens *l) {
     switch (l->tag) {
     case L_UNION:
         /* cfg(l1|..|ln, N) -> N := N1 | N2 | ... | Nn */
-        for (int i=0; i < l->nchildren; i++) {
+        for (int i = 0; i < l->nchildren; i++) {
             add_trans(rtn, start, prod->end, l->children[i]);
             RTN_BAIL(rtn);
             rtn_rules(rtn, l->children[i]);
@@ -1462,7 +1554,7 @@ static void rtn_rules(struct rtn *rtn, struct lens *l) {
         break;
     case L_CONCAT:
         /* cfg(l1 . l2 ... ln, N) -> N := N1 . N2 ... Nn */
-        for (int i=0; i < l->nchildren-1; i++) {
+        for (int i = 0; i < l->nchildren - 1; i++) {
             struct state *s = add_state(prod);
             RTN_BAIL(rtn);
             add_trans(rtn, start, s, l->children[i]);
@@ -1536,45 +1628,49 @@ 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;
     }
- error:
-    return;
+    error: return;
 }
 
 /* Replace transition t with two epsilon transitions s => p->start and
  * p->end => s->trans[i].to where s is the start of t. Instead of adding
  * epsilon transitions, we expand the epsilon transitions.
  */
-static void prod_splice(struct rtn *rtn,
-                        struct prod *from, struct prod *to, struct trans *t) {
+static void
+prod_splice(struct rtn *rtn, struct prod *from, struct prod *to,
+        struct trans *t) {
 
     add_trans(rtn, to->end, t->to, NULL);
     ERR_BAIL(from->lens->info);
     t->to = to->start;
     unref(t->re, regexp);
 
- error:
-    return;
+    error: return;
 }
 
-static void rtn_splice(struct rtn *rtn, struct prod *prod) {
+static void
+rtn_splice(struct rtn *rtn, struct prod *prod) {
     for (struct state *s = prod->start; s != prod->end; s = s->next) {
-        for (int i=0; i < s->ntrans; i++) {
+        for (int i = 0; i < s->ntrans; i++) {
             struct prod *p = prod_for_lens(rtn, s->trans[i].lens);
             if (p != NULL) {
-                prod_splice(rtn, prod, p, s->trans+i);
+                prod_splice(rtn, prod, p, s->trans + i);
                 RTN_BAIL(rtn);
             }
         }
     }
- error:
-    return;
+    error: return;
 }
 
-static struct rtn *rtn_build(struct lens *rec, enum lens_type lt) {
+static struct rtn *
+rtn_build(struct lens *rec, enum lens_type lt) {
     int r;
     struct rtn *rtn;
 
@@ -1589,19 +1685,19 @@ static struct rtn *rtn_build(struct lens *rec, enum lens_type lt) {
     if (debugging("cf.approx"))
         rtn_dot(rtn, "10-rules");
 
-    for (int i=0; i < rtn->nprod; i++) {
+    for (int i = 0; i < rtn->nprod; i++) {
         rtn_splice(rtn, rtn->prod[i]);
         RTN_BAIL(rtn);
     }
     if (debugging("cf.approx"))
         rtn_dot(rtn, "11-splice");
 
- error:
-    return rtn;
+    error: return rtn;
 }
 
 /* Compare transitions lexicographically by (to, lens) */
-static int trans_to_cmp(const void *v1, const void *v2) {
+static int
+trans_to_cmp(const void *v1, const void *v2) {
     const struct trans *t1 = v1;
     const struct trans *t2 = v2;
 
@@ -1618,15 +1714,14 @@ static int trans_to_cmp(const void *v1, const void *v2) {
  * existing transition S1 -> S2. If LOOP is NULL or R3 does not exist,
  * label the transition with a simplified regexp by treating NULL as
  * epsilon */
-static void collapse_trans(struct rtn *rtn,
-                           struct state *s1, struct state *s2,
-                           struct regexp *r1, struct regexp *loop,
-                           struct regexp *r2) {
+static void
+collapse_trans(struct rtn *rtn, struct state *s1, struct state *s2,
+        struct regexp *r1, struct regexp *loop, struct regexp *r2) {
 
     struct trans *t = NULL;
     struct regexp *r = NULL;
 
-    for (int i=0; i < s1->ntrans; i++) {
+    for (int i = 0; i < s1->ntrans; i++) {
         if (s1->trans[i].to == s2) {
             t = s1->trans + i;
             break;
@@ -1641,14 +1736,16 @@ static void collapse_trans(struct rtn *rtn,
             r = ref(r1);
         else
             r = regexp_concat(rtn->info, r1, r2);
-    } else {
+    }
+    else {
         struct regexp *s = regexp_iter(rtn->info, loop, 0, -1);
         ERR_NOMEM(s == NULL, rtn->info);
         struct regexp *c = NULL;
         if (r1 == NULL) {
             c = s;
             s = NULL;
-        } else {
+        }
+        else {
             c = regexp_concat(rtn->info, r1, s);
             unref(s, regexp);
             ERR_NOMEM(c == NULL, rtn->info);
@@ -1656,7 +1753,8 @@ static void collapse_trans(struct rtn *rtn,
         if (r2 == NULL) {
             r = c;
             c = NULL;
-        } else {
+        }
+        else {
             r = regexp_concat(rtn->info, c, r2);
             unref(c, regexp);
             ERR_NOMEM(r == NULL, rtn->info);
@@ -1667,7 +1765,8 @@ static void collapse_trans(struct rtn *rtn,
         t = add_trans(rtn, s1, s2, NULL);
         ERR_NOMEM(t == NULL, rtn->info);
         t->re = r;
-    } else if (t->re == NULL) {
+    }
+    else if (t->re == NULL) {
         if (r == NULL || regexp_matches_empty(r))
             t->re = r;
         else {
@@ -1675,14 +1774,16 @@ static void collapse_trans(struct rtn *rtn,
             unref(r, regexp);
             ERR_NOMEM(t->re == NULL, rtn->info);
         }
-    } else if (r == NULL) {
+    }
+    else if (r == NULL) {
         if (!regexp_matches_empty(t->re)) {
             r = regexp_maybe(rtn->info, t->re);
             unref(t->re, regexp);
             t->re = r;
             ERR_NOMEM(r == NULL, rtn->info);
         }
-    } else {
+    }
+    else {
         struct regexp *u = regexp_union(rtn->info, r, t->re);
         unref(r, regexp);
         unref(t->re, regexp);
@@ -1691,8 +1792,7 @@ static void collapse_trans(struct rtn *rtn,
     }
 
     return;
- error:
-    rtn->exn = rtn->info->error->exn;
+    error: rtn->exn = rtn->info->error->exn;
     return;
 }
 
@@ -1703,37 +1803,39 @@ static void collapse_trans(struct rtn *rtn,
  *
  * This is the same algorithm as fa_as_regexp in fa.c
  */
-static struct regexp *rtn_reduce(struct rtn *rtn, struct lens *rec) {
+static struct regexp *
+rtn_reduce(struct rtn *rtn, struct lens *rec) {
     struct prod *prod = prod_for_lens(rtn, rec);
     int r;
 
     ERR_THROW(prod == NULL, rtn->info, AUG_EINTERNAL,
-              "No production for recursive lens");
+            "No production for recursive lens");
 
     /* Eliminate epsilon transitions and turn transitions between the same
      * two states into a regexp union */
-    list_for_each(s, rtn->states) {
+    list_for_each(s, rtn->states)
+    {
         qsort(s->trans, s->ntrans, sizeof(*s->trans), trans_to_cmp);
         for (int i=0; i < s->ntrans; i++) {
             int j = i+1;
             for (;j < s->ntrans && s->trans[i].to == s->trans[j].to;
-                 j++);
+                    j++);
             if (j > i+1) {
                 struct regexp *u, **v;
                 r = ALLOC_N(v, j - i);
                 ERR_NOMEM(r < 0, rtn->info);
                 for (int k=i; k < j; k++)
-                    v[k-i] = s->trans[k].re;
+                v[k-i] = s->trans[k].re;
                 u = regexp_union_n(rtn->info, j - i, v);
                 if (u == NULL) {
                     // FIXME: The calling convention for regexp_union_n
                     // is bad, since we can't distinguish between alloc
                     // failure and unioning all NULL's
                     for (int k=0; k < j-i; k++)
-                        if (v[k] != NULL) {
-                            FREE(v);
-                            ERR_NOMEM(true, rtn->info);
-                        }
+                    if (v[k] != NULL) {
+                        FREE(v);
+                        ERR_NOMEM(true, rtn->info);
+                    }
                 }
                 FREE(v);
                 for (int k=i; k < j; k++) {
@@ -1781,9 +1883,10 @@ static struct regexp *rtn_reduce(struct rtn *rtn, struct lens *rec) {
      *        set the regexp on the transition S1 -> S2 to
      *          R1 . (LOOP)* . R2 | R3 */
     // FIXME: This does not go over all states
-    list_for_each(s, rtn->states) {
+    list_for_each(s, rtn->states)
+    {
         if (s == prod->end || s == prod->start)
-            continue;
+        continue;
         struct regexp *loop = NULL;
         for (int i=0; i < s->ntrans; i++) {
             if (s == s->trans[i].to) {
@@ -1793,16 +1896,16 @@ static struct regexp *rtn_reduce(struct rtn *rtn, struct lens *rec) {
         }
         list_for_each(s1, rtn->states) {
             if (s == s1)
-                continue;
+            continue;
             for (int t1=0; t1 < s1->ntrans; t1++) {
                 if (s == s1->trans[t1].to) {
                     for (int t2=0; t2 < s->ntrans; t2++) {
                         struct state *s2 = s->trans[t2].to;
                         if (s2 == s)
-                            continue;
+                        continue;
                         collapse_trans(rtn, s1, s2,
-                                       s1->trans[t1].re, loop,
-                                       s->trans[t2].re);
+                                s1->trans[t1].re, loop,
+                                s->trans[t2].re);
                         RTN_BAIL(rtn);
                     }
                 }
@@ -1812,29 +1915,29 @@ static struct regexp *rtn_reduce(struct rtn *rtn, struct lens *rec) {
 
     /* Find the overall regexp */
     struct regexp *result = NULL;
-    for (int i=0; i < prod->start->ntrans; i++) {
+    for (int i = 0; i < prod->start->ntrans; i++) {
         if (prod->start->trans[i].to == prod->end) {
             ensure(result == NULL, rtn->info);
             result = ref(prod->start->trans[i].re);
         }
     }
     return result;
- error:
-    return NULL;
+    error: return NULL;
 }
 
-static void propagate_type(struct lens *l, enum lens_type lt) {
+static void
+propagate_type(struct lens *l, enum lens_type lt) {
     struct regexp **types = NULL;
     int r;
 
-    if (! l->recursive || ltype(l, lt) != NULL)
+    if (!l->recursive || ltype(l, lt) != NULL)
         return;
 
-    switch(l->tag) {
+    switch (l->tag) {
     case L_CONCAT:
         r = ALLOC_N(types, l->nchildren);
         ERR_NOMEM(r < 0, l->info);
-        for (int i=0; i < l->nchildren; i++) {
+        for (int i = 0; i < l->nchildren; i++) {
             propagate_type(l->children[i], lt);
             types[i] = ltype(l->children[i], lt);
         }
@@ -1844,7 +1947,7 @@ static void propagate_type(struct lens *l, enum lens_type lt) {
     case L_UNION:
         r = ALLOC_N(types, l->nchildren);
         ERR_NOMEM(r < 0, l->info);
-        for (int i=0; i < l->nchildren; i++) {
+        for (int i = 0; i < l->nchildren; i++) {
             propagate_type(l->children[i], lt);
             types[i] = ltype(l->children[i], lt);
         }
@@ -1878,46 +1981,48 @@ static void propagate_type(struct lens *l, enum lens_type lt) {
         break;
     }
 
- error:
+    error:
     FREE(types);
 }
 
-static struct value *typecheck(struct lens *l, int check);
+static struct value *
+typecheck(struct lens *l, int check);
 
-typedef struct value *typecheck_n_make(struct info *,
-                                       struct lens *, struct lens *, int);
+typedef struct value *
+typecheck_n_make(struct info *, struct lens *, struct lens *, int);
 
-static struct info *merge_info(struct info *i1, struct info *i2) {
+static struct info *
+merge_info(struct info *i1, struct info *i2) {
     struct info *info;
-    make_ref(info);
-    ERR_NOMEM(info == NULL, i1);
+make_ref(info);
+        ERR_NOMEM(info == NULL, i1);
 
     info->filename = ref(i1->filename);
     info->first_line = i1->first_line;
     info->first_column = i1->first_column;
-    info->last_line    = i2->last_line;
-    info->last_column  = i2->last_column;
-    info->error        = i1->error;
+    info->last_line = i2->last_line;
+    info->last_column = i2->last_column;
+    info->error = i1->error;
     return info;
 
- error:
+    error:
     unref(info, info);
     return NULL;
 }
 
-static struct value *typecheck_n(struct lens *l,
-                                 typecheck_n_make *make, int check) {
+static struct value *
+typecheck_n(struct lens *l, typecheck_n_make *make, int check) {
     struct value *exn = NULL;
     struct lens *acc = NULL;
 
     ensure(l->tag == L_CONCAT || l->tag == L_UNION, l->info);
-    for (int i=0; i < l->nchildren; i++) {
+    for (int i = 0; i < l->nchildren; i++) {
         exn = typecheck(l->children[i], check);
         if (exn != NULL)
             goto error;
     }
     acc = ref(l->children[0]);
-    for (int i=1; i < l->nchildren; i++) {
+    for (int i = 1; i < l->nchildren; i++) {
         struct info *info = merge_info(acc->info, l->children[i]->info);
         ERR_BAIL(acc->info);
         exn = (*make)(info, acc, ref(l->children[i]), check);
@@ -1929,19 +2034,20 @@ static struct value *typecheck_n(struct lens *l,
     }
     l->value = acc->value;
     l->key = acc->key;
- error:
+    error:
     unref(acc, lens);
     return exn;
 }
 
-static struct value *typecheck(struct lens *l, int check) {
+static struct value *
+typecheck(struct lens *l, int check) {
     struct value *exn = NULL;
 
     /* Nonrecursive lenses are typechecked at build time */
-    if (! l->recursive)
+    if (!l->recursive)
         return NULL;
 
-    switch(l->tag) {
+    switch (l->tag) {
     case L_CONCAT:
         exn = typecheck_n(l, lns_make_concat, check);
         break;
@@ -1977,7 +2083,8 @@ static struct value *typecheck(struct lens *l, int check) {
     return exn;
 }
 
-static struct value *rtn_approx(struct lens *rec, enum lens_type lt) {
+static struct value *
+rtn_approx(struct lens *rec, enum lens_type lt) {
     struct rtn *rtn = NULL;
     struct value *result = NULL;
 
@@ -1991,8 +2098,7 @@ static struct value *rtn_approx(struct lens *rec, enum lens_type lt) {
     propagate_type(rec->body, lt);
     ERR_BAIL(rec->info);
 
- done:
-    free_rtn(rtn);
+    done: free_rtn(rtn);
 
     if (debugging("cf.approx")) {
         printf("approx %s  => ", lens_type_names[lt]);
@@ -2001,8 +2107,7 @@ static struct value *rtn_approx(struct lens *rec, enum lens_type lt) {
     }
 
     return result;
- error:
-    if (rtn->exn == NULL)
+    error: if (rtn->exn == NULL)
         result = rec->info->error->exn;
     else
         result = ref(rtn->exn);
@@ -2010,13 +2115,12 @@ static struct value *rtn_approx(struct lens *rec, enum lens_type lt) {
 }
 
 static struct value *
-exn_multiple_epsilons(struct lens *lens,
-                      struct lens *l1, struct lens *l2) {
+exn_multiple_epsilons(struct lens *lens, struct lens *l1, struct lens *l2) {
     char *fi = NULL;
     struct value *exn = NULL;
 
     exn = make_exn_value(ref(lens->info),
-                         "more than one nullable branch in a union");
+            "more than one nullable branch in a union");
     fi = format_info(l1->info);
     exn_printf_line(exn, "First nullable lens: %s", fi);
     FREE(fi);
@@ -2030,32 +2134,33 @@ exn_multiple_epsilons(struct lens *lens,
 
 /* Update lens->ctype_nullable and return 1 if there was a change,
  * 0 if there was none */
-static int ctype_nullable(struct lens *lens, struct value **exn) {
+static int
+ctype_nullable(struct lens *lens, struct value **exn) {
     int nullable = 0;
     int ret = 0;
     struct lens *null_lens = NULL;
 
-    if (! lens->recursive)
+    if (!lens->recursive)
         return 0;
 
-    switch(lens->tag) {
+    switch (lens->tag) {
     case L_CONCAT:
         nullable = 1;
-        for (int i=0; i < lens->nchildren; i++) {
+        for (int i = 0; i < lens->nchildren; i++) {
             if (ctype_nullable(lens->children[i], exn))
                 ret = 1;
-            if (! lens->children[i]->ctype_nullable)
+            if (!lens->children[i]->ctype_nullable)
                 nullable = 0;
         }
         break;
     case L_UNION:
-        for (int i=0; i < lens->nchildren; i++) {
+        for (int i = 0; i < lens->nchildren; i++) {
             if (ctype_nullable(lens->children[i], exn))
                 ret = 1;
             if (lens->children[i]->ctype_nullable) {
                 if (nullable) {
                     *exn = exn_multiple_epsilons(lens, null_lens,
-                                                 lens->children[i]);
+                            lens->children[i]);
                     return 0;
                 }
                 nullable = 1;
@@ -2088,9 +2193,8 @@ static int ctype_nullable(struct lens *lens, struct value **exn) {
     return ret;
 }
 
-struct value *lns_check_rec(struct info *info,
-                            struct lens *body, struct lens *rec,
-                            int check) {
+struct value *
+lns_check_rec(struct info *info, struct lens *body, struct lens *rec, int check) {
     /* The types in the order of approximation */
     static const enum lens_type types[] = { KTYPE, VTYPE, ATYPE };
     struct value *result = NULL;
@@ -2099,7 +2203,7 @@ struct value *lns_check_rec(struct info *info,
     ensure(rec->rec_internal, info);
 
     /* The user might have written down a regular lens with 'let rec' */
-    if (! body->recursive) {
+    if (!body->recursive) {
         result = make_lens_value(ref(body));
         ERR_NOMEM(result == NULL, info);
         return result;
@@ -2113,17 +2217,17 @@ struct value *lns_check_rec(struct info *info,
      * The internal instance of the recursive lens is REC, the external one
      * is TOP, constructed below
      */
-    rec->body = body;                          /* REC does not own BODY */
+    rec->body = body; /* REC does not own BODY */
 
-    for (int i=0; i < ARRAY_CARDINALITY(types); i++) {
+    for (int i = 0; i < ARRAY_CARDINALITY(types); i++) {
         result = rtn_approx(rec, types[i]);
         ERR_BAIL(info);
     }
 
     if (rec->atype == NULL) {
         result = make_exn_value(ref(rec->info),
-        "recursive lens generates the empty language for its %s",
-         rec->ctype == NULL ? "ctype" : "atype");
+                "recursive lens generates the empty language for its %s",
+                rec->ctype == NULL ? "ctype" : "atype");
         goto error;
     }
 
@@ -2131,7 +2235,8 @@ struct value *lns_check_rec(struct info *info,
     rec->value = rec->body->value;
     rec->consumes_value = rec->body->consumes_value;
 
-    while(ctype_nullable(rec->body, &result));
+    while (ctype_nullable(rec->body, &result))
+        ;
     if (result != NULL)
         goto error;
     rec->ctype_nullable = rec->body->ctype_nullable;
@@ -2142,7 +2247,7 @@ struct value *lns_check_rec(struct info *info,
 
     result = lns_make_rec(ref(rec->info));
     struct lens *top = result->lens;
-    for (int t=0; t < ntypes; t++)
+    for (int t = 0; t < ntypes; t++)
         ltype(top, t) = ref(ltype(rec, t));
     top->value = rec->value;
     top->key = rec->key;
@@ -2157,8 +2262,7 @@ struct value *lns_check_rec(struct info *info,
     ERR_BAIL(info);
 
     return result;
- error:
-    if (result != NULL && result->tag != V_EXN)
+    error: if (result != NULL && result->tag != V_EXN)
         unref(result, value);
     if (result == NULL)
         result = info->error->exn;
@@ -2166,7 +2270,8 @@ struct value *lns_check_rec(struct info *info,
 }
 
 #if ENABLE_DEBUG
-void dump_lens_tree(struct lens *lens){
+void
+dump_lens_tree(struct lens *lens) {
     static int count = 0;
     FILE *fp;
 
@@ -2181,27 +2286,29 @@ void dump_lens_tree(struct lens *lens){
     fclose(fp);
 }
 
-void dump_lens(FILE *out, struct lens *lens){
+void
+dump_lens(FILE *out, struct lens *lens) {
     int i = 0;
     struct regexp *re;
 
     fprintf(out, "\"%p\" [ shape = box, label = \"%s\\n", lens, ltag(lens));
 
-    for (int t=0; t < ntypes; t++) {
+    for (int t = 0; t < ntypes; t++) {
         re = ltype(lens, t);
         if (re == NULL)
             continue;
-        fprintf(out, "%s=",lens_type_names[t]);
+        fprintf(out, "%s=", lens_type_names[t]);
         print_regexp(out, re);
         fprintf(out, "\\n");
     }
 
+    fprintf(out, "ref=%x\\n", lens->ref);
     fprintf(out, "recursive=%x\\n", lens->recursive);
     fprintf(out, "rec_internal=%x\\n", lens->rec_internal);
     fprintf(out, "consumes_value=%x\\n", lens->consumes_value);
     fprintf(out, "ctype_nullable=%x\\n", lens->ctype_nullable);
     fprintf(out, "\"];\n");
-    switch(lens->tag){
+    switch (lens->tag) {
     case L_DEL:
         break;
     case L_STORE:
@@ -2217,13 +2324,13 @@ void dump_lens(FILE *out, struct lens *lens){
     case L_COUNTER:
         break;
     case L_CONCAT:
-        for(i = 0; i<lens->nchildren;i++){
+        for (i = 0; i < lens->nchildren; i++) {
             fprintf(out, "\"%p\" -> \"%p\"\n", lens, lens->children[i]);
             dump_lens(out, lens->children[i]);
         }
         break;
     case L_UNION:
-        for(i = 0; i<lens->nchildren;i++){
+        for (i = 0; i < lens->nchildren; i++) {
             fprintf(out, "\"%p\" -> \"%p\"\n", lens, lens->children[i]);
             dump_lens(out, lens->children[i]);
         }
@@ -2243,7 +2350,7 @@ void dump_lens(FILE *out, struct lens *lens){
 
         break;
     case L_REC:
-        if (lens->rec_internal == 0){
+        if (lens->rec_internal == 0) {
             fprintf(out, "\"%p\" -> \"%p\"\n", lens, lens->child);
             dump_lens(out, lens->body);
         }
diff --git a/src/lens.h b/src/lens.h
index 62c2f77..c77a198 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,8 @@ 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);
@@ -163,11 +160,10 @@ struct value *lns_check_rec(struct info *info,
 struct skel {
     struct skel *next;
     enum lens_tag tag;
-    union {
+    struct { /* L_SQUARE uses both, L_SUBTREE sets both to NULL */
         char        *text;    /* L_DEL */
         struct skel *skels;   /* L_CONCAT, L_STAR */
     };
-    /* Also tag == L_SUBTREE, with no data in the union */
 };
 
 struct lns_error {
@@ -241,6 +237,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..a2a7660 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,33 @@ 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,11 +685,10 @@ 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);
     }
 
 }
@@ -703,6 +726,33 @@ 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 +827,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);
diff --git a/tests/modules/fail_square_consistency.aug b/tests/modules/fail_square_consistency.aug
new file mode 100644
index 0000000..b1cc3db
--- /dev/null
+++ b/tests/modules/fail_square_consistency.aug
@@ -0,0 +1,6 @@
+module Fail_square_consistency =
+
+let left = key "a"
+let right = del "b" "b"
+let body = del "x" "x"
+let s = square left body right
diff --git a/tests/modules/fail_square_consistency_del.aug b/tests/modules/fail_square_consistency_del.aug
new file mode 100644
index 0000000..bff74e8
--- /dev/null
+++ b/tests/modules/fail_square_consistency_del.aug
@@ -0,0 +1,6 @@
+module Fail_square_consistency_del =
+
+let left = del /[ab]/ "a"
+let right = del /[ab]/ "b"
+let body = del "x" "x"
+let s = square left body right
diff --git a/tests/modules/fail_square_dup_key.aug b/tests/modules/fail_square_dup_key.aug
new file mode 100644
index 0000000..0e540da
--- /dev/null
+++ b/tests/modules/fail_square_dup_key.aug
@@ -0,0 +1,6 @@
+module Fail_square_dup_key =
+
+let left = key "a"
+let right = del "a" "a"
+let body = key "a"
+let s = square left body right
diff --git a/tests/modules/fail_square_lens_type.aug b/tests/modules/fail_square_lens_type.aug
new file mode 100644
index 0000000..f1099e3
--- /dev/null
+++ b/tests/modules/fail_square_lens_type.aug
@@ -0,0 +1,6 @@
+module Fail_square_lens_type =
+
+let left = [ key "a" ]
+let right = [ key "a" ]
+let body = del "x" "x"
+let s = square left body right
diff --git a/tests/modules/pass_square.aug b/tests/modules/pass_square.aug
index f41761d..fff56e9 100644
--- a/tests/modules/pass_square.aug
+++ b/tests/modules/pass_square.aug
@@ -8,17 +8,24 @@ let dels (s:string) = del s s
  *************************************************************************)
 
 (* Simplest square lens *)
-let s = store /[yz]/
-let sqr0 = [ square "x" s ] *
-test sqr0 get "xyxxyxxyx" = { "x" = "y" }{ "x" = "y" }{ "x" = "y" }
-test sqr0 put "xyx" after set "/x[3]" "z" = "xyxxzx"
+let s = store /[ab]/
+let sqr0 =
+	let k = key "x" in
+	let d = dels "x" in
+	[ square k s d ] *
+test sqr0 get "xaxxbxxax" = { "x" = "a" }{ "x" = "b" }{ "x" = "a" }
+test sqr0 put "xax" after set "/x[3]" "b" = "xaxxbx"
 
 (* test mismatch tag *)
 test sqr0 get "xya" = *
 
 (* Test regular expression matching with multiple groups *)
 let body = del /([f]+)([f]+)/ "ff" . del /([g]+)([g]+)/ "gg"
-let sqr1 = [ square /([a-b]*)([a-b]*)([a-b]*)/ body . del /([x]+)([x]+)/ "xx" ] *
+let sqr1 =
+	let k = key /([a-b]*)([a-b]*)([a-b]*)/ in
+	let d1 = del /([a-b]*)([a-b]*)([a-b]*)/ "a" in
+	let d2 = del /([x]+)([x]+)/ "xx" in
+	[ square k body d1 . d2 ] *
 
 test sqr1 get "aaffggaaxxbbffggbbxx" = { "aa" }{ "bb" }
 test sqr1 get "affggaxx" = { "a" }
@@ -26,10 +33,12 @@ test sqr1 put "affggaxx" after clear "/b" = "affggaxxbffggbxx"
 
 (* Test XML like elements up to depth 2 *)
 let b = del ">" ">" . del /[a-z ]*/ "" . del "</" "</"
-let xml = [ del "<" "<" . square /[a-z]+/ b . del ">" ">" ] *
+let open_tag = key /[a-z]+/
+let close_tag = del /[a-z]+/ "a"
+let xml = [ del "<" "<" . square open_tag b close_tag . del ">" ">" ] *
 
 let b2 = del ">" ">" . xml . del "</" "</"
-let xml2 = [ del "<" "<" . square /[a-z]+/ b2 . del ">" ">" ] *
+let xml2 = [ del "<" "<" . square open_tag b2 close_tag . del ">" ">" ] *
 
 test xml get "<a></a><b></b>" = { "a" }{ "b" }
 
@@ -49,72 +58,30 @@ test xml2 put "<a></a>" after clear "/x/y" = "<a></a><x><y></y></x>"
 (* test nested put of depth 3 : should fail *)
 test xml2 put "<a></a>" after clear "/x/y/z" = *
 
-(************************************************************************
- *                        Recursive square lens
- *************************************************************************)
-
-(* Basic element *)
-let xml_element (body:lens) =
-    let g = del ">" ">" . body . del "</" "</" in
-        [ del "<" "<" . square /[a-z]+/ g . del ">" ">" ] *
-
-let rec xml_rec = xml_element xml_rec
-
-test xml_rec get "<a><b><c><d><e></e></d></c></b></a>" =
-  { "a"
-    { "b"
-      { "c"
-        { "d"
-          { "e" }
-        }
-      }
-    }
-  }
-
-test xml_rec get "<a><b></b><c></c><d></d><e></e></a>" =
-  { "a"
-    { "b" }
-    { "c" }
-    { "d" }
-    { "e" }
-  }
-
-test xml_rec put "<a></a><b><c></c></b>" after clear "/x/y/z" = "<a></a><b><c></c></b><x><y><z></z></y></x>"
-
-(* mismatch tag *)
-test xml_rec get "<a></c>" = *
-test xml_rec get "<a><b></b></c>" = *
-test xml_rec get "<a><b></c></a>" = *
-
-(* test ctype_nullable and typecheck *)
-let rec z = [ square "ab" z? ]
-test z get "abab" = { "ab" }
-
-(* test tip handling when using store inside body *)
-let c (body:lens) =
-    let sto = store "c" . body* in
-        [ square "ab" sto ]
-
-let rec cc = c cc
-
-test cc get "abcabcabab" =
-  { "ab" = "c"
-    { "ab" = "c" }
-  }
-
-(* test correct put behavior *)
-let input3 = "aaxyxbbaaaxyxbb"
-let b3 = dels "y"
-let sqr3 = [ del /[a]*/ "a" . square /[x]/ b3 . del /[b]*/ "b" ]*
-test sqr3 get input3 = { "x" }{ "x" }
-test sqr3 put input3 after clear "/x[1]" = input3
-
-let b4 = del "x" "x"
-let rec sqr4 = [ del /[a]+/ "a" . square /[b]|[c]/ (b4|sqr4) ]
-test sqr4 put "aabaaacxcb" after rm "x" = "aabaaacxcb"
-
 (* matches can be case-insensitive *)
 let s5 = store /[yz]/
-let sqr5 = [ square /x/i s ] *
+let sqr5 =
+	let k = key /x/i in
+	let d = del /x/i "x" in
+	[ square k s5 d ] *
+test sqr5 get "xyX" = { "x" = "y" }
 test sqr5 get "xyXXyxXyx" = { "x" = "y" }{ "X" = "y" }{ "X" = "y" }
 test sqr5 put "xyX" after set "/x[3]" "z" = "xyxxzx"
+
+(* test concat multiple squares *)
+let rex = /[a-z]/
+let csqr =
+	let k = key rex in
+	let d = del rex "a" in
+	let e = dels "" in
+	[ square k e d . square d e d ] *
+
+test csqr get "aabbccdd" = { "a" } { "c" }
+test csqr put "aabb" after insa "z" "/a" = "aabbzzaa"
+
+(* test default square create values *)
+let create_square =
+	let d = dels "a" in
+	[ key "x" . square d d d ]*
+
+test create_square put "" after clear "/x" = "xaaa"
diff --git a/tests/modules/pass_square_rec.aug b/tests/modules/pass_square_rec.aug
new file mode 100644
index 0000000..a8711f6
--- /dev/null
+++ b/tests/modules/pass_square_rec.aug
@@ -0,0 +1,139 @@
+module Pass_square_rec =
+
+(*  Utilities lens *)
+let dels (s:string) = del s s
+
+(************************************************************************
+ *                        Recursive square lens
+ *************************************************************************)
+(* test square with left and right as dels *) 
+let lr (body:lens) =
+    let k = key "c" . body* in
+    let d = dels "ab" in
+        [ square d k d ]
+
+let rec lr2 = lr lr2
+
+test lr2 get "abcabcabab" =
+  { "c"
+    { "c" }
+  }
+
+let open_tag = key /[a-z]+/
+let close_tag = del /[a-z]+/ "a"
+
+(* Basic element *)
+let xml_element (body:lens) =
+    let g = del ">" ">" . body . del "</" "</" in
+        [ del "<" "<" . square open_tag g close_tag . del ">" ">" ] *
+
+let rec xml_rec = xml_element xml_rec
+
+test xml_rec get "<a><b><c><d><e></e></d></c></b></a>" =
+  { "a"
+    { "b"
+      { "c"
+        { "d"
+          { "e" }
+        }
+      }
+    }
+  }
+
+test xml_rec get "<a><b></b><c></c><d></d><e></e></a>" =
+  { "a"
+    { "b" }
+    { "c" }
+    { "d" }
+    { "e" }
+  }
+
+test xml_rec put "<a></a><b><c></c></b>" after clear "/x/y/z" = "<a></a><b><c></c></b><x><y><z></z></y></x>"
+
+(* mismatch tag *)
+test xml_rec get "<a></c>" = *
+test xml_rec get "<a><b></b></c>" = *
+test xml_rec get "<a><b></c></a>" = *
+
+
+(* test ctype_nullable and typecheck *)
+let rec z =
+	let k = key "ab" in
+	let d = dels "ab" in
+	[ square k z? d ]
+test z get "abab" = { "ab" }
+
+(* test tip handling when using store inside body *)
+let c (body:lens) =
+    let sto = store "c" . body* in
+    let d = dels "ab" in
+    let k = key "ab" in
+        [ square k sto d ]
+
+let rec cc = c cc
+
+test cc get "abcabcabab" =
+  { "ab" = "c"
+    { "ab" = "c" }
+  }
+
+(* test mixing regular and recursive lenses *)
+
+let reg1 = 
+	let k = key "y" in
+	let d = dels "y" in
+	let e = dels "" in
+	[ square k e d ]
+
+let reg2 = 
+	let k = key "y" in
+	let d = dels "y" in
+	[ square k reg1 d ]
+	
+let rec rec2 = 
+	let d1 = dels "x" in
+	let k1 = key "x" in
+	let body = reg2 | rec2 in
+	[ square k1 body d1 ]?
+
+test rec2 get "xyyyyx" = 
+  { "x"
+    { "y"
+      { "y" }
+    }
+  }
+
+test rec2 put "" after clear "/x/y/y" = "xyyyyx"
+
+(* test correct put behavior *)
+let input3 = "aaxyxbbaaaxyxbb"
+let b3 = dels "y"
+let sqr3 =
+	let k = key /[x]/ in
+	let d = dels "x" in
+	[ del /[a]*/ "a" . square k b3 d . del /[b]*/ "b" ]*
+test sqr3 get input3 = { "x" }{ "x" }
+test sqr3 put input3 after clear "/x[1]" = input3
+
+let b4 = dels "x"
+let rec sqr4 = 
+	let k = key /[b]|[c]/ in
+	let d = del /[b]|[c]/ "b" in
+	[ del /[a]+/ "a" . square k (b4|sqr4) d ]
+test sqr4 put "aabaaacxcb" after rm "x" = "aabaaacxcb"
+
+(* test concat multiple squares *)
+let rex = /[a-z]/
+let rec csqr =
+	let k = key rex in
+	let d = del rex "a" in
+	let e = dels "" in
+	[ square k e d . csqr* . square d e d ] 
+
+test csqr get "aabbccdd" = 
+  { "a"
+    { "b" }
+  }
+
+test csqr put "aabbccdd" after clear "/a" = "aabbccdd"
+test csqr put "aabb" after clear "/a/z" = "aazzaabb"
-- 
1.7.9.5




More information about the augeas-devel mailing list