/*
* 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
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
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 */
/* 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;
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])
#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);
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);
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;
}
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;
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);
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;
}
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;
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;
}
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;
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);
}
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);
}
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;
state->span = span;
return tree;
error:
+ free_span(state->span);
+ state->span = span;
return NULL;
}
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;
}
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);
+ }
}
}
/* 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);
}
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,
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,
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,
void *data) {
struct rec_state *rec_state = data;
struct state *state = rec_state->state;
+ struct ast *child;
if (state->error != NULL)
return;
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)
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;
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)
}
}
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;
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",
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;
}
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;
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);
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);
}
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);
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
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");
}