
/* An implementation of Lisp's quasiquote. 2009-05-26 AG. */

/* NOTE: This is still considered experimental and is not part of the official
   Pure library (yet). But it's installed in the Pure library directory for
   those who need it. */

/* Copyright (c) 2009-2012 by Albert Graef <Dr.Graef@t-online.de>.

   This file is part of the Pure standard library.

   The Pure standard library is free software: you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation, either version 3 of the License,
   or (at your option) any later version.

   Pure is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
   more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

/* Our implementation tries to stay as close to the spirit of the original
   quasiquote (see, e.g., Guy L. Steele, Common Lisp the Language, 2nd
   Edition) as reasonably possible in Pure. Note that, due to the differences
   in representing function applications, splicing works somewhat differently
   from the Lisp version:

   - `,@[a,b,c]		=> a b c
   - `(,@[a,b,c]) x y	=> a b c x y
   - `x (,@[a,b,c]) y	=> x a b c y

   Thus, in our implementation splicing essentially "curries" the argument
   list, and this will work in any context.

   This version implements the quasiquote as a macro, which has the advantage
   that quasiquoted expressions are (mostly) expanded at compile time.
   However, this also means that nested quasiquotes are expanded in one go;
   see quasiquote1.pure for a more Scheme-like version which only strips away
   the outermost quasiquote level. E.g., the macro implementation produces:

   > `(a (`(b (,$(1+2)) (,$(foo (,$(1+3)) d)) e)) f);
   a (quote b (1+2) (foo 4 d) (quote e)) f

   Conversely, the implementation in quasiquote1.pure yields:

   > `(a ('`(b (,$(1+2)) (,$(foo (,$(1+3)) d)) e)) f);
   a (quasiquote (b (unquote (1+2)) (unquote (foo 4 d)) e)) f

   Whether to prefer one or the other is a matter of taste; note that both
   forms yield the same result 'a (b 3 (foo 4 d) e) f' when evaluated. */

public quasiquote unquote splice;

/* Some syntactic sugar for Lisp weenies. Note that we cannot have ',' for
   unquoting, we use ',$' instead. Also note that '`', ',$' and ',@' are just
   ordinary prefix operators, applications of these must be parenthesized
   accordingly. Thus, e.g., the Lisp expression `(f ,@(a b c)) would be
   written as `f (,@[a,b,c]) in Pure. */

prefix (') ` ,$ ,@ ;
def (`) = quasiquote; def (,$) = unquote; def (,@) = splice;

/* Note that we have to deal with both the normalized and the literal forms of
   embedded instances of quasiquote, unquote and splice here, as these forms
   may occur unevaluated inside quoted subterms. */

def quasiquote (quasiquote x)	= quasiquote (quasiquote x);
def quasiquote (`x)		= quasiquote (quasiquote x);
def quasiquote (unquote x)	= x;
def quasiquote (,$x)		= x;
def quasiquote (splice x)	= case x of
				    x = foldl1 ($) x if rlistp x;
				      = throw (bad_list_value x);
				  end;
def quasiquote (,@x)		= quasiquote (splice x);
def quasiquote (f@_ (splice x))	= case x of
				    x = foldl ($) (quasiquote f) x if rlistp x;
				      = throw (bad_list_value x);
				  end;
def quasiquote (f@_ (,@x))	= quasiquote (f (splice x));
def quasiquote (f@_ x)		= quasiquote f (quasiquote x);
def quasiquote x::matrix	= map qq (quote x) with
				    qq (unquote x) = eval x;
				    qq (f@_ x)     = qq f (qq x);
				    qq x::matrix   = map qq x;
				    qq x           = x;
				  end;
def quasiquote x		= quote x;
