diff options
-rw-r--r-- | match.c | 84 | ||||
-rw-r--r-- | txr.1 | 75 |
2 files changed, 99 insertions, 60 deletions
@@ -989,6 +989,7 @@ static val h_coll(match_line_ctx *c) cnum ctimin = if3(times, c_num(times), if3(mintimes, c_num(mintimes), 0)); cnum cchars = if3(chars, c_num(chars), 0); cnum timescounter = 0, charscounter = 0; + int compat_222 = opt_compat && opt_compat <= 222; val iter; if (op_sym == rep_s) { @@ -1064,6 +1065,7 @@ static val h_coll(match_line_ctx *c) val strictly_new_bindings = set_diff(new_bindings, c->bindings, eq_f, nil); val have_new = strictly_new_bindings; + val all_dfl = t, some_required = nil; new_pos = minus(new_pos, c->base); LOG_MATCH("coll", new_pos); @@ -1071,32 +1073,38 @@ static val h_coll(match_line_ctx *c) for (iter = vars; iter; iter = cdr(iter)) { cons_bind (var, dfl, car(iter)); val exists = tx_lookup_var(var, new_bindings); + int is_dfl = (dfl != noval_s); - if (!exists) { - if (dfl == noval_s) - ptail = list_collect(ptail, var); - else - strictly_new_bindings = acons(var, dfl, strictly_new_bindings); - } + if (!is_dfl) + some_required = t; + + if (exists) + all_dfl = nil; + else if (is_dfl) + strictly_new_bindings = acons(var, dfl, strictly_new_bindings); + else + ptail = list_collect(ptail, var); } if (have_new && missing) sem_error(elem, lit("~s failed to bind ~a"), op_sym, missing, nao); - for (iter = strictly_new_bindings; iter; iter = cdr(iter)) - { - val binding = car(iter); - val sym = car(binding); + if (!vars || !all_dfl || !some_required || compat_222) { + for (iter = strictly_new_bindings; iter; iter = cdr(iter)) + { + val binding = car(iter); + val sym = car(binding); - if (!have_vars || assoc(sym, vars)) { - val existing = assoc(sym, bindings_coll); - val newlist = cons(cdr(binding), cdr(existing)); + if (!have_vars || assoc(sym, vars)) { + val existing = assoc(sym, bindings_coll); + val newlist = cons(cdr(binding), cdr(existing)); - if (existing) - rplacd(existing, newlist); - else - bindings_coll = acons(sym, newlist, bindings_coll); + if (existing) + rplacd(existing, newlist); + else + bindings_coll = acons(sym, newlist, bindings_coll); + } } } } @@ -3321,6 +3329,7 @@ static val v_collect(match_files_ctx *c) volatile cnum timescounter = 0, linescounter = 0; cnum ctimes = if3(times, c_num(times), 0); cnum clines = if3(lines, c_num(lines), 0); + int compat_222 = opt_compat && opt_compat <= 222; val iter; uw_mark_frame; uw_block_begin(nil, result); @@ -3409,6 +3418,7 @@ static val v_collect(match_files_ctx *c) val strictly_new_bindings = set_diff(new_bindings, c->bindings, eq_f, nil); val have_new = strictly_new_bindings; + val all_dfl = t, some_required = nil; debuglf(specline, lit("~s matched ~a:~d"), op_sym, c->curfile, c->data_lineno, nao); @@ -3416,32 +3426,38 @@ static val v_collect(match_files_ctx *c) for (iter = vars; iter; iter = cdr(iter)) { cons_bind (var, dfl, car(iter)); val exists = tx_lookup_var(var, new_bindings); + int is_dfl = (dfl != noval_s); - if (!exists) { - if (dfl == noval_s) - ptail = list_collect(ptail, var); - else - strictly_new_bindings = acons(var, dfl, strictly_new_bindings); - } + if (!is_dfl) + some_required = t; + + if (exists) + all_dfl = nil; + else if (is_dfl) + strictly_new_bindings = acons(var, dfl, strictly_new_bindings); + else + ptail = list_collect(ptail, var); } if (have_new && missing) sem_error(specline, lit("~s failed to bind ~a"), op_sym, missing, nao); - for (iter = strictly_new_bindings; iter; iter = cdr(iter)) - { - val binding = car(iter); - val sym = car(binding); + if (!vars || !all_dfl || !some_required || compat_222) { + for (iter = strictly_new_bindings; iter; iter = cdr(iter)) + { + val binding = car(iter); + val sym = car(binding); - if (!have_vars || assoc(sym, vars)) { - val existing = assoc(sym, bindings_coll); - val newlist = cons(cdr(binding), cdr(existing)); + if (!have_vars || assoc(sym, vars)) { + val existing = assoc(sym, bindings_coll); + val newlist = cons(cdr(binding), cdr(existing)); - if (existing) - rplacd(existing, newlist); - else - bindings_coll = acons(sym, newlist, bindings_coll); + if (existing) + rplacd(existing, newlist); + else + bindings_coll = acons(sym, newlist, bindings_coll); + } } } } @@ -5784,48 +5784,56 @@ body. The argument to .code :vars is a list of variable specs. A variable spec is either a -symbol, or a +symbol, denoting a required variable, or a .mono .meti >> ( symbol << default-value ) .onom pair, where .meta default-value is a Lisp expression whose value specifies a default value -for the variable. +for the variable, which is optional. When a .code :vars list is specified, it means that only the given variables can emerge from the successful collect. Any newly introduced bindings for other -variables do not propagate. - -Furthermore, for any variable which is not specified with a default value, the -collect body, whenever it matches successfully, must bind that variable. If it -neglects to bind the variable, an exception of type query-error is thrown. -(If a -.code collect -body matches successfully, but produces no new bindings, then -this error is suppressed.) - -For any variable which does have a default value, if the -.code collect -body neglects -to bind that variable, the behavior is as if -.code collect -did bind that variable -to that default value. - -The default values are expressions, and so can be quasiliterals. +variables do not propagate. More precisely, whenever the collect body matches +successfully, the following three rules apply: +.IP 1 +If +.code :vars +specifies required variables, the collect body must bind all of them, +or else must not bind any variable at all, whether listed in +.code :vars +or not, otherwise an exception of type +.code query-error +is thrown. +.IP 2 +If +.code :vars +specifies required variables, and also specifies default variables, +and the collect body binds no variable at all, then the default variables +are not bound to their default values. +.IP 3 +If +.code :vars +specifies optional variables, and all required variables are bound by +the collect body, then all those optional variables that are not bound +by the collect body are bound to their default values. Under this rule, if +.code :vars +specifies no required variables, that is deemed to be +logically equivalent to all required variables being bound. +.PP -Lastly, if in the event that +In the event that .code collect does not match anything, the variables specified in -.code :vars -(whether or not they have a default value) are all bound to -empty lists. (These bindings are established after the processing of the +.codn :vars , +whether required or optional, are all bound to +empty lists. These bindings are established after the processing of the .cod3 until / last -last clause, if present.) +last clause, if present. Example: @@ -69038,6 +69046,21 @@ of these version values, the described behaviors are provided if is given an argument which is equal or lower. For instance .code "-C 103" selects the behaviors described below for version 105, but not those for 102. +.IP 222 +After \*(TX 222, the behavior of +.code :vars +in +.code @(collect) +was subject to an adjustment. Previously, if the collect body +didn't bind any variables, and both required and optional variables +were specified in +.codn :vars , +it would still bind all of the optional ones to their default values. +This was a poor behavior which violated the idea that +.code :vars +enforces an all-or-nothing binding discipline to keep the collected +lists consistent. Selecting 222 compatibility or lower restores this +behavior. .IP 215 After \*(TX 215, the behavior of the .code load |