diff options
-rw-r--r-- | ChangeLog | 15 | ||||
-rw-r--r-- | filter.c | 23 | ||||
-rw-r--r-- | filter.h | 2 | ||||
-rw-r--r-- | match.c | 12 | ||||
-rw-r--r-- | txr.1 | 28 |
5 files changed, 68 insertions, 12 deletions
@@ -1,5 +1,20 @@ 2011-10-22 Kaz Kylheku <kaz@kylheku.com> + * filter.c (get_filter_trie): Function renamed to get_filter. A filter + is not necessarily a trie. + (string_filter, compound_filter): New functions. + (get_filter): Recognize a compound filters and return a function + which implements it. + + * filter.h (get_filter_trie): Declaration renamed. + + * match.c (format_field, v_bind, v_output): Follow get_filter_trie + rename. Error message text updated. + + * txr.1: Describe compound filters. + +2011-10-22 Kaz Kylheku <kaz@kylheku.com> + Task #11474 * filter.c (filter_equal): New function. @@ -122,9 +122,28 @@ val trie_lookup_feed_char(val node, val ch) return nil; } -val get_filter_trie(val sym) +static val string_filter(val str, val filter) { - return gethash(filters, sym); + return filter_string(filter, str); +} + +static val compound_filter(val filter_list, val string) +{ + return reduce_left(func_n2(string_filter), filter_list, string, nil); +} + +val get_filter(val spec) +{ + if (consp(spec)) { + val filter_list = mapcar(curry_12_2(func_n2(gethash), filters), spec); + + if (memqual(nil, filter_list)) + return nil; + + return curry_12_2(func_n2(compound_filter), filter_list); + } + + return gethash(filters, spec); } struct filter_pair { @@ -30,7 +30,7 @@ extern val filter_k, to_html_k, from_html_k; val trie_lookup_begin(val trie); val trie_value_at(val node); val trie_lookup_feed_char(val node, val ch); -val get_filter_trie(val sym); +val get_filter(val sym); val filter_string(val trie, val str); val filter_equal(val filter, val left, val right); val register_filter(val sym, val table); @@ -1002,10 +1002,10 @@ static val format_field(val string_or_list, val modifier, val filter) val filter_sym = getplist(plist, filter_k); if (filter_sym) { - filter = get_filter_trie(filter_sym); + filter = get_filter(filter_sym); if (!filter) { - uw_throwf(query_error_s, lit("format_field: filter ~s not known"), + uw_throwf(query_error_s, lit("format_field: ~s specifies unknown filter"), filter_sym, nao); } } @@ -2144,10 +2144,10 @@ static val v_bind(match_files_ctx c, match_files_ctx *cout) val filter_sym = getplist(keywords, filter_k); if (filter_sym) { - val filter = get_filter_trie(filter_sym); + val filter = get_filter(filter_sym); if (!filter) { - uw_throwf(query_error_s, lit("bind: filter ~s not known"), + uw_throwf(query_error_s, lit("bind: ~s specifies unknown filter"), filter_sym, nao); } @@ -2239,10 +2239,10 @@ static val v_output(match_files_ctx c, match_files_ctx *cout) val filter_sym = cdr(assoc(alist, filter_k)); if (filter_sym) { - filter = get_filter_trie(filter_sym); + filter = get_filter(filter_sym); if (!filter) - sem_error(spec_linenum, lit("unknown filter ~s"), filter_sym, nao); + sem_error(spec_linenum, lit("~s specifies unknown filter"), filter_sym, nao); } } @@ -2144,6 +2144,8 @@ Bind will see that A and B have bindings already, and so compare their contents. Since the :upcase filter is specified, both their contents will be reduced through it for the purposes of the comparison, rendering them equal. +Of course, compound filters are supported like (:from_html :upcase). + .SS The Set Directive The @(set) directive resembles bind, but is not a pattern match. It overwrites @@ -2750,8 +2752,11 @@ The following value keywords are supported: .IP :filter -The argument is a symbol, which specifies a filter to be applied to the -variable substitutions occuring within the output clause. +The argument can be a symbol, which specifies a filter to be applied to +the variable substitutions occuring within the output clause. +The argument can also be a list of filter symbols, which specifies +that multiple filters are to be applied, in left to right order. + See the later sections Output Filtering below, and The Deffilter Directive. .IP :into @@ -2788,7 +2793,9 @@ that field, if the width is specified as a positive number, and right-adjusted if the width is specified as negative. An output variable may specify a filter which overrides any filter established -for the output clause. The syntax for this is @(NAME :filter <filtername>}. +for the output clause. The syntax for this is @(NAME :filter <filterspec>}. +The filter specification syntax is the same as in the output clause. +See Output Filtering below. .SS The Repeat Directive @@ -2979,6 +2986,21 @@ To filter an individual variable, add the syntax to the variable spec: @{x :filter :to_html} @(end) +Multiple filters can be applied at the same time. For instance: + + @(output) + @{x :filter (:upcase :to_html)} + @(end) + +This will fold the contents of x to upper case, and then encode any special +characters into HTML. Beware of combinations that do not make sense. +For instance, suppose the original text is HTML, containing codes +like '"'. The compound filter (:upcase :from_html) will not work +because '"' will turn to '"' which no longer be recognized +by the :from_html filter, because the entity names in HTML codes +are case-sensitive. + + .SS The Deffilter Directive The deffilter directive allows a query to define a custom filter, which |