diff options
-rw-r--r-- | ChangeLog | 24 | ||||
-rw-r--r-- | eval.c | 2 | ||||
-rw-r--r-- | match.c | 122 | ||||
-rw-r--r-- | match.h | 2 | ||||
-rw-r--r-- | parser.l | 12 | ||||
-rw-r--r-- | parser.y | 35 | ||||
-rw-r--r-- | txr.1 | 33 | ||||
-rw-r--r-- | txr.vim | 2 |
8 files changed, 202 insertions, 30 deletions
@@ -1,5 +1,29 @@ 2011-12-29 Kaz Kylheku <kaz@kylheku.com> + 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 <kaz@kylheku.com> + * parser.y (repeat_rep_helper): Bugfix. Circular lists were being created here when clauses of the same kind appear multiple times. The problem is that append2 no longer copies the second list, @@ -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)); @@ -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) @@ -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); @@ -322,6 +322,18 @@ UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} return EMPTY; } +<SPECIAL>\({WS}mod/{ID_END} { + yy_push_state(NESTED); + yylval.lineno = lineno; + return MOD; + } + +<SPECIAL>\({WS}modlast/{ID_END} { + yy_push_state(NESTED); + yylval.lineno = lineno; + return MODLAST; + } + <SPECIAL>\({WS}define/{ID_END} { yy_push_state(NESTED); yylval.lineno = lineno; @@ -69,8 +69,8 @@ static val parsed_spec; %token <lexeme> SPACE TEXT IDENT KEYWORD METAVAR %token <lineno> ALL SOME NONE MAYBE CASES CHOOSE GATHER %token <lineno> AND OR END COLLECT -%token <lineno> UNTIL COLL OUTPUT REPEAT REP SINGLE FIRST LAST EMPTY DEFINE -%token <lineno> TRY CATCH FINALLY +%token <lineno> UNTIL COLL OUTPUT REPEAT REP SINGLE FIRST LAST EMPTY +%token <lineno> MOD MODLAST DEFINE TRY CATCH FINALLY %token <lineno> ERRTOK /* deliberately not used in grammar */ %token <lineno> 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) @@ -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 @@ -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 |