From 0b81b8b4c39b6c60cbbe8de5a6a785beb43c3dbb Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Thu, 29 Dec 2011 23:28:45 -0800 Subject: New functionality: mod and modlast directives in repeat and rep. * eval.c (eval_init): Use new symbol variable mod_s instead of calling intern. * match.c (mod_s, modlast_s): Symbol variables defined. (do_output_line): mod and modlast directives implemented under rep. (do_output): likewise under repeat. (syms_init): Initialize new symbol variables. * match.h (mod_s, modlast_s): Declared. * parser.l (MOD, MODLAST): Parse new token types. * parser.y (MOD, MODLAST): New tokens. (repeat_parts_opt, rep_parts_opt): New syntax. (repeat_rep_helper): Handle mod and modlast syntax. * txr.1: Updated. * txr.vim: Updated. --- ChangeLog | 24 +++++++++++++ eval.c | 2 +- match.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- match.h | 2 +- parser.l | 12 +++++++ parser.y | 35 ++++++++++++++++-- txr.1 | 33 ++++++++++++----- txr.vim | 2 +- 8 files changed, 202 insertions(+), 30 deletions(-) diff --git a/ChangeLog b/ChangeLog index 99ae385e..13c01dae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2011-12-29 Kaz Kylheku + + New functionality: mod and modlast directives in repeat and rep. + + * eval.c (eval_init): Use new symbol variable mod_s instead + of calling intern. + + * match.c (mod_s, modlast_s): Symbol variables defined. + (do_output_line): mod and modlast directives implemented under rep. + (do_output): likewise under repeat. + (syms_init): Initialize new symbol variables. + + * match.h (mod_s, modlast_s): Declared. + + * parser.l (MOD, MODLAST): Parse new token types. + + * parser.y (MOD, MODLAST): New tokens. + (repeat_parts_opt, rep_parts_opt): New syntax. + (repeat_rep_helper): Handle mod and modlast syntax. + + * txr.1: Updated. + + * txr.vim: Updated. + 2011-12-29 Kaz Kylheku * parser.y (repeat_rep_helper): Bugfix. Circular lists diff --git a/eval.c b/eval.c index f3025be0..e5672f24 100644 --- a/eval.c +++ b/eval.c @@ -1264,7 +1264,7 @@ void eval_init(void) reg_fun(intern(lit("*"), user_package), func_n0v(mulv)); reg_fun(intern(lit("abs"), user_package), func_n1(abso)); reg_fun(intern(lit("trunc"), user_package), func_n2(trunc)); - reg_fun(intern(lit("mod"), user_package), func_n2(mod)); + reg_fun(mod_s, func_n2(mod)); reg_fun(intern(lit("expt"), user_package), func_n0v(exptv)); reg_fun(intern(lit("exptmod"), user_package), func_n3(exptmod)); reg_fun(intern(lit("sqrt"), user_package), func_n1(isqrt)); diff --git a/match.c b/match.c index c53d16b0..e87b7496 100644 --- a/match.c +++ b/match.c @@ -55,7 +55,7 @@ int opt_arraydims = 1; val decline_k, next_spec_k, repeat_spec_k; val mingap_k, maxgap_k, gap_k, mintimes_k, maxtimes_k, times_k; val lines_k, chars_k; -val text_s, choose_s, gather_s, do_s; +val text_s, choose_s, gather_s, do_s, mod_s, modlast_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; @@ -1533,11 +1533,14 @@ static void do_output_line(val bindings, val specline, val filter, val out) second(elem), nao); put_string(out, str); } else if (directive == rep_s) { - val main_clauses = second(elem); - val single_clauses = third(elem); - val first_clauses = fourth(elem); - val last_clauses = fifth(elem); - val empty_clauses = sixth(elem); + val clauses = cdr(elem); + val main_clauses = pop(&clauses); + val single_clauses = pop(&clauses); + val first_clauses = pop(&clauses); + val last_clauses = pop(&clauses); + val empty_clauses = pop(&clauses); + val mod_clauses = pop(&clauses); + val modlast_clauses = pop(&clauses); val bind_cp = extract_bindings(bindings, elem); val max_depth = reduce_left(func_n2(max2), bind_cp, zero, @@ -1559,8 +1562,49 @@ static void do_output_line(val bindings, val specline, val filter, val out) if (i == 0 && first_clauses) { do_output_line(bind_a, first_clauses, filter, out); - } else if (i == c_num(max_depth) - 1 && last_clauses) { - do_output_line(bind_a, last_clauses, filter, out); + } else if (i == c_num(max_depth) - 1 && + (last_clauses || modlast_clauses)) { + if (modlast_clauses) { + val iter; + list_collect_decl (active_mods, ptail); + + for (iter = modlast_clauses; iter != nil; iter = cdr(iter)) { + val clause = car(iter); + val args = first(clause); + val n = txeval(args, first(args), bind_a); + val m = txeval(args, second(args), bind_a); + + if (eql(mod(num_fast(i), m), n)) + list_collect_append (ptail, rest(clause)); + } + + if (active_mods) + do_output_line(bind_a, active_mods, filter, out); + else if (last_clauses) + do_output_line(bind_a, last_clauses, filter, out); + else + goto mod_fallback; + } else { + do_output_line(bind_a, last_clauses, filter, out); + } + } else if (mod_clauses) mod_fallback: { + val iter; + list_collect_decl (active_mods, ptail); + + for (iter = mod_clauses; iter != nil; iter = cdr(iter)) { + val clause = car(iter); + val args = first(clause); + val n = txeval(args, first(args), bind_a); + val m = txeval(args, second(args), bind_a); + + if (eql(mod(num_fast(i), m), n)) + list_collect_append (ptail, rest(clause)); + } + + if (active_mods) + do_output_line(bind_a, active_mods, filter, out); + else + do_output_line(bind_a, main_clauses, filter, out); } else { do_output_line(bind_a, main_clauses, filter, out); } @@ -1600,11 +1644,14 @@ static void do_output(val bindings, val specs, val filter, val out) val sym = first(first_elem); if (sym == repeat_s) { - val main_clauses = second(first_elem); - val single_clauses = third(first_elem); - val first_clauses = fourth(first_elem); - val last_clauses = fifth(first_elem); - val empty_clauses = sixth(first_elem); + val clauses = cdr(first_elem); + val main_clauses = pop(&clauses); + val single_clauses = pop(&clauses); + val first_clauses = pop(&clauses); + val last_clauses = pop(&clauses); + val empty_clauses = pop(&clauses); + val mod_clauses = pop(&clauses); + val modlast_clauses = pop(&clauses); val bind_cp = extract_bindings(bindings, first_elem); val max_depth = reduce_left(func_n2(max2), bind_cp, zero, @@ -1626,8 +1673,50 @@ static void do_output(val bindings, val specs, val filter, val out) if (i == 0 && first_clauses) { do_output(bind_a, first_clauses, filter, out); - } else if (i == c_num(max_depth) - 1 && last_clauses) { - do_output(bind_a, last_clauses, filter, out); + } else if (i == c_num(max_depth) - 1 && + (last_clauses || modlast_clauses)) + { + if (modlast_clauses) { + val iter; + list_collect_decl (active_mods, ptail); + + for (iter = modlast_clauses; iter != nil; iter = cdr(iter)) { + val clause = car(iter); + val args = first(clause); + val n = txeval(args, first(args), bind_a); + val m = txeval(args, second(args), bind_a); + + if (eql(mod(num_fast(i), m), n)) + list_collect_append (ptail, rest(clause)); + } + + if (active_mods) + do_output(bind_a, active_mods, filter, out); + else if (last_clauses) + do_output(bind_a, last_clauses, filter, out); + else + goto mod_fallback; + } else { + do_output(bind_a, last_clauses, filter, out); + } + } else if (mod_clauses) mod_fallback: { + val iter; + list_collect_decl (active_mods, ptail); + + for (iter = mod_clauses; iter != nil; iter = cdr(iter)) { + val clause = car(iter); + val args = first(clause); + val n = txeval(args, first(args), bind_a); + val m = txeval(args, second(args), bind_a); + + if (eql(mod(num_fast(i), m), n)) + list_collect_append (ptail, rest(clause)); + } + + if (active_mods) + do_output(bind_a, active_mods, filter, out); + else + do_output(bind_a, main_clauses, filter, out); } else { do_output(bind_a, main_clauses, filter, out); } @@ -3323,6 +3412,9 @@ static void syms_init(void) filter_s = intern(lit("filter"), user_package); noval_s = intern(lit("noval"), system_package); + + mod_s = intern(lit("mod"), system_package); + modlast_s = intern(lit("modlast"), system_package); } static void dir_tables_init(void) diff --git a/match.h b/match.h index 8d8e9476..55deef7a 100644 --- a/match.h +++ b/match.h @@ -24,7 +24,7 @@ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -extern val text_s, choose_s, gather_s, do_s; +extern val text_s, choose_s, gather_s, do_s, mod_s, modlast_s; val format_field(val string_or_list, val modifier, val filter); val match_funcall(val name, val arg, val other_args); int extract(val spec, val filenames, val bindings); diff --git a/parser.l b/parser.l index 0ade291d..c571691c 100644 --- a/parser.l +++ b/parser.l @@ -322,6 +322,18 @@ UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} return EMPTY; } +\({WS}mod/{ID_END} { + yy_push_state(NESTED); + yylval.lineno = lineno; + return MOD; + } + +\({WS}modlast/{ID_END} { + yy_push_state(NESTED); + yylval.lineno = lineno; + return MODLAST; + } + \({WS}define/{ID_END} { yy_push_state(NESTED); yylval.lineno = lineno; diff --git a/parser.y b/parser.y index ef7fefdf..13dff805 100644 --- a/parser.y +++ b/parser.y @@ -69,8 +69,8 @@ static val parsed_spec; %token SPACE TEXT IDENT KEYWORD METAVAR %token ALL SOME NONE MAYBE CASES CHOOSE GATHER %token AND OR END COLLECT -%token UNTIL COLL OUTPUT REPEAT REP SINGLE FIRST LAST EMPTY DEFINE -%token TRY CATCH FINALLY +%token UNTIL COLL OUTPUT REPEAT REP SINGLE FIRST LAST EMPTY +%token MOD MODLAST DEFINE TRY CATCH FINALLY %token ERRTOK /* deliberately not used in grammar */ %token HASH_BACKSLASH @@ -494,6 +494,18 @@ repeat_parts_opt : SINGLE newl out_clauses_opt repeat_parts_opt { $$ = cons(cons(empty_s, $3), $4); rl($$, num($1)); } + | MOD exprs_opt ')' + newl + out_clauses_opt + repeat_parts_opt { $$ = cons(cons(mod_s, + cons($2, $5)), $6); + rl($$, num($1)); } + | MODLAST exprs_opt ')' + newl + out_clauses_opt + repeat_parts_opt { $$ = cons(cons(modlast_s, + cons($2, $5)), $6); + rl($$, num($1)); } | /* empty */ { $$ = nil; } ; @@ -548,6 +560,16 @@ rep_parts_opt : SINGLE o_elems_opt2 | EMPTY o_elems_opt2 rep_parts_opt { $$ = cons(cons(empty_s, $2), $3); rl($$, num($1)); } + | MOD exprs_opt ')' + o_elems_opt2 + rep_parts_opt { $$ = cons(cons(mod_s, + cons($2, $4)), $5); + rl($$, num($1)); } + | MODLAST exprs_opt ')' + o_elems_opt2 + rep_parts_opt { $$ = cons(cons(modlast_s, + cons($2, $4)), $5); + rl($$, num($1)); } | /* empty */ { $$ = nil; } ; @@ -788,6 +810,8 @@ static val repeat_rep_helper(val sym, val main, val parts) val first_parts = nil; val last_parts = nil; val empty_parts = nil; + val mod_parts = nil; + val modlast_parts = nil; val iter; for (iter = parts; iter != nil; iter = cdr(iter)) { @@ -803,12 +827,17 @@ static val repeat_rep_helper(val sym, val main, val parts) last_parts = nappend2(last_parts, clauses); else if (sym == empty_s) empty_parts = nappend2(empty_parts, clauses); + else if (sym == mod_s) + mod_parts = cons(clauses, mod_parts); + else if (sym == modlast_s) + modlast_parts = cons(clauses, modlast_parts); else abort(); } return list(sym, main, single_parts, first_parts, - last_parts, empty_parts, nao); + last_parts, empty_parts, + nreverse(mod_parts), nreverse(modlast_parts), nao); } static val o_elems_transform(val o_elems) diff --git a/txr.1 b/txr.1 index 0a9ab615..1b360ea8 100644 --- a/txr.1 +++ b/txr.1 @@ -3354,17 +3354,32 @@ repetition. If the repeat produces no repetitions, then the body of this clause is output. If this clause is absent or empty, the repeat produces no output. +.IP @(mod n m) +The forms n and m are expressions that evaluate to integers. The value of +m should be nonzero. The clause denoted this way is active if the repetition +modulo m is equal to n. The first repetition is numbered zero. +For instance the clause headed by @(mod 0 2) will be used on repetitions +0, 2, 4, 6, ... and @(mod 1 2) will be used on repetitions 1, 3, 5, 7, ... + +.IP @(modlast n m) +The meaning of n and m is the same as in @(mod n m), but one more condition +is imposed. This clause is used if the repetition modulo m is +equal to n, and if it is the last repetition. + .PP The precedence among the clauses which take an iteration is: -single > first > last > main. That is if two or more of these clauses -can apply to a repetition, then the leftmost one in this precedence list -applies. For instance, if there is just a single repetition, then any of these -special clause types can apply to that repetition, since it is the only -repetition, as well as the first and last one. In this situation, if -there is a single clause present, then the repetition is processed -using that clause. Otherwise, if there is a first clause present, that -clause is used. Failing that, a last clause applies. Only if none of these -clauses are present will the repetition be processed using the main clause. +single > first > mod > modlast > last > main. That is if two or more of these +clauses can apply to a repetition, then the leftmost one in this precedence +list applies. For instance, if there is just a single repetition, then any of +these special clause types can apply to that repetition, since it is the only +repetition, as well as the first and last one. In this situation, if there is a +@(single) clause present, then the repetition is processed using that clause. +Otherwise, if there is a @(first) clause present, that clause is used. Failing +that, @(mod) is used if there is such a clause and its numeric conditions +are satisfied. If not then @(modlast) clauses are considered, and if there +are none, or none of them activate, then @(last) is considered. If none +of those clauses are present or apply, then the repetition is processed +using the main clause. .SS Nested Repeats diff --git a/txr.vim b/txr.vim index 2dde4106..a4659ba9 100644 --- a/txr.vim +++ b/txr.vim @@ -21,7 +21,7 @@ syn keyword txr_keyword contained skip trailer freeform block accept fail syn keyword txr_keyword contained next some all none and or syn keyword txr_keyword contained maybe cases choose gather collect coll until last end syn keyword txr_keyword contained flatten forget local merge bind set cat output -syn keyword txr_keyword contained repeat rep first last single empty +syn keyword txr_keyword contained repeat rep first last single empty mod modlast syn keyword txr_keyword contained define try catch finally throw syn keyword txr_keyword contained defex throw deffilter filter eof eol do -- cgit v1.2.3