summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--match.c30
-rw-r--r--txr.134
3 files changed, 73 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index aa7c7418..8bb66845 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,19 @@
2011-11-06 Kaz Kylheku <kaz@kylheku.com>
+ Task #11581 & bugfix.
+
+ * match.c (noval_s): New symbol variable.
+ (vars_to_bindings): Use a default value of noval_s to indicate a
+ required variable, rather than nil, which would not allow
+ an optional variable with a default value of nil.
+ (h_coll, v_collect): Check default value against noval_s, rather than nil.
+ (v_gather): Support :vars keyword.
+ (syms_init): Initialize new symbol variable.
+
+ * txr.1: Documented gather's :vars parameter.
+
+2011-11-06 Kaz Kylheku <kaz@kylheku.com>
+
Task #11581
* match.c (gather_s): New keyword variable.
diff --git a/match.c b/match.c
index 066dbb23..e4ff0e22 100644
--- a/match.c
+++ b/match.c
@@ -58,6 +58,8 @@ val append_k, into_k, var_k, list_k, string_k, env_k;
val filter_s;
+val noval_s;
+
static val h_directive_table, v_directive_table;
static void debugf(val fmt, ...)
@@ -338,7 +340,7 @@ static val vars_to_bindings(val lineno, val vars, val bindings)
for (iter = vars; iter; iter = cdr(iter)) {
val item = car(iter);
if (bindable(item)) {
- list_collect (tail, cons(item, nil));
+ list_collect (tail, cons(item, noval_s));
} else if (consp(item) && bindable(first(item))) {
list_collect (tail, cons(first(item),
cdr(eval_form(lineno, second(item), bindings))));
@@ -715,7 +717,7 @@ static val h_coll(match_line_ctx c, match_line_ctx *cout)
val exists = assoc(new_bindings, var);
if (!exists) {
- if (!dfl)
+ if (dfl == noval_s)
sem_error(c.spec_lineno, lit("coll failed to bind ~a"),
var, nao);
else
@@ -1925,6 +1927,8 @@ static val v_gather(match_files_ctx *c)
{
spec_bind (specline, spec_linenum, first_spec, c->spec);
val specs = copy_list(second(first_spec));
+ val args = third(first_spec);
+ val vars = vars_to_bindings(spec_linenum, getplist(args, vars_k), c->bindings);
while (specs && c->data) {
list_collect_decl (new_specs, ptail);
@@ -1971,6 +1975,25 @@ static val v_gather(match_files_ctx *c)
}
}
+ if (vars) {
+ val iter;
+
+ for (iter = vars; iter != nil; iter = cdr(iter)) {
+ cons_bind (var, dfl_val, car(iter));
+ if (!assoc(c->bindings, var)) {
+ if (dfl_val == noval_s) {
+ debuglf(spec_linenum, lit("gather failed to match some required vars"), nao);
+ return nil;
+ } else {
+ c->bindings = acons(c->bindings, var, dfl_val);
+ }
+ }
+ }
+
+ debuglf(spec_linenum, lit("gather matched all required vars"), nao);
+ return next_spec_k;
+ }
+
if (specs) {
debuglf(spec_linenum, lit("gather failed to match some specs:"), nao);
debuglf(spec_linenum, lit("~s"), specs, nao);
@@ -2071,7 +2094,7 @@ static val v_collect(match_files_ctx *c)
val exists = assoc(new_bindings, var);
if (!exists) {
- if (!dfl)
+ if (dfl == noval_s)
sem_error(spec_linenum, lit("collect failed to bind ~a"),
var, nao);
else
@@ -2950,6 +2973,7 @@ static void syms_init(void)
env_k = intern(lit("env"), keyword_package);
filter_s = intern(lit("filter"), user_package);
+ noval_s = intern(lit("noval"), system_package);
}
static void dir_tables_init(void)
diff --git a/txr.1 b/txr.1
index ba6093ff..29a71c04 100644
--- a/txr.1
+++ b/txr.1
@@ -1619,7 +1619,7 @@ which all have to match somewhere in the data, but in any order.
For further convenience, the lines of the first clause of the gather directive
are implicitly treated as separate clauses.
-The syntax follow this pattern
+The syntax follows this pattern
@(gather)
one-line-query1
@@ -1644,7 +1644,8 @@ The syntax follow this pattern
.
@(end)
-Of course the multi-line clauses are optional.
+Of course the multi-line clauses are optional. The gather directive takes
+keyword parameters, see below.
How gather works is that the text is searched for matches for the single line
and multi-line queries. The clauses are applied in the order in which they appear.
@@ -1666,6 +1667,35 @@ order:
SHELL=@SHELL
@(end)
+.SS Gather Keyword Parameters
+
+The gather diretive accepts the keyword parameter :vars. The argument to vars is a list
+of required and optional variables. Optional variables are denoted by the
+specification of a default value. Example:
+
+ @(gather :vars (a b c (d "foo")))
+ ...
+ @(end)
+
+Here, a, b, c and e are required variables, and d is optional. Variable e is
+required because its default value is the empty list (), same as the symbol
+nil.
+
+The presence of vars changes the behavior in three ways.
+
+Firstly, even if all the clauses in the gather match successfully and are
+eliminated, the directive will fail if the required variables do not have
+bindings. It doesn't matter whether the bindings are existing, or whether they
+are established by the gather.
+
+Secondly, if some of the clauses of the gather did not match, but all
+of the required variables have bindings, then the directive succeeds.
+Without the presence of :vars, it would fail in this situation.
+
+Thirdly, if the the gather succeeds (all required variables have bindings),
+then all of the optional variables which do not have bindings are given
+bindings to their default values.
+
.SS The Collect Directive
The syntax of the collect directive is: