summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--match.c43
-rw-r--r--parser.l9
-rw-r--r--parser.y39
-rw-r--r--txr.165
4 files changed, 132 insertions, 24 deletions
diff --git a/match.c b/match.c
index 22bb3fb5..7d738b6d 100644
--- a/match.c
+++ b/match.c
@@ -65,7 +65,7 @@ val include_s, close_s, require_s;
val longest_k, shortest_k, greedy_k;
val vars_k, resolve_k;
val append_k, into_k, var_k, list_k, string_k, env_k, counter_k;
-val named_k, continue_k, finish_k;
+val named_k, continue_k, finish_k, mandatory_k;
val filter_s;
@@ -798,6 +798,9 @@ static val h_coll(match_line_ctx *c)
val op_sym = first(elem);
val coll_specline = second(elem);
val until_last_specline = third(elem);
+ cons_bind (ul_sym, ul_args, until_last_specline);
+ cons_bind (ul_opts, spec, ul_args);
+ val ul_match = nil;
val args = fourth(elem);
val bindings_coll = nil;
val last_bindings = nil;
@@ -848,7 +851,6 @@ static val h_coll(match_line_ctx *c)
if (until_last_specline) {
uses_or2;
- cons_bind (sym, spec, until_last_specline);
cons_bind (until_last_bindings, until_pos,
match_line(ml_bindings_specline(*c,
or2(new_bindings, c->bindings),
@@ -858,11 +860,12 @@ static val h_coll(match_line_ctx *c)
until_pos = minus(until_pos, c->base);
LOG_MATCH("until/last", until_pos);
- if (sym == last_s) {
+ if (ul_sym == last_s) {
last_bindings = set_diff(until_last_bindings,
new_bindings, eq_f, nil);
c->pos = until_pos;
}
+ ul_match = t;
break;
} else {
LOG_MISMATCH("until/last");
@@ -940,6 +943,12 @@ next_coll:
return nil;
}
+ if (!ul_match && ul_opts && memq(mandatory_k, ul_opts)) {
+ debuglf(elem, lit("~s didn't match mandatory until/last"),
+ op_sym, nao);
+ return nil;
+ }
+
if (!bindings_coll)
debuglf(elem, lit("nothing was collected"), nao);
@@ -2596,6 +2605,9 @@ static val v_gather(match_files_ctx *c)
val specs = copy_list(second(first_spec));
val args = third(first_spec);
val until_last = fourth(first_spec);
+ cons_bind (ul_sym, ul_args, until_last);
+ cons_bind (ul_opts, ul_spec, ul_args);
+ val ul_match = nil;
val have_vars;
val vars = vars_to_bindings(specline,
getplist_f(args, vars_k, mkcloc(have_vars)),
@@ -2631,7 +2643,6 @@ static val v_gather(match_files_ctx *c)
if (until_last)
{
- cons_bind (sym, ul_spec, until_last);
cons_bind (until_last_bindings, success,
match_files(mf_spec(*c, ul_spec)));
@@ -2639,7 +2650,7 @@ static val v_gather(match_files_ctx *c)
debuglf(specline, lit("until/last matched ~a:~d"),
c->curfile, c->data_lineno, nao);
/* Until discards bindings and position, last keeps them. */
- if (sym == last_s) {
+ if (ul_sym == last_s) {
val last_bindings = set_diff(until_last_bindings, c->bindings, eq_f, nil);
c->bindings = nappend2(last_bindings, orig_bindings);
@@ -2651,6 +2662,7 @@ static val v_gather(match_files_ctx *c)
c->data_lineno = new_line;
}
}
+ ul_match = t;
break;
}
}
@@ -2672,6 +2684,11 @@ static val v_gather(match_files_ctx *c)
}
}
+ if (!ul_match && ul_opts && memq(mandatory_k, ul_opts)) {
+ debuglf(specline, lit("gather didn't match mandatory until/last"), nao);
+ return nil;
+ }
+
if (have_vars) {
val iter;
@@ -2706,6 +2723,9 @@ static val v_collect(match_files_ctx *c)
val op_sym = first(first_spec);
val coll_spec = second(first_spec);
val until_last_spec = third(first_spec);
+ cons_bind (ul_sym, ul_args, until_last_spec);
+ cons_bind (ul_opts, ul_spec, ul_args);
+ val ul_match = nil, accept_jump = t;
val args = fourth(first_spec);
volatile val bindings_coll = nil;
volatile val last_bindings = nil;
@@ -2767,7 +2787,6 @@ static val v_collect(match_files_ctx *c)
if (until_last_spec)
{
uses_or2;
- cons_bind (sym, ul_spec, until_last_spec);
cons_bind (until_last_bindings, success,
match_files(mf_spec_bindings(*c, ul_spec,
or2(new_bindings, c->bindings))));
@@ -2776,7 +2795,7 @@ static val v_collect(match_files_ctx *c)
debuglf(specline, lit("until/last matched ~a:~d"),
c->curfile, c->data_lineno, nao);
/* Until discards bindings and position, last keeps them. */
- if (sym == last_s) {
+ if (ul_sym == last_s) {
last_bindings = set_diff(until_last_bindings,
new_bindings, eq_f, nil);
if (success == t) {
@@ -2788,6 +2807,7 @@ static val v_collect(match_files_ctx *c)
c->data_lineno = new_line;
}
}
+ ul_match = t;
break;
}
}
@@ -2869,6 +2889,8 @@ next_collect:
}
}
+ accept_jump = nil;
+
uw_block_end;
if (!result) {
@@ -2882,6 +2904,12 @@ next_collect:
return nil;
}
+ if (!ul_match && ul_opts && memq(mandatory_k, ul_opts) && !accept_jump) {
+ debuglf(specline, lit("~s didn't match mandatory until/last"),
+ op_sym, nao);
+ return nil;
+ }
+
if (!bindings_coll)
debuglf(specline, lit("nothing was collected"), nao);
@@ -4076,6 +4104,7 @@ static void syms_init(void)
named_k = intern(lit("named"), keyword_package);
continue_k = intern(lit("continue"), keyword_package);
finish_k = intern(lit("finish"), keyword_package);
+ mandatory_k = intern(lit("mandatory"), keyword_package);
filter_s = intern(lit("filter"), user_package);
noval_s = intern(lit("noval"), system_package);
diff --git a/parser.l b/parser.l
index 79392ce3..aa7fb6dc 100644
--- a/parser.l
+++ b/parser.l
@@ -474,8 +474,8 @@ UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
return COLL;
}
-<SPECIAL>\({WS}until{WS}\) {
- yy_pop_state(yyscanner);
+<SPECIAL>\({WS}until/{ID_END} {
+ yy_push_state(NESTED, yyscanner);
yylval->lineno = yyextra->lineno;
return UNTIL;
}
@@ -492,7 +492,6 @@ UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
return REPEAT;
}
-
<SPECIAL>\({WS}rep/{ID_END} {
yy_push_state(NESTED, yyscanner);
yylval->lineno = yyextra->lineno;
@@ -511,8 +510,8 @@ UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
return FIRST;
}
-<SPECIAL>\({WS}last{WS}\) {
- yy_pop_state(yyscanner);
+<SPECIAL>\({WS}last/{ID_END} {
+ yy_push_state(NESTED, yyscanner);
yylval->lineno = yyextra->lineno;
return LAST;
}
diff --git a/parser.y b/parser.y
index f442ff66..0885861b 100644
--- a/parser.y
+++ b/parser.y
@@ -290,12 +290,13 @@ gather_clause : GATHER exprs_opt ')'
| GATHER exprs_opt ')'
newl gather_parts
- until_last newl
+ until_last exprs_opt ')' newl
clauses
END newl { $$ = list(gather_s,
append2(mapcar(curry_12_1(func_n2(cons), nil),
first($5)), rest($5)),
- $2, cons(cdr($6), $8), nao);
+ $2, cons(cdr($6),
+ cons($7, $10)), nao);
rl($$, num($1)); }
| GATHER exprs_opt ')'
@@ -320,12 +321,13 @@ collect_clause : collect_repeat exprs_opt ')' newl
nao);
rl($$, cdr($1)); }
| collect_repeat exprs_opt ')'
- newl clauses until_last
+ newl clauses until_last exprs_opt ')'
newl clauses END newl { $$ = list(car($1), $5,
- cons(cdr($6), $8),
+ cons(cdr($6),
+ cons($7, $10)),
$2, nao);
rl($$, cdr($1));
- rl($8, car($6)); }
+ rl($10, car($6)); }
| collect_repeat exprs_opt ')'
newl error { $$ = nil;
if (yychar == UNTIL ||
@@ -425,14 +427,19 @@ elem : texts { $$ = rlcp(cons(text_s, $1), $1);
| COLL exprs_opt ')' elems END { $$ = list(coll_s, $4, nil, $2, nao);
rl($$, num($1)); }
| COLL exprs_opt ')' elems
- until_last elems END { $$ = list(coll_s, $4, cons(cdr($5), $6),
+ until_last exprs_opt ')'
+ elems END { $$ = list(coll_s, $4, cons(cdr($5),
+ cons($6, $8)),
$2, nao);
rl($$, num($1));
rl($6, car($5)); }
| REP exprs_opt ')' elems END { $$ = list(rep_s, $4, nil, $2, nao);
rl($$, num($1)); }
| REP exprs_opt ')' elems
- until_last elems END { $$ = list(rep_s, $4, cons(cdr($5), $6),
+ until_last exprs_opt ')'
+ elems END
+ { $$ = list(rep_s, $4, cons(cdr($5),
+ cons($6, $8)),
$2, nao);
rl($$, num($1));
rl($6, car($5)); }
@@ -596,9 +603,14 @@ repeat_parts_opt : SINGLE newl
out_clauses_opt
repeat_parts_opt { $$ = cons(cons(first_s, $3), $4);
rl($$, num($1)); }
- | LAST newl
+ | LAST exprs_opt ')' newl
out_clauses_opt
- repeat_parts_opt { $$ = cons(cons(last_s, $3), $4);
+ repeat_parts_opt { if ($2)
+ yyerrorf(scnr,
+ lit("last: in output, "
+ "takes no arguments"),
+ nao);
+ $$ = cons(cons(last_s, $5), $6);
rl($$, num($1)); }
| EMPTY newl
out_clauses_opt
@@ -660,8 +672,13 @@ rep_parts_opt : SINGLE o_elems_opt
| FIRST o_elems_opt
rep_parts_opt { $$ = cons(cons(first_s, $2), $3);
rl($$, num($1)); }
- | LAST o_elems_opt
- rep_parts_opt { $$ = cons(cons(last_s, $2), $3);
+ | LAST exprs_opt ')'
+ o_elems_opt rep_parts_opt { if ($2)
+ yyerrorf(scnr,
+ lit("last: in output, "
+ "takes no arguments"),
+ nao);
+ $$ = cons(cons(last_s, $4), $5);
rl($$, num($1)); }
| EMPTY o_elems_opt
rep_parts_opt { $$ = cons(cons(empty_s, $2), $3);
diff --git a/txr.1 b/txr.1
index 30017711..9b1da3e3 100644
--- a/txr.1
+++ b/txr.1
@@ -4451,6 +4451,34 @@ clause has visibility to bindings established in the
previous clauses in that same iteration, even though those bindings
end up thrown away.
+For consistency, the
+.code :mandatory
+keyword is supported in the
+.cod3 until / last
+clause of
+.codn gather .
+The semantics of using
+.code :mandatory
+in this situation is tricky. In particular, if it is in effect, and the
+.code gather
+terminates successfully by collecting all required matches, it will
+trigger a failure. On the other hand, if the
+.code until
+or
+.code last
+clause activates before all required matches are gathered, a failure
+also occurs, whether or not the clause is
+.codn :mandatory .
+
+Meaningful use of
+.code :mandatory
+requires that the gather be open-ended; it must allow some (or all) variables
+not to be required. The presence of the option means that for the gather
+to succeed, all required variables must be gathered first, but then termination
+must be achieved via the
+.cod3 until / last
+clause before all gather clauses are satisfied.
+
.coNP Keyword parameters in @ gather
The
.code gather
@@ -4998,6 +5026,39 @@ directive used in
.code @(output)
clauses; that is a different directive.
+.coNP Mandatory @ until and @ last
+
+The
+.cod3 until / last
+clause supports the option keyword
+.codn :mandatory ,
+exemplified by the following:
+
+.cblk
+ @(collect)
+ ...
+ @(last :mandatory)
+ ...
+ @(end)
+.cble
+
+This means that the collect
+.B must
+be terminated by a match for the
+.cod3 until / last
+clause, or else by an explicit
+.codn @(accept) .
+
+Specifically, the collect cannot terminate due to simply running out of data,
+or exceeding a limit on the number of matches that may be collected. In
+those situations, if an
+.code until
+or
+.code last
+clause is present with
+.codn :mandatory ,
+the collect is deemed to have failed.
+
.dir coll
The
@@ -5056,7 +5117,9 @@ and any bindings from that iteration are discarded.
Like collect, coll also supports an
.cod3 until / last
clause, which propagates variable
-bindings and advances the position.
+bindings and advances the position. The
+.code :mandatory
+keyword is supported.
.code coll
clauses nest, and variables bound within a coll are available to clauses