Imported Upstream version 1.12.0
[platform/upstream/augeas.git] / src / get.c
index 3aeb277..94b9ba2 100644 (file)
--- a/src/get.c
+++ b/src/get.c
@@ -1,7 +1,7 @@
 /*
  * parser.c: parse a configuration file according to a grammar
  *
- * Copyright (C) 2007-2011 David Lutterkort
+ * Copyright (C) 2007-2016 David Lutterkort
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -50,8 +50,8 @@ 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;
+    int               enable_span;
     /* We use the registers from a regular expression match to keep track
      * of the substring we are currently looking at. REGS are the registers
      * from the last regexp match; NREG is the number of the register
@@ -69,11 +69,17 @@ struct state {
     uint                 nreg;
 };
 
-/* Used by recursive lenses to stack intermediate results */
+/* Used by recursive lenses to stack intermediate results
+   Frames are used for a few different purposes:
+
+   (1) to save results from individual lenses that are later gathered and
+       combined by combinators like L_STAR and L_CONCAT
+   (2) to preserve parse state when descending into a L_SUBTREE
+   (3) as a marker to determine if a L_MAYBE had a match or not
+ */
 struct frame {
     struct lens     *lens;
     char            *key;
-    char            *square;
     struct span     *span;
     union {
         struct { /* MGET */
@@ -90,6 +96,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 +115,10 @@ struct rec_state {
     struct frame        *frames;
     size_t               start;
     uint                 lvl;  /* Debug only */
+    struct ast          *ast;
+    /* Will either be get_combine or parse_combine, depending on MODE, for
+       the duration of the whole recursive parse */
+    void (*combine)(struct rec_state *, struct lens *, uint);
 };
 
 #define REG_START(state) ((state)->regs->start[(state)->nreg])
@@ -109,6 +130,120 @@ struct rec_state {
 #define REG_MATCHED(state) (REG_VALID(state)                            \
                             && (state)->regs->start[(state)->nreg] >= 0)
 
+/* The macros SAVE_REGS and RESTORE_REGS are used to remember and restore
+ * where our caller was in processing their regexp match before we do
+ * matching ourselves (and would therefore clobber the caller's state)
+ *
+ * These two macros are admittedly horrible in that they declare local
+ * variables called OLD_REGS and OLD_NREG. The alternative to avoiding that
+ * would be to manually manage a stack of regs/nreg in STATE.
+ */
+#define SAVE_REGS(state)                                                \
+    struct re_registers *old_regs = state->regs;                        \
+    uint old_nreg = state->nreg;                                        \
+    state->regs = NULL;                                                 \
+    state->nreg = 0
+
+#define RESTORE_REGS(state)                                             \
+    free_regs(state);                                                   \
+    state->regs = old_regs;                                             \
+    state->nreg = old_nreg;
+
+/*
+ * AST utils
+ */
+static struct ast *make_ast(struct lens *lens) {
+    struct ast *ast = NULL;
+
+    if (ALLOC(ast) < 0)
+        return NULL;
+    ast->lens = lens;
+    ast->capacity = 4;
+    if (ALLOC_N(ast->children, ast->capacity) < 0) {
+        FREE(ast);
+        return NULL;
+    }
+    return ast;
+}
+
+/* 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);
+}
+
+static struct ast *ast_root(struct ast *ast) {
+    struct ast *root = ast;
+    while(root != NULL && root->parent != NULL)
+        root = root->parent;
+    return root;
+}
+
+static void ast_set(struct ast *ast, uint start, uint end) {
+    if (ast == NULL)
+        return;
+    ast->start = start;
+    ast->end = end;
+}
+
+/* append a child to the parent ast */
+static struct ast *ast_append(struct rec_state *rec_state, struct lens *lens,
+                           uint start, uint end) {
+    int ret;
+    struct ast *child, *parent;
+    struct state *state = rec_state->state;
+
+    parent = rec_state->ast;
+    if (parent == NULL)
+       return NULL;
+
+    child = make_ast(lens);
+    ERR_NOMEM(child == NULL, state->info);
+
+    ast_set(child, start, end);
+    if (parent->nchildren >= parent->capacity) {
+       ret = REALLOC_N(parent->children, parent->capacity * 2);
+       ERR_NOMEM(ret < 0, state->info);
+       parent->capacity = parent->capacity * 2;
+    }
+    parent->children[parent->nchildren++] = child;
+    child->parent = parent;
+
+    return child;
+ error:
+    free_ast(child);
+    return NULL;
+}
+
+/* pop the ast from one level, fail the parent is NULL */
+static void ast_pop(struct rec_state *rec_state) {
+    ensure(rec_state->ast != NULL && rec_state->ast->parent != NULL, rec_state->state->info);
+    rec_state->ast = rec_state->ast->parent;
+ error:
+    return;
+}
+
+static void print_ast(const struct ast *ast, int lvl) {
+    int i;
+    char *lns;
+    if (ast == NULL)
+        return;
+    for (i = 0; i < lvl; i++) fputs(" ", stdout);
+    lns = format_lens(ast->lens);
+    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);
@@ -128,7 +263,8 @@ static void vget_error(struct state *state, struct lens *lens,
 
     if (state->error != NULL)
         return;
-    CALLOC(state->error, 1);
+    if (ALLOC(state->error) < 0)
+        return;
     state->error->lens = ref(lens);
     if (REG_MATCHED(state))
         state->error->pos  = REG_END(state);
@@ -152,7 +288,8 @@ static void get_error(struct state *state, struct lens *lens,
 static struct skel *make_skel(struct lens *lens) {
     struct skel *skel;
     enum lens_tag tag = lens->tag;
-    CALLOC(skel, 1);
+    if (ALLOC(skel) < 0)
+        return NULL;
     skel->tag = tag;
     return skel;
 }
@@ -257,6 +394,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;
@@ -347,7 +488,8 @@ static struct seq *find_seq(const char *name, struct state *state) {
          seq = seq->next);
 
     if (seq == NULL) {
-        CALLOC(seq, 1);
+        if (ALLOC(seq) < 0)
+            return NULL;
         seq->name = name;
         seq->value = 1;
         list_append(state->seqs, seq);
@@ -393,9 +535,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;
 }
@@ -582,17 +721,99 @@ static struct skel *parse_concat(struct lens *lens, struct state *state,
     return skel;
 }
 
+/* Given a lens that does not match at the current position, try to find
+ * the left-most child LAST that does match and the lens next to it NEXT
+ * that does not match. Return the length of the match.
+ *
+ * If no such child exists, return 0
+ */
+static int try_match(struct lens *lens, struct state *state,
+                     uint start, uint end,
+                     struct lens **last, struct lens **next) {
+    int result = 0, r;
+
+    switch(lens->tag) {
+    case L_VALUE:
+    case L_LABEL:
+    case L_SEQ:
+    case L_COUNTER:
+        *last = lens;
+        return result;
+        break;
+    case L_DEL:
+    case L_KEY:
+    case L_STORE:
+        result = regexp_match(lens->ctype, state->text, end, start, NULL);
+        if (result >= 0)
+            *last = lens;
+        return result;
+    case L_CONCAT:
+        for (int i=0; i < lens->nchildren; i++) {
+            struct lens *child = lens->children[i];
+            struct lens *next_child  =
+                (i < lens->nchildren - 1) ? lens->children[i+1] : NULL;
+
+            r = regexp_match(child->ctype, state->text, end, start, NULL);
+            if (r >= 0) {
+                result += r;
+                start += r;
+                *last = child;
+            } else if (result > 0) {
+                if (*next == NULL)
+                    *next = child;
+                return result;
+            } else {
+                result = try_match(child, state, start, end, last, next);
+                if (result > 0 && *next == NULL)
+                    *next = next_child;
+                return result;
+            }
+        }
+        return result;
+        break;
+    case L_UNION:
+        for (int i=0; i < lens->nchildren; i++) {
+            struct lens *child = lens->children[i];
+            result = try_match(child, state, start, end, last, next);
+            if (result > 0)
+                return result;
+        }
+        return 0;
+        break;
+    case L_SUBTREE:
+    case L_STAR:
+    case L_MAYBE:
+    case L_SQUARE:
+        return try_match(lens->child, state, start, end, last, next);
+        break;
+    default:
+        BUG_ON(true, state->info, "illegal lens tag %d", lens->tag);
+        break;
+    }
+ error:
+    return 0;
+}
+
+static void short_iteration_error(struct lens *lens, struct state *state,
+                                    uint start, uint end) {
+    int match_count;
+
+    get_error(state, lens, "%s", short_iteration);
+
+    match_count = try_match(lens->child, state, start, end,
+                            &state->error->last, &state->error->next);
+    state->error->pos = start + match_count;
+}
+
 static struct tree *get_quant_star(struct lens *lens, struct state *state) {
     ensure0(lens->tag == L_STAR, state->info);
     struct lens *child = lens->child;
     struct tree *tree = NULL, *tail = NULL;
-    struct re_registers *old_regs = state->regs;
-    uint old_nreg = state->nreg;
     uint end = REG_END(state);
     uint start = REG_START(state);
     uint size = end - start;
 
-    state->regs = NULL;
+    SAVE_REGS(state);
     while (size > 0 && match(state, child, child->ctype, end, start) > 0) {
         struct tree *t = NULL;
 
@@ -603,12 +824,9 @@ static struct tree *get_quant_star(struct lens *lens, struct state *state) {
         size -= REG_SIZE(state);
         free_regs(state);
     }
-    free_regs(state);
-    state->regs = old_regs;
-    state->nreg = old_nreg;
+    RESTORE_REGS(state);
     if (size != 0) {
-        get_error(state, lens, "%s", short_iteration);
-        state->error->pos = start;
+        short_iteration_error(lens, state, start, end);
     }
     return tree;
 }
@@ -618,14 +836,12 @@ static struct skel *parse_quant_star(struct lens *lens, struct state *state,
     ensure0(lens->tag == L_STAR, state->info);
     struct lens *child = lens->child;
     struct skel *skel = make_skel(lens), *tail = NULL;
-    struct re_registers *old_regs = state->regs;
-    uint old_nreg = state->nreg;
     uint end = REG_END(state);
     uint start = REG_START(state);
     uint size = end - start;
 
     *dict = NULL;
-    state->regs = NULL;
+    SAVE_REGS(state);
     while (size > 0 && match(state, child, child->ctype, end, start) > 0) {
         struct skel *sk;
         struct dict *di = NULL;
@@ -638,9 +854,7 @@ static struct skel *parse_quant_star(struct lens *lens, struct state *state,
         size -= REG_SIZE(state);
         free_regs(state);
     }
-    free_regs(state);
-    state->regs = old_regs;
-    state->nreg = old_nreg;
+    RESTORE_REGS(state);
     if (size != 0) {
         get_error(state, lens, "%s", short_iteration);
     }
@@ -682,13 +896,13 @@ static struct skel *parse_quant_maybe(struct lens *lens, struct state *state,
 static struct tree *get_subtree(struct lens *lens, struct state *state) {
     char *key = state->key;
     char *value = state->value;
-    struct span *span = state->span;
+    struct span *span = move(state->span);
 
     struct tree *tree = NULL, *children;
 
     state->key = NULL;
     state->value = NULL;
-    if (state->info->flags & AUG_ENABLE_SPAN) {
+    if (state->enable_span) {
         state->span = make_span(state->info);
         ERR_NOMEM(state->span == NULL, state->info);
     }
@@ -696,10 +910,11 @@ static struct tree *get_subtree(struct lens *lens, struct state *state) {
     children = get_lens(lens->child, state);
 
     tree = make_tree(state->key, state->value, NULL, children);
-    tree->span = state->span;
+    ERR_NOMEM(tree == NULL, state->info);
+    tree->span = move(state->span);
 
-    if (state->span != NULL) {
-        update_span(span, state->span->span_start, state->span->span_end);
+    if (tree->span != NULL) {
+        update_span(span, tree->span->span_start, tree->span->span_end);
     }
 
     state->key = key;
@@ -707,6 +922,8 @@ static struct tree *get_subtree(struct lens *lens, struct state *state) {
     state->span = span;
     return tree;
  error:
+    free_span(state->span);
+    state->span = span;
     return NULL;
 }
 
@@ -723,39 +940,104 @@ static struct skel *parse_subtree(struct lens *lens, struct state *state,
     return make_skel(lens);
 }
 
+/* Check if left and right strings matches according to the square lens
+ * definition.
+ *
+ * Returns 1 if strings matches, 0 otherwise
+ */
+static int square_match(struct lens *lens, char *left, char *right) {
+    int cmp = 0;
+    struct lens *concat = NULL;
+
+    // if one of the argument is NULL, returns no match
+    if (left == NULL || right == NULL || lens == NULL)
+        return cmp;
+
+    concat = lens->child;
+    /* If either right or left lens is nocase, then ignore case */
+    if (child_first(concat)->ctype->nocase ||
+            child_last(concat)->ctype->nocase) {
+        cmp = STRCASEEQ(left, right);
+    } else {
+        cmp = STREQ(left, right);
+    }
+    return cmp;
+}
+
+/*
+ * This function applies only for non-recursive lens, handling of recursive
+ * square is done in visit_exit().
+ */
 static struct tree *get_square(struct lens *lens, struct state *state) {
     ensure0(lens->tag == L_SQUARE, state->info);
 
+    struct lens *concat = lens->child;
     struct tree *tree = NULL;
-    char *key = NULL, *square = NULL;
+    uint end = REG_END(state);
+    uint start = REG_START(state);
+    char *rsqr = NULL, *lsqr = NULL;
+    int r;
 
-    // get the child lens
-    tree = get_concat(lens->child, state);
+    SAVE_REGS(state);
+    r = match(state, lens->child, lens->child->ctype, end, start);
+    ERR_NOMEM(r < 0, state->info);
 
-    key = state->key;
-    square = state->square;
-    ensure0(key != NULL, state->info);
-    ensure0(square != NULL, state->info);
+    tree = get_lens(lens->child, state);
 
-    if (strcmp(key, square) != 0) {
+    /* retrieve left component */
+    state->nreg = 1;
+    start = REG_START(state);
+    end = REG_END(state);
+    lsqr = token_range(state->text, start, end);
+
+    /* retrieve right component */
+    /* compute nreg for the last children */
+    for (int i = 0; i < concat->nchildren - 1; i++)
+        state->nreg += 1 + regexp_nsub(concat->children[i]->ctype);
+
+    start = REG_START(state);
+    end = REG_END(state);
+    rsqr = token_range(state->text, start, end);
+
+    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:
+    RESTORE_REGS(state);
+    FREE(lsqr);
+    FREE(rsqr);
     return tree;
+
+ error:
+    free_tree(tree);
+    tree = NULL;
+    goto done;
 }
 
 static struct skel *parse_square(struct lens *lens, struct state *state,
                                  struct dict **dict) {
     ensure0(lens->tag == L_SQUARE, state->info);
-    struct skel *skel, *sk;
+    uint end = REG_END(state);
+    uint start = REG_START(state);
+    struct skel *skel = NULL, *sk = NULL;
+    int r;
 
-    skel = parse_concat(lens->child, state, dict);
+    SAVE_REGS(state);
+    r = match(state, lens->child, lens->child->ctype, end, start);
+    ERR_NOMEM(r < 0, state->info);
+
+    skel = parse_lens(lens->child, state, dict);
+    if (skel == NULL)
+        return NULL;
     sk = make_skel(lens);
     sk->skels = skel;
 
+ error:
+    RESTORE_REGS(state);
     return sk;
 }
 
@@ -768,13 +1050,17 @@ 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 {
             fprintf(stderr, " { %s = %s } ", f->tree->label, f->tree->value);
         }
-        fprintf(stderr, "%s\n", format_lens(f->lens));
+        char *s = format_lens(f->lens);
+        if (s != NULL) {
+            fprintf(stderr, "%s\n", s);
+            free(s);
+        }
     }
 }
 
@@ -787,7 +1073,7 @@ static struct frame *top_frame(struct rec_state *state) {
 /* The nth frame from the top of the stack, where 0th frame is the top */
 ATTRIBUTE_PURE
 static struct frame *nth_frame(struct rec_state *state, uint n) {
-    assert(state->fsize > n);
+    ensure0(state->fsize > n, state->state->info);
     return state->frames + state->fused - (n+1);
 }
 
@@ -816,20 +1102,20 @@ static struct frame *push_frame(struct rec_state *state, struct lens *lens) {
 static struct frame *pop_frame(struct rec_state *state) {
     ensure0(state->fused > 0, state->state->info);
 
+    struct frame *result = top_frame(state);
     state->fused -= 1;
-    if (state->fused > 0)
-        return top_frame(state);
-    else
-        return NULL;
+    return result;
 }
 
 static void dbg_visit(struct lens *lens, char action, size_t start, size_t end,
                       int fused, int lvl) {
-
+    char *lns;
     for (int i=0; i < lvl; i++)
         fputc(' ', stderr);
+    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 +1123,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,
@@ -855,23 +1139,29 @@ static void visit_terminal(struct lens *lens, size_t start, size_t end,
                            void *data) {
     struct rec_state *rec_state = data;
     struct state *state = rec_state->state;
-    struct re_registers *old_regs = state->regs;
-    uint old_nreg = state->nreg;
+    struct ast *child;
 
     if (state->error != NULL)
         return;
 
+    SAVE_REGS(state);
     if (debugging("cf.get"))
         dbg_visit(lens, 'T', start, end, rec_state->fused, rec_state->lvl);
     match(state, lens, lens->ctype, end, start);
     struct frame *top = push_frame(rec_state, lens);
+    ERR_BAIL(state->info);
     if (rec_state->mode == M_GET)
         get_terminal(top, lens, state);
     else
         parse_terminal(top, lens, state);
-    free_regs(state);
-    state->regs = old_regs;
-    state->nreg = old_nreg;
+    child = ast_append(rec_state, lens, start, end);
+    ERR_NOMEM(child == NULL, state->info);
+ error:
+    RESTORE_REGS(state);
+}
+
+static bool rec_gen_span(struct rec_state *rec_state) {
+    return ((rec_state->mode == M_GET) && (rec_state->state->enable_span));
 }
 
 static void visit_enter(struct lens *lens,
@@ -880,6 +1170,7 @@ static void visit_enter(struct lens *lens,
                         void *data) {
     struct rec_state *rec_state = data;
     struct state *state = rec_state->state;
+    struct ast *child;
 
     if (state->error != NULL)
         return;
@@ -889,33 +1180,42 @@ static void visit_enter(struct lens *lens,
     rec_state->lvl += 1;
     if (lens->tag == L_SUBTREE) {
         /* Same for parse and get */
+        /* Use this frame to preserve the current state before we process
+           the contents of the subtree, i.e., lens->child */
         struct frame *f = push_frame(rec_state, lens);
+        ERR_BAIL(state->info);
         f->key = state->key;
         f->value = state->value;
-        f->span = state->span;
         state->key = NULL;
         state->value = NULL;
-    } else if (lens->tag == L_MAYBE) {
-        push_frame(rec_state, lens);
-        if (state->info->flags & AUG_ENABLE_SPAN) {
+        if (rec_gen_span(rec_state)) {
+            f->span = state->span;
             state->span = make_span(state->info);
             ERR_NOMEM(state->span == NULL, state->info);
         }
+    } else if (lens->tag == L_MAYBE) {
+        /* Push a frame as a marker so we can tell whether lens->child
+           actually had a match or not */
+        push_frame(rec_state, lens);
+        ERR_BAIL(state->info);
     }
+    child = ast_append(rec_state, lens, start, end);
+    if (child != NULL)
+        rec_state->ast = child;
  error:
     return;
 }
 
+/* Combine n frames from the stack into one new frame for M_GET */
 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)
-        top = top_frame(rec_state);
-
-    for (int i=0; i < n; i++, top = pop_frame(rec_state)) {
+    for (int i=0; i < n; i++) {
+        top = pop_frame(rec_state);
+        ERR_BAIL(lens->info);
         list_tail_cons(tree, tail, top->tree);
         /* top->tree might have more than one node, update tail */
         if (tail != NULL)
@@ -929,20 +1229,17 @@ static void get_combine(struct rec_state *rec_state,
             ensure(value == NULL, rec_state->state->info);
             value = top->value;
         }
-        if (top->square != NULL) {
-            ensure(square == NULL, rec_state->state->info);
-            square = top->square;
-        }
     }
     top = push_frame(rec_state, lens);
+    ERR_BAIL(lens->info);
     top->tree = tree;
     top->key = key;
     top->value = value;
-    top->square = square;
  error:
     return;
 }
 
+/* Combine n frames from the stack into one new frame for M_PUT */
 static void parse_combine(struct rec_state *rec_state,
                           struct lens *lens, uint n) {
     struct skel *skel = make_skel(lens), *tail = NULL;
@@ -950,10 +1247,9 @@ static void parse_combine(struct rec_state *rec_state,
     char *key = NULL;
     struct frame *top = NULL;
 
-    if (n > 0)
-        top = top_frame(rec_state);
-
-    for (int i=0; i < n; i++, top = pop_frame(rec_state)) {
+    for (int i=0; i < n; i++) {
+        top = pop_frame(rec_state);
+        ERR_BAIL(lens->info);
         list_tail_cons(skel->skels, tail, top->skel);
         /* top->skel might have more than one node, update skel */
         if (tail != NULL)
@@ -965,19 +1261,48 @@ static void parse_combine(struct rec_state *rec_state,
         }
     }
     top = push_frame(rec_state, lens);
-    top->skel = skel;
-    top->dict = dict;
+    ERR_BAIL(lens->info);
+    top->skel = move(skel);
+    top->dict = move(dict);
     top->key = key;
  error:
+    free_skel(skel);
+    free_dict(dict);
     return;
 }
 
+static void visit_exit_put_subtree(struct lens *lens,
+                                   struct rec_state *rec_state,
+                                   struct frame *top) {
+    struct state *state = rec_state->state;
+    struct skel *skel = NULL;
+    struct dict *dict = NULL;
+
+    skel = make_skel(lens);
+    ERR_NOMEM(skel == NULL, lens->info);
+    dict = make_dict(top->key, top->skel, top->dict);
+    ERR_NOMEM(dict == NULL, lens->info);
+
+    top = pop_frame(rec_state);
+    ERR_BAIL(state->info);
+    ensure(lens == top->lens, state->info);
+    state->key = top->key;
+    top = push_frame(rec_state, lens);
+    ERR_BAIL(state->info);
+    top->skel = move(skel);
+    top->dict = move(dict);
+ error:
+    free_skel(skel);
+    free_dict(dict);
+}
+
 static void visit_exit(struct lens *lens,
                        ATTRIBUTE_UNUSED size_t start,
                        ATTRIBUTE_UNUSED size_t end,
                        void *data) {
     struct rec_state *rec_state = data;
     struct state *state = rec_state->state;
+    struct tree *tree = NULL;
 
     if (state->error != NULL)
         return;
@@ -989,40 +1314,32 @@ static void visit_exit(struct lens *lens,
     ERR_BAIL(lens->info);
 
     if (lens->tag == L_SUBTREE) {
-        struct frame *top = top_frame(rec_state);
+        /* Get the result of parsing lens->child */
+        struct frame *top = pop_frame(rec_state);
+        ERR_BAIL(state->info);
         if (rec_state->mode == M_GET) {
-            struct tree *tree;
-            // FIXME: tree may leak if pop_frame ensure0 fail
             tree = make_tree(top->key, top->value, NULL, top->tree);
-            tree->span = state->span;
             ERR_NOMEM(tree == NULL, lens->info);
+            tree->span = state->span;
+            /* Restore the parse state from before entering this subtree */
             top = pop_frame(rec_state);
+            ERR_BAIL(state->info);
             ensure(lens == top->lens, state->info);
             state->key = top->key;
             state->value = top->value;
             state->span = top->span;
-            pop_frame(rec_state);
+            /* Push the result of parsing this subtree */
             top = push_frame(rec_state, lens);
-            top->tree = tree;
+            ERR_BAIL(state->info);
+            top->tree = move(tree);
         } else {
-            struct skel *skel;
-            struct dict *dict;
-            skel = make_skel(lens);
-            ERR_NOMEM(skel == NULL, lens->info);
-            dict = make_dict(top->key, top->skel, top->dict);
-            ERR_NOMEM(dict == NULL, lens->info);
-            top = pop_frame(rec_state);
-            ensure(lens == top->lens, state->info);
-            state->key = top->key;
-            pop_frame(rec_state);
-            top = push_frame(rec_state, lens);
-            top->skel = skel;
-            top->dict = dict;
+            visit_exit_put_subtree(lens, rec_state, top);
         }
     } else if (lens->tag == L_CONCAT) {
         ensure(rec_state->fused >= lens->nchildren, state->info);
         for (int i = 0; i < lens->nchildren; i++) {
             struct frame *fr = nth_frame(rec_state, i);
+            ERR_BAIL(state->info);
             BUG_ON(lens->children[i] != fr->lens,
                     lens->info,
              "Unexpected lens in concat %zd..%zd\n  Expected: %s\n  Actual: %s",
@@ -1030,57 +1347,59 @@ static void visit_exit(struct lens *lens,
                     format_lens(lens->children[i]),
                     format_lens(fr->lens));
         }
-        if (rec_state->mode == M_GET)
-            get_combine(rec_state, lens, lens->nchildren);
-        else
-            parse_combine(rec_state, lens, lens->nchildren);
+        rec_state->combine(rec_state, lens, lens->nchildren);
     } else if (lens->tag == L_STAR) {
         uint n = 0;
         while (n < rec_state->fused &&
                nth_frame(rec_state, n)->lens == lens->child)
             n++;
-        if (rec_state->mode == M_GET)
-            get_combine(rec_state, lens, n);
-        else
-            parse_combine(rec_state, lens, n);
+        ERR_BAIL(state->info);
+        rec_state->combine(rec_state, lens, n);
     } else if (lens->tag == L_MAYBE) {
         uint n = 1;
         if (rec_state->fused > 0
             && top_frame(rec_state)->lens == lens->child) {
             n = 2;
         }
-        if (rec_state->mode == M_GET)
-            get_combine(rec_state, lens, n);
-        else
-            parse_combine(rec_state, lens, n);
+        ERR_BAIL(state->info);
+        /* If n = 2, the top of the stack is our child's result, and the
+           frame underneath it is the marker frame we pushed during
+           visit_enter. Combine these two frames into one, which represents
+           the result of parsing the whole L_MAYBE. */
+        rec_state->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 (strcmp(key, square) != 0){
+            struct ast *square, *concat, *right, *left;
+            char *rsqr, *lsqr;
+            int ret;
+
+            square = rec_state->ast;
+            concat = child_first(square);
+            right = child_first(concat);
+            left = child_last(concat);
+            lsqr = token_range(state->text, left->start, left->end);
+            rsqr = token_range(state->text, right->start, right->end);
+            ret = square_match(lens, lsqr, rsqr);
+            if (! ret) {
                 get_error(state, lens, "%s \"%s\" %s \"%s\"",
-                        "Parse error: mismatched key in square lens, expecting",
-                        key, "but got", square);
-                state->error->pos = end - strlen(square);
-                goto error;
+                        "Parse error: mismatched in square lens, expecting", lsqr,
+                        "but got", rsqr);
             }
-
-            FREE(square);
-            get_combine(rec_state, lens, 1);
-        } else {
-            parse_combine(rec_state, lens, 1);
+            FREE(lsqr);
+            FREE(rsqr);
+            if (! ret)
+                goto error;
         }
+        rec_state->combine(rec_state, lens, 1);
     } else {
+        /* Turn the top frame from having the result of one of our children
+           to being our result */
         top_frame(rec_state)->lens = lens;
+        ERR_BAIL(state->info);
     }
+    ast_pop(rec_state);
  error:
+    free_tree(tree);
     return;
 }
 
@@ -1100,8 +1419,6 @@ static struct frame *rec_process(enum mode_t mode, struct lens *lens,
     uint end = REG_END(state);
     uint start = REG_START(state);
     size_t len = 0;
-    struct re_registers *old_regs = state->regs;
-    uint old_nreg = state->nreg;
     int r;
     struct jmt_visitor visitor;
     struct rec_state rec_state;
@@ -1110,20 +1427,21 @@ static struct frame *rec_process(enum mode_t mode, struct lens *lens,
 
     MEMZERO(&rec_state, 1);
     MEMZERO(&visitor, 1);
+    SAVE_REGS(state);
 
     if (lens->jmt == NULL) {
         lens->jmt = jmt_build(lens);
         ERR_BAIL(lens->info);
     }
 
-    state->regs = NULL;
-    state->nreg = 0;
-
     rec_state.mode  = mode;
     rec_state.state = state;
     rec_state.fused = 0;
     rec_state.lvl   = 0;
     rec_state.start = start;
+    rec_state.ast = make_ast(lens);
+    rec_state.combine = (mode == M_GET) ? get_combine : parse_combine;
+    ERR_NOMEM(rec_state.ast == NULL, state->info);
 
     visitor.parse = jmt_parse(lens->jmt, state->text + start, end - start);
     ERR_BAIL(lens->info);
@@ -1148,17 +1466,21 @@ static struct frame *rec_process(enum mode_t mode, struct lens *lens,
         goto error;
     }
 
+    rec_state.ast = ast_root(rec_state.ast);
+    ensure(rec_state.ast->parent == NULL, state->info);
  done:
-    state->regs = old_regs;
-    state->nreg = old_nreg;
+    if (debugging("cf.get.ast"))
+        print_ast(ast_root(rec_state.ast), 0);
+    RESTORE_REGS(state);
     jmt_free_parse(visitor.parse);
+    free_ast(ast_root(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);
+        free_span(f->span);
         if (mode == M_GET) {
             FREE(f->value);
             free_tree(f->tree);
@@ -1280,7 +1602,7 @@ static int init_regs(struct state *state, struct lens *lens, uint size) {
 }
 
 struct tree *lns_get(struct info *info, struct lens *lens, const char *text,
-                     struct lns_error **err) {
+                     int enable_span, struct lns_error **err) {
     struct state state;
     struct tree *tree = NULL;
     uint size = strlen(text);
@@ -1295,6 +1617,8 @@ struct tree *lns_get(struct info *info, struct lens *lens, const char *text,
 
     state.text = text;
 
+    state.enable_span = enable_span;
+
     /* We are probably being overly cautious here: if the lens can't process
      * all of TEXT, we should really fail somewhere in one of the sublenses.
      * But to be safe, we check that we can process everything anyway, then
@@ -1318,10 +1642,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");
     }