From df9f85b3b720a9ebf7381b2cad7a9680ad0e31bf Mon Sep 17 00:00:00 2001
From: Kaz Kylheku <kaz@kylheku.com>
Date: Sat, 26 Nov 2016 08:40:22 -0800
Subject: bugfix: quasilit read/print consistency, part 1.

The bug is that `@@@a` prints as `@@a` which
reads as a different object.

In this patch we simplify how quasiliterals are represented.
Embedded expressions are no longer (sys:expr E), just E.
Meta-numbers N and variables V are still (sys:var N).

However `@@a` and `@a` remain equivalent.

* eval.c (subst_vars): No need to look for expr_s;
just evaluate a compound form. The recursive nested
case is unnecessary and is removed.
(expand_quasi): Do nothandle expr_s; it is not
part of the quasi syntax any more.

* lib.c (out_quasi_str): Do not look for expr_s in the
quasi syntax; just print any expression with a @
the fallback case.

* match.c (tx_subst_vars): Analogous changes to those
done in subst_vars in eval.c.

* parser.y (quasi_meta_helper): Static function removed.
This was responsible for the issue due to stripping a
level of meta from expressions already having a meta
on them.
(quasi_item): In the `@` n_expr syntax case, no longer
call quasi_meta_helper. The remaining logic is simple
enough to put in line. Symbols and integers get wrapped
with (sys:var ...); other expressions are integrated
into the syntax as-is.
---
 eval.c   | 15 +++------------
 lib.c    |  4 ++--
 match.c  | 11 +++--------
 parser.y | 25 +++++--------------------
 4 files changed, 13 insertions(+), 42 deletions(-)

diff --git a/eval.c b/eval.c
index a5f74b5a..6fe12132 100644
--- a/eval.c
+++ b/eval.c
@@ -2365,19 +2365,14 @@ val subst_vars(val forms, val env, val filter)
         iter = list_collect_append(iter, nested);
         forms = cdr(forms);
         continue;
-      } else if (sym == expr_s) {
-        val str = eval(rest(form), env, form);
+      } else {
+        val str = eval(form, env, form);
         if (listp(str))
           str = cat_str(mapcar(func_n1(tostringp), str), lit(" "));
         else if (!stringp(str))
           str = tostringp(str);
         forms = cons(filter_string_tree(filter, tostringp(str)), rest(forms));
         continue;
-      } else {
-        val nested = subst_vars(form, env, filter);
-        iter = list_collect_append(iter, nested);
-        forms = cdr(forms);
-        continue;
       }
     } else if (bindable(form)) {
       forms = cons(cons(var_s, cons(form, nil)), cdr(forms));
@@ -2983,12 +2978,8 @@ static val expand_quasi(val quasi_forms, val menv)
 
     if (consp(form)) {
       val sym = car(form);
-      if (sym == expr_s) {
-        val expr_ex = expand(rest(form), menv);
 
-        if (expr_ex != rest(form))
-          form_ex = rlcp(cons(sym, expr_ex), form);
-      } else if (sym == var_s) {
+      if (sym == var_s) {
         val param = second(form);
         val mods = third(form);
         val param_ex = expand(param, menv);
diff --git a/lib.c b/lib.c
index 641b7337..c510ba73 100644
--- a/lib.c
+++ b/lib.c
@@ -9481,9 +9481,9 @@ static void out_quasi_str(val args, val out, struct strm_ctx *ctx)
         }
         if (need_brace)
           put_char(chr('}'), out);
-      } else if (sym == expr_s) {
+      } else {
         put_char(chr('@'), out);
-        obj_print_impl(rest(elem), out, nil, ctx);
+        obj_print_impl(elem, out, nil, ctx);
       }
     } else {
       obj_print_impl(elem, out, nil, ctx);
diff --git a/match.c b/match.c
index d9df8764..93b84a07 100644
--- a/match.c
+++ b/match.c
@@ -1493,13 +1493,13 @@ static val tx_subst_vars(val spec, val bindings, val filter)
           iter = list_collect_append(iter, nested);
           spec = cdr(spec);
           continue;
-        } else if (sym == expr_s) {
+        } else {
           if (opt_compat && opt_compat < 100) {
-            val result = tleval(spec, rest(elem), bindings);
+            val result = tleval(spec, elem, bindings);
             spec = cons(filter_string_tree(filter, tostringp(result)), rest(spec));
             continue;
           } else {
-            val str = tleval(spec, rest(elem), bindings);
+            val str = tleval(spec, elem, bindings);
             if (listp(str))
               str = cat_str(mapcar(func_n1(tostringp), str), lit(" "));
             else if (!stringp(str))
@@ -1507,11 +1507,6 @@ static val tx_subst_vars(val spec, val bindings, val filter)
             spec = cons(filter_string_tree(filter, tostringp(str)), rest(spec));
             continue;
           }
-        } else {
-          val nested = tx_subst_vars(elem, bindings, filter);
-          iter = list_collect_append(iter, nested);
-          spec = cdr(spec);
-          continue;
         }
       }
 
diff --git a/parser.y b/parser.y
index 8d3e5a89..b54c7bf8 100644
--- a/parser.y
+++ b/parser.y
@@ -68,7 +68,6 @@ static val rlrec(parser_t *, val form, val line);
 static wchar_t char_from_name(const wchar_t *name);
 static val make_expr(parser_t *, val sym, val rest, val lineno);
 static val check_for_include(val spec_rev);
-static val quasi_meta_helper(val obj);
 static void misplaced_consing_dot_check(scanner_t *scanner, val term_atom_cons);
 
 #if YYBISON
@@ -1165,7 +1164,11 @@ quasi_item : litchars           { $$ = lit_char_helper($1); }
            | q_var              { $$ = $1; }
            | METANUM            { $$ = cons(var_s, cons($1, nil));
                                   rl($$, num(parser->lineno)); }
-           | '@' n_expr         { $$ = quasi_meta_helper($2); }
+           | '@' n_expr         { if (integerp($2) || symbolp($2))
+                                    $$ = rlcp_tree(cons(var_s, cons($2, nil)),
+                                                   $2);
+                                  else
+                                    $$ = $2; }
            ;
 
 litchars : LITCHAR              { $$ = rl(cons(chr($1), nil), num(parser->lineno)); }
@@ -1644,24 +1647,6 @@ static val check_for_include(val spec_rev)
   return spec_rev;
 }
 
-static val quasi_meta_helper(val obj)
-{
-  if (integerp(obj) || symbolp(obj))
-    goto var;
-
-  if (atom(obj))
-    goto expr;
-
-  if (first(obj) == var_s || first(obj) == expr_s)
-    return obj;
-
-expr:
-  return rlcp(cons(expr_s, obj), obj);
-
-var:
-  return rlcp_tree(cons(var_s, cons(obj, nil)), obj);
-}
-
 static void misplaced_consing_dot_check(scanner_t *scanner, val term_atom_cons)
 {
   if (car(term_atom_cons) != unique_s) {
-- 
cgit v1.2.3