summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog28
-rw-r--r--filter.c19
-rw-r--r--filter.h1
-rw-r--r--lib.c49
-rw-r--r--lib.h9
-rw-r--r--match.c42
-rw-r--r--txr.177
7 files changed, 210 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index 4d25b968..41a7a8d1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,33 @@
2011-09-26 Kaz Kylheku <kaz@kylheku.com>
+ New feature: @(deffilter)
+
+ Bugfix in @(throw) when non-symbol is thrown: exception message
+ referred to the symbol throw rather than the erroneous object.
+
+ * filter.c (build_filter_from_list, register_filter): New functions.
+
+ * filter.h (register_filter): New function declared.
+
+ * lib.c (deffilter_s): New variable defined.
+ (chain): Function changed from single list argument to variable
+ argument list to reduce the complexity of use.
+ (do_and, and): New functions.
+ (obj_init): deffilter_s initializatio added.
+
+ * lib.h (deffilter_s, and): New declarations.
+ (chain): Declaration updated to new function signature.
+ (eq): Changed from macro to inline function.
+
+ * match.c (do_output_line): Simplified expression involving chain.
+ (do_output): Likewise.
+ (match_files): Bugfix in error handling of throw.
+ Implementation of deffilter.
+
+ * txr.1: Documented deffilter.
+
+2011-09-26 Kaz Kylheku <kaz@kylheku.com>
+
Trie compression. Hash table iteration.
Bugfix in typeof.
diff --git a/filter.c b/filter.c
index 5053520f..78373cf2 100644
--- a/filter.c
+++ b/filter.c
@@ -136,6 +136,20 @@ static val build_filter(struct filter_pair *pair)
return trie;
}
+static val build_filter_from_list(val list)
+{
+ val trie = make_trie();
+ val iter;
+
+ for (iter = list; iter; iter = cdr(iter)) {
+ val pair = car(iter);
+ trie_add(trie, first(pair), second(pair));
+ }
+
+ trie_compress(&trie);
+ return trie;
+}
+
static val trie_filter_string(val filter, val str)
{
val len = length_str(str);
@@ -190,6 +204,11 @@ val filter_string(val filter, val str)
uw_throwf(error_s, lit("filter_string: invalid filter ~a"), filter, nao);
}
+val register_filter(val sym, val table)
+{
+ return sethash(filters, sym, build_filter_from_list(table));
+}
+
static struct filter_pair to_html_table[] = {
{ L"<", L"&lt;" },
{ L">", L"&gt;" },
diff --git a/filter.h b/filter.h
index dca864a3..40602f51 100644
--- a/filter.h
+++ b/filter.h
@@ -32,5 +32,6 @@ val trie_value_at(val node);
val trie_lookup_feed_char(val node, val ch);
val get_filter_trie(val sym);
val filter_string(val trie, val str);
+val register_filter(val sym, val table);
void filter_init(void);
diff --git a/lib.c b/lib.c
index 92aaf68a..77c01623 100644
--- a/lib.c
+++ b/lib.c
@@ -60,7 +60,7 @@ val all_s, some_s, none_s, maybe_s, cases_s, collect_s, until_s, coll_s;
val define_s, output_s, single_s, first_s, last_s, empty_s;
val repeat_s, rep_s, flatten_s, forget_s;
val local_s, merge_s, bind_s, cat_s;
-val try_s, catch_s, finally_s, throw_s, defex_s;
+val try_s, catch_s, finally_s, throw_s, defex_s, deffilter_s;
val error_s, type_error_s, internal_error_s;
val numeric_error_s, range_error_s;
val query_error_s, file_error_s, process_error_s;
@@ -1483,9 +1483,51 @@ static val do_chain(val fun1_list, val arg)
return arg;
}
-val chain(val fun1_list)
+val chain(val first_fun, ...)
{
- return func_f1(fun1_list, do_chain);
+ va_list vl;
+ list_collect_decl (out, iter);
+
+ if (first_fun != nao) {
+ val next_fun;
+ va_start (vl, first_fun);
+ list_collect (iter, first_fun);
+
+ while ((next_fun = va_arg(vl, val)) != nao)
+ list_collect (iter, next_fun);
+
+ va_end (vl);
+ }
+
+ return func_f1(out, do_chain);
+}
+
+static val do_and(val fun1_list, val arg)
+{
+ for (; fun1_list; fun1_list = cdr(fun1_list))
+ if (nullp(funcall1(car(fun1_list), arg)))
+ return nil;
+
+ return t;
+}
+
+val and(val first_fun, ...)
+{
+ va_list vl;
+ list_collect_decl (out, iter);
+
+ if (first_fun != nao) {
+ val next_fun;
+ va_start (vl, first_fun);
+ list_collect (iter, first_fun);
+
+ while ((next_fun = va_arg(vl, val)) != nao)
+ list_collect (iter, next_fun);
+
+ va_end (vl);
+ }
+
+ return func_f1(out, do_and);
}
val vector(val alloc)
@@ -2085,6 +2127,7 @@ static void obj_init(void)
finally_s = intern(lit("finally"), user_package);
throw_s = intern(lit("throw"), user_package);
defex_s = intern(lit("defex"), user_package);
+ deffilter_s = intern(lit("deffilter"), user_package);
error_s = intern(lit("error"), user_package);
type_error_s = intern(lit("type_error"), user_package);
internal_error_s = intern(lit("internal_error"), user_package);
diff --git a/lib.h b/lib.h
index db597da5..4ad81d7b 100644
--- a/lib.h
+++ b/lib.h
@@ -222,7 +222,7 @@ extern val all_s, some_s, none_s, maybe_s, cases_s, collect_s, until_s, coll_s;
extern val define_s, output_s, single_s, first_s, last_s, empty_s;
extern val repeat_s, rep_s, flatten_s, forget_s;
extern val local_s, merge_s, bind_s, cat_s;
-extern val try_s, catch_s, finally_s, throw_s, defex_s;
+extern val try_s, catch_s, finally_s, throw_s, defex_s, deffilter_s;
extern val error_s, type_error_s, internal_error_s;
extern val numeric_error_s, range_error_s;
extern val query_error_s, file_error_s, process_error_s;
@@ -350,7 +350,8 @@ val funcall2(val fun, val arg1, val arg2);
val reduce_left(val fun, val list, val init, val key);
val bind2(val fun2, val arg);
val bind2other(val fun2, val arg2);
-val chain(val fun1_list);
+val chain(val first_fun, ...);
+val and(val first_fun, ...);
val vector(val alloc);
val vec_get_fill(val vec);
val vec_set_fill(val vec, val fill);
@@ -392,9 +393,9 @@ val match(val spec, val data);
#define nil ((obj_t *) 0)
-#define nao ((obj_t *) (1 << TAG_SHIFT)) /* "not an object" sentinel value. */
+INLINE val eq(val a, val b) { return ((a) == (b) ? t : nil); }
-#define eq(a, b) ((a) == (b) ? t : nil)
+#define nao ((obj_t *) (1 << TAG_SHIFT)) /* "not an object" sentinel value. */
#define if2(a, b) ((a) ? (b) : nil)
diff --git a/match.c b/match.c
index e92d9687..fb533c50 100644
--- a/match.c
+++ b/match.c
@@ -805,9 +805,9 @@ static void do_output_line(val bindings, val specline,
val bind_cp = extract_bindings(bindings, elem);
val max_depth = reduce_left(func_n2(max2),
bind_cp, zero,
- chain(list(func_n1(cdr),
- func_n1(robust_length),
- nao)));
+ chain(func_n1(cdr),
+ func_n1(robust_length),
+ nao));
if (equal(max_depth, zero) && empty_clauses) {
do_output_line(bindings, empty_clauses, spec_lineno, filter, out);
@@ -871,9 +871,9 @@ static void do_output(val bindings, val specs, val filter, val out)
val bind_cp = extract_bindings(bindings, first_elem);
val max_depth = reduce_left(func_n2(max2),
bind_cp, zero,
- chain(list(func_n1(cdr),
- func_n1(robust_length),
- nao)));
+ chain(func_n1(cdr),
+ func_n1(robust_length),
+ nao));
if (equal(max_depth, zero) && empty_clauses) {
do_output(bind_cp, empty_clauses, filter, out);
@@ -1672,12 +1672,40 @@ repeat_spec_same_data:
val args = rest(rest(first_spec));
if (!symbolp(type))
sem_error(spec_linenum, lit("throw: ~a is not a type symbol"),
- first(first_spec), nao);
+ type, nao);
{
val values = mapcar(bind2other(func_n2(eval_form), bindings),
args);
uw_throw(type, values);
}
+ } else if (sym == deffilter_s) {
+ val sym = second(first_spec);
+ val table = rest(rest(first_spec));
+
+ if (!symbolp(sym))
+ sem_error(spec_linenum, lit("deffilter: ~a is not a symbol"),
+ first(first_spec), nao);
+
+ if (!all_satisfy(table, and(func_n1(listp),
+ chain(func_n1(length),
+ bind2(func_n2(eq), two),
+ nao),
+ chain(func_n1(first),
+ func_n1(stringp),
+ nao),
+ chain(func_n1(second),
+ func_n1(stringp),
+ nao),
+ nao),
+ nil))
+ sem_error(spec_linenum,
+ lit("deffilter arguments must be string pairs"),
+ nao);
+ register_filter(sym, table);
+ /* TODO: warn about replaced filter. */
+ if ((spec = rest(spec)) == nil)
+ break;
+ goto repeat_spec_same_data;
} else {
val func = uw_get_func(sym);
diff --git a/txr.1 b/txr.1
index d8c44eb1..9a654d57 100644
--- a/txr.1
+++ b/txr.1
@@ -973,6 +973,13 @@ A directive understood within an @(output) section, for repeating multi-line
text, with successive substitutions pulled from lists. A version @(rept)
produces repeated text within one line.
+.IP @(deffilter)
+This directive is used for defining named filters, which are useful
+for filtering variable substitutions in output blocks. Filters are useful
+when data must be translated between different representations that
+have different special characters or other syntax, requiring escaping
+or similar treatment.
+
.PP
.SS The Next Directive
@@ -2484,7 +2491,8 @@ This is what filtering is for. Filtering is applied to the contents of output
variables, not to any template text.
.B txr
implements named filters. Currently, the only built-in filters available are
-:to_html and :from_html. User-defined filters are not possible.
+:to_html and :from_html. User-defined filters are possible, however.
+See notes on the deffilter directive below.
To escape HTML characters in all variable substitutions occuring in
an output clause, specify :filter :to_html in the directive:
@@ -2499,6 +2507,73 @@ To filter an individual variable, add the syntax to the variable spec:
@{x :filter :to_html}
@(end)
+.SS The Deffilter Directive
+
+The deffilter directive allows a query to define a custom filter, which
+can then be used in @(output) clauses to transform substituted data.
+
+This directive's syntax is illustrated in this example:
+
+ Query: @(deffilter rot13
+ ("a" "n")
+ ("b" "o")
+ ("c" "p")
+ ("d" "q")
+ ("e" "r")
+ ("f" "s")
+ ("g" "t")
+ ("h" "u")
+ ("i" "v")
+ ("j" "w")
+ ("k" "x")
+ ("l" "y")
+ ("m" "z")
+ ("n" "a")
+ ("o" "b")
+ ("p" "c")
+ ("q" "d")
+ ("r" "e")
+ ("s" "f")
+ ("t" "g")
+ ("u" "h")
+ ("v" "i")
+ ("w" "j")
+ ("x" "k")
+ ("y" "l")
+ ("z" "m"))
+ @(collect)
+ @line
+ @(end)
+ @(output :filter rot13)
+ @(repeat)
+ @line
+ @(end)
+ @(end)
+
+ Input: hey there!
+
+ Output: url gurer!
+
+
+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.
+
+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.
+
+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.
+
+
+
.SH EXCEPTIONS