diff options
-rw-r--r-- | ChangeLog | 18 | ||||
-rw-r--r-- | match.c | 29 | ||||
-rw-r--r-- | txr.1 | 15 |
3 files changed, 57 insertions, 5 deletions
@@ -1,3 +1,21 @@ +2014-10-15 Kaz Kylheku <kaz@kylheku.com> + + * match.c (subst_vars): Fix buggy rendering of TXR Lisp expressions + that evaluate to lists. For instance `@(list)` renders to the + string "nil", and `@(list 1 2)` renders as "(1 2)". The desired + behavior is "" and "1 2", respectively. + (do_output_line): In output directives, there is a similar problem. + A @(list) in the middle of an output block turns to nil, and + a @(list 1 2) renders in parentheses as (1 2). Furthermore, + there is the additional problem that no filtering is applied + to the interpolated value. These behaviors are subject to the + compatibility option, since they change the externally visible + behavior of TXR programs. + + * txr.1: Document that empty lists in @(output) variable substitutions + turn into nothing. Document value of 100 for -C option, describing + the above issue. + 2014-10-14 Kaz Kylheku <kaz@kylheku.com> More type safety, with help from C++ compiler. @@ -1422,9 +1422,19 @@ static val subst_vars(val spec, val bindings, val filter) spec = cdr(spec); continue; } else if (sym == expr_s) { - val result = eval(rest(elem), make_env(bindings, nil, nil), elem); - spec = cons(filter_string_tree(filter, tostringp(result)), rest(spec)); - continue; + if (opt_compat && opt_compat < 100) { + val result = eval(rest(elem), make_env(bindings, nil, nil), elem); + spec = cons(filter_string_tree(filter, tostringp(result)), rest(spec)); + continue; + } else { + val str = eval(rest(elem), make_env(bindings, nil, nil), elem); + if (listp(str)) + str = cat_str(mapcar(func_n1(tostringp), str), lit(" ")); + else if (!stringp(str)) + str = tostringp(str); + spec = cons(filter_string_tree(filter, tostringp(str)), rest(spec)); + continue; + } } else { val nested = subst_vars(elem, bindings, filter); iter = list_collect_append(iter, nested); @@ -1774,8 +1784,17 @@ static void do_output_line(val bindings, val specline, val filter, val out) } } else if (directive == expr_s) { - format(out, lit("~a"), - eval(rest(elem), make_env(bindings, nil, nil), elem), nao); + if (opt_compat && opt_compat < 100) { + format(out, lit("~a"), + eval(rest(elem), make_env(bindings, nil, nil), elem), nao); + } else { + val str = cat_str(subst_vars(cons(elem, nil), + bindings, filter), nil); + if (str == nil) + sem_error(specline, lit("bad substitution: ~a"), + second(elem), nao); + put_string(str, out); + } } } break; @@ -6731,6 +6731,7 @@ A list is converted to a string in a special way: the elements are individually converted to a string and then they are catenated together. The default separator string is a single space: an alternate separation can be specified as an argument in the brace substitution syntax. +Empty lists turn into an empty string. Lists may be output within .code @(repeat) @@ -25322,6 +25323,20 @@ can be emulated is \*(TX 97. Here are values which have a special meaning as arguments to the .code -C option, along with a description of what behaviors are affected: +.IP 99 +Up to \*(TX 99, the substitution of TXR Lisp expressions in +.code @(output) +directives and in the quasistrings of the pattern language +exhibited the buggy behavior that if the TXR Lisp expression +produced a list, the list was rendered as a parenthesized +representation, or the text +.code nil +in the empty list case. Moreover, in the +.code @(output) +case, the value of TXR Lisp expressions was not subject to filtering. +Starting with \*(TX 100, these issues +are fixed, making the the behavior is consistent with +the behavior of TXR Lisp quasiliterals. .IP 97 Up to \*(TX 97, the error exception symbols such as .code file-error |