summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog18
-rw-r--r--filter.c4
-rw-r--r--lib.c20
-rw-r--r--lib.h2
-rw-r--r--match.c14
-rw-r--r--txr.132
6 files changed, 70 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index 6812cdf3..13aa17e9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
2011-10-25 Kaz Kylheku <kaz@kylheku.com>
+ Shorthand for filters which map multiple texts to a common
+ replacement text.
+
+ * filter.c (build_filter_from_list): Allow tuples to denote
+ multiple keys mapping to the same value.
+
+ * lib.c (do_curry_123_2, do_curry_123_1): New static functions.
+ (curry_123_2, curry_123_1): New functions.
+
+ * lib.h (curry_123_2, curry_123_1): New functions declared.
+
+ * match.c (v_deffilter): Allow tuples of strings rather than
+ just pairs.
+
+ * txr.1: Updated.
+
+2011-10-25 Kaz Kylheku <kaz@kylheku.com>
+
* parser.y: Remove mention of nonexistent terminal \\
from %right associativity clause.
diff --git a/filter.c b/filter.c
index 85704240..dc9c2ac2 100644
--- a/filter.c
+++ b/filter.c
@@ -184,8 +184,8 @@ static val build_filter_from_list(val list)
val iter;
for (iter = list; iter; iter = cdr(iter)) {
- val pair = car(iter);
- trie_add(trie, first(pair), second(pair));
+ val tuple = reverse(car(iter));
+ mapcar(curry_123_2(func_n3(trie_add), trie, first(tuple)), rest(tuple));
}
trie_compress(&trie);
diff --git a/lib.c b/lib.c
index d5a6634a..6af8dd58 100644
--- a/lib.c
+++ b/lib.c
@@ -1598,6 +1598,16 @@ val curry_12_1(val fun2, val arg2)
return func_f1(cons(fun2, arg2), do_curry_12_1);
}
+static val do_curry_123_3(val fcons, val arg3)
+{
+ return funcall3(car(fcons), car(cdr(fcons)), cdr(cdr(fcons)), arg3);
+}
+
+val curry_123_3(val fun3, val arg1, val arg2)
+{
+ return func_f1(cons(fun3, cons(arg1, arg2)), do_curry_123_3);
+}
+
static val do_curry_123_2(val fcons, val arg2)
{
return funcall3(car(fcons), car(cdr(fcons)), arg2, cdr(cdr(fcons)));
@@ -1608,6 +1618,16 @@ val curry_123_2(val fun3, val arg1, val arg3)
return func_f1(cons(fun3, cons(arg1, arg3)), do_curry_123_2);
}
+static val do_curry_123_1(val fcons, val arg1)
+{
+ return funcall3(car(fcons), arg1, car(cdr(fcons)), cdr(cdr(fcons)));
+}
+
+val curry_123_1(val fun3, val arg2, val arg3)
+{
+ return func_f1(cons(fun3, cons(arg2, arg3)), do_curry_123_1);
+}
+
static val do_curry_123_23(val fcons, val arg2, val arg3)
{
return funcall3(car(fcons), cdr(fcons), arg2, arg3);
diff --git a/lib.h b/lib.h
index 76ff65df..50789367 100644
--- a/lib.h
+++ b/lib.h
@@ -390,7 +390,9 @@ val reduce_left(val fun, val list, val init, val key);
Other variations follow by analogy. */
val curry_12_2(val fun2, val arg);
val curry_12_1(val fun2, val arg2);
+val curry_123_3(val fun3, val arg1, val arg2);
val curry_123_2(val fun3, val arg1, val arg3);
+val curry_123_1(val fun3, val arg2, val arg3);
val curry_123_23(val fun3, val arg1);
val curry_1234_34(val fun3, val arg1, val arg2);
val chain(val first_fun, ...);
diff --git a/match.c b/match.c
index 42f42b90..7bda5f4d 100644
--- a/match.c
+++ b/match.c
@@ -2555,18 +2555,14 @@ static val v_deffilter(match_files_ctx c, match_files_ctx *cout)
if (!all_satisfy(table, andf(func_n1(listp),
chain(func_n1(length),
- curry_12_2(func_n2(eq), two),
- nao),
- chain(func_n1(first),
- func_n1(stringp),
- nao),
- chain(func_n1(second),
- func_n1(stringp),
- nao),
+ curry_12_1(func_n2(ge), two), nao),
+ chain(func_n1(rest),
+ curry_123_1(func_n3(all_satisfy),
+ func_n1(stringp), nil), nao),
nao),
nil))
sem_error(spec_linenum,
- lit("deffilter arguments must be string pairs"),
+ lit("deffilter arguments must be lists of at least two strings"),
nao);
register_filter(sym, table);
/* TODO: warn about replaced filter. */
diff --git a/txr.1 b/txr.1
index 89dd966f..b82c85e6 100644
--- a/txr.1
+++ b/txr.1
@@ -3124,23 +3124,37 @@ This directive's syntax is illustrated in this example:
The deffilter symbol must be followed by the name of the filter to be defined,
-followed by pairs of strings. Each pair specifies a piece of text to be
-filtered from the left hand side to the right hand side.
+followed by tuples of strings. Each tuple specifies one or more texts
+which are mapped to a replacement text. For instance, the following specifies
+a telephone keypad mapping from upper case letters to digits.
+
+ @(deffilter alpha_to_phone ("E" "0")
+ ("J" "N" "Q" "1")
+ ("R" "W" "X" "2")
+ ("D" "S" "Y" "3")
+ ("F" "T" "4")
+ ("A" "M" "5")
+ ("C" "I" "V" "6")
+ ("B" "K" "U" "7")
+ ("L" "O" "P" "8")
+ ("G" "H" "Z" "9"))
Filtering works using a longest match algorithm. The input is scanned from left
to right, and the longest piece of text is identified at every character
position which matches a string on the left hand side, and that text is
-replaced with the right hand string.
+replaced with its associated replacement text. The scanning then continues
+at the first character after the matched text.
If none of the strings matches at a given character position, then that
-character is passed through untranslated, and the scan continues at the next
-character in the input.
-
-If a filter definition accidentally
-contains two or more repetitions of the same left hand string with different
-right hand translations, the later ones take precedence. No warning is issued.
+character is passed through the filter untranslated, and the scan continues at
+the next character in the input.
+Filtering is not in-place but rather instantiates a new text, and so
+replacement text is not re-scanned for more replacements.
+If a filter definition accidentally contains two or more repetitions of the
+same left hand string with different right hand translations, the later ones
+take precedence. No warning is issued.
.SH EXCEPTIONS