diff options
-rw-r--r-- | match.c | 43 | ||||
-rw-r--r-- | parser.l | 9 | ||||
-rw-r--r-- | parser.y | 39 | ||||
-rw-r--r-- | txr.1 | 65 |
4 files changed, 132 insertions, 24 deletions
@@ -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); @@ -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; } @@ -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); @@ -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 |