summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--match.c84
-rw-r--r--txr.175
2 files changed, 99 insertions, 60 deletions
diff --git a/match.c b/match.c
index 5937130f..676796e4 100644
--- a/match.c
+++ b/match.c
@@ -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);
+ }
}
}
}
diff --git a/txr.1 b/txr.1
index c46a07a5..84c25d8f 100644
--- a/txr.1
+++ b/txr.1
@@ -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