summaryrefslogtreecommitdiffstats
path: root/match.c
diff options
context:
space:
mode:
Diffstat (limited to 'match.c')
-rw-r--r--match.c234
1 files changed, 231 insertions, 3 deletions
diff --git a/match.c b/match.c
index 1f10ab78..dd908cf1 100644
--- a/match.c
+++ b/match.c
@@ -607,7 +607,7 @@ static val search_match_binding_var(match_line_ctx *c, val sym,
return nil;
}
-static val h_var(match_line_ctx *c)
+static val h_var_compat(match_line_ctx *c)
{
val elem = pop(&c->specline);
val sym = second(elem);
@@ -821,6 +821,229 @@ static val h_var(match_line_ctx *c)
return next_spec_k;
}
+static val h_var(match_line_ctx *c)
+{
+ val elem = pop(&c->specline);
+ val sym = second(elem);
+ val next = first(c->specline);
+ val modifiers = third(elem);
+ val modifier = first(modifiers);
+ val pair = if2(sym, tx_lookup_var(sym, c->bindings));
+
+ if (sym == t)
+ sem_error(elem, lit("t is not a bindable symbol"), nao);
+
+ if (gt(length_list(modifiers), one)) {
+ sem_error(elem, lit("multiple modifiers on variable ~s"),
+ sym, nao);
+ }
+
+ if (bindable(modifier)) {
+ val mpair = tx_lookup_var_ubc(modifier, c->bindings, elem);
+ modifier = cdr(mpair);
+ }
+
+ if (pair && !consp(modifier)) {
+ /* Except in two cass, if the variable already has a binding, we replace
+ it with its value, and treat it as a string match. And if the spec looks
+ like ((var <sym>) <next> ...) and it must be transformed into
+ (<sym-substituted> <next> ...).
+ The special cases are:
+ - if the variable is a fix sized field match, then it has to match
+ that much text;
+ - if the variable is a function-spanning match, the function has
+ to be called every time; hence he !consp(modifier) check above,
+ and the use of dest_bind in the function spanning case. */
+ if (integerp(modifier)) {
+ val past = plus(c->pos, modifier);
+
+ if (length_str_lt(c->dataline, past) || lt(past, c->pos))
+ {
+ LOG_MISMATCH("fixed field size");
+ return nil;
+ }
+
+ if (!tree_find(trim_str(sub_str(c->dataline, c->pos, past)),
+ cdr(pair), equal_f))
+ {
+ LOG_MISMATCH("fixed field contents");
+ return nil;
+ }
+
+ LOG_MATCH("fixed field", past);
+ c->pos = past;
+ c->specline = rest(c->specline);
+ } else {
+ c->specline = rlcp(cons(cdr(pair), c->specline), c->specline);
+ }
+ return repeat_spec_k;
+ } else if (consp(modifier) || regexp(modifier)) { /* var bound over text matched by form */
+ cons_bind (new_bindings, new_pos,
+ match_line(ml_specline(*c, cons(modifier, nil))));
+
+ if (!new_pos) {
+ LOG_MISMATCH("var spanning form");
+ return nil;
+ }
+
+ new_pos = minus(new_pos, c->base);
+
+
+ LOG_MATCH("var spanning form", new_pos);
+
+ c->bindings = dest_bind(c->specline, new_bindings, sym,
+ sub_str(c->dataline, c->pos, new_pos), equal_f);
+ if (c->bindings == t) {
+ LOG_MISMATCH("function span mismatch");
+ return nil;
+ }
+
+ c->pos = new_pos;
+ /* This may have another variable attached */
+ if (next) {
+ c->specline = rlcp(cons(next, rest(c->specline)), c->specline);
+ return repeat_spec_k;
+ }
+ } else if (integerp(modifier)) { /* fixed field */
+ val past = plus(c->pos, modifier);
+ if (length_str_lt(c->dataline, past) || lt(past, c->pos))
+ {
+ LOG_MISMATCH("count based var");
+ return nil;
+ }
+ LOG_MATCH("count based var", past);
+ if (sym)
+ c->bindings = acons(sym, trim_str(sub_str(c->dataline, c->pos, past)), c->bindings);
+ c->pos = past;
+ /* This may have another variable attached */
+ if (next) {
+ c->specline = rlcp(cons(next, rest(c->specline)), c->specline);
+ return repeat_spec_k;
+ }
+ } else if (modifier && modifier != t) {
+ sem_error(elem, lit("invalid modifier ~s on variable ~s"),
+ modifier, sym, nao);
+ } else if (next == nil) { /* no modifier, no elem -> to end of line */
+ if (sym)
+ c->bindings = acons(sym, sub_str(c->dataline, c->pos, nil), c->bindings);
+ c->pos = length_str(c->dataline);
+ } else if (type(next) == STR) {
+ val find = search_str(c->dataline, next, c->pos, modifier);
+ if (!find) {
+ LOG_MISMATCH("var delimiting string");
+ return nil;
+ }
+ LOG_MATCH("var delimiting string", find);
+ if (sym)
+ c->bindings = acons(sym, sub_str(c->dataline, c->pos, find), c->bindings);
+ c->pos = plus(find, length_str(next));
+ } else if (regexp(next)) {
+ val find = search_regex(c->dataline, next, c->pos, modifier);
+ val fpos = car(find);
+ val flen = cdr(find);
+ if (!find) {
+ LOG_MISMATCH("var delimiting regex");
+ return nil;
+ }
+ LOG_MATCH("var delimiting regex", fpos);
+ if (sym)
+ c->bindings = acons(sym, sub_str(c->dataline, c->pos, fpos), c->bindings);
+ c->pos = if3(flen == t, t, plus(fpos, flen));
+ } else if (consp(next)) {
+ val op = first(next);
+
+ if (op == var_s) {
+ /* Unbound var followed by var: the following one must either
+ be bound, or must specify a regex. */
+ val second_sym = second(next);
+ val next_modifiers = third(next);
+ val next_modifier = first(next_modifiers);
+ val pair = if2(second_sym, tx_lookup_var(second_sym, c->bindings));
+
+ if (gt(length_list(next_modifiers), one)) {
+ sem_error(elem, lit("multiple modifiers on variable ~s"),
+ second_sym, nao);
+ }
+
+ if (!pair && regexp(next_modifier)) {
+ val find = search_regex(c->dataline, next_modifier, c->pos, modifier);
+ val fpos = car(find);
+ val flen = cdr(find);
+
+ if (!find) {
+ LOG_MISMATCH("double var regex");
+ return nil;
+ }
+
+ /* Text from here to start of regex match goes to this
+ variable. */
+ if (sym)
+ c->bindings = acons(sym, sub_str(c->dataline, c->pos, fpos),
+ c->bindings);
+ /* Text from start of regex match to end goes to the
+ second variable */
+ if (second_sym)
+ c->bindings = acons(second_sym,
+ sub_str(c->dataline, fpos, plus(fpos, flen)),
+ c->bindings);
+ LOG_MATCH("double var regex (first var)", fpos);
+ c->pos = fpos;
+ LOG_MATCH("double var regex (second var)", plus(fpos, flen));
+ c->pos = plus(fpos, flen);
+ return next_spec_k;
+ } else if (!pair) {
+ sem_error(elem, lit("consecutive unbound variables"), nao);
+ } else {
+ /* Re-generate a new spec in which the next variable
+ is replaced by its value, and repeat. */
+ val r = rest(c->specline);
+ c->specline = rlcp(cons(elem, rlcp(cons(cdr(pair), r), r)), r);
+ return repeat_spec_k;
+ }
+ } else if (op == text_s) {
+ val text_only_spec = rlcp(cons(next, nil), next);
+ val find = search_match(c, modifier, text_only_spec);
+ val fpos = car(find);
+ if (!find) {
+ LOG_MISMATCH("var delimiting text compound");
+ return nil;
+ }
+ LOG_MATCH("var delimiting text compound", fpos);
+ if (sym)
+ c->bindings = acons(sym, sub_str(c->dataline, c->pos, fpos), c->bindings);
+ c->pos = fpos;
+ return repeat_spec_k;
+ } else if (consp(op) || stringp(op)) {
+ cons_bind (find, len, search_str_tree(c->dataline, next, c->pos, modifier));
+ if (!find) {
+ LOG_MISMATCH("string");
+ return nil;
+ }
+ if (sym)
+ c->bindings = acons(sym, sub_str(c->dataline, c->pos, find), c->bindings);
+ c->pos = plus(find, len);
+ } else {
+ val find = if3(opt_compat && opt_compat <= 172,
+ search_match(c, modifier, c->specline),
+ search_match_binding_var(c, sym, modifier, c->specline));
+ val fpos = car(find);
+ if (!find) {
+ LOG_MISMATCH("var delimiting spec");
+ return nil;
+ }
+ LOG_MATCH("var delimiting spec", fpos);
+ if (sym)
+ c->bindings = acons(sym, sub_str(c->dataline, c->pos, fpos), c->bindings);
+ c->pos = fpos;
+ return repeat_spec_k;
+ }
+ } else {
+ sem_error(elem, lit("variable followed by invalid element: ~s"), next, nao);
+ }
+
+ return next_spec_k;
+}
+
static val h_skip(match_line_ctx *c)
{
val self = lit("skip");
@@ -2339,7 +2562,10 @@ static val v_var(match_files_ctx *c)
if (ret == next_spec_k) {
c->data = fc.data;
- c->bindings = acons(varsym, ldiff(data, fc.data), fc.bindings);
+ c->bindings = dest_bind(specline, fc.bindings, varsym,
+ ldiff(data, fc.data), equal_f);
+ if (c->bindings == t)
+ ret = nil;
}
return ret;
@@ -5098,6 +5324,8 @@ void match_init(void)
void match_compat_fixup(int compat_ver)
{
- if (compat_ver <= 272)
+ if (compat_ver <= 272) {
sethash(v_directive_table, var_s, cptr(coerce(mem_t *, v_var_compat)));
+ sethash(h_directive_table, var_s, cptr(coerce(mem_t *, h_var_compat)));
+ }
}