summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog24
-rw-r--r--eval.c2
-rw-r--r--match.c122
-rw-r--r--match.h2
-rw-r--r--parser.l12
-rw-r--r--parser.y35
-rw-r--r--txr.133
-rw-r--r--txr.vim2
8 files changed, 202 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog
index 99ae385e..13c01dae 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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,
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;
}
+<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;
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 <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)
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