
/* cond.q: Conditionals and list/stream comprehensions.
   $Id: cond.q,v 1.14 2007/10/03 03:05:39 agraef Exp $ */

/* This file is part of the Q programming system.

   The Q programming system is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   The Q programming system 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

include stdlib, stream;

/* The "in" relation. Used in the binding clauses of list and stream
   comprehensions and iteration functions.  */

public const (in) X Y @ 2;

/* Conditionals. */

public special ifelse ~P X Y, when ~P X, unless ~P X;
public special cond CASES, condfun CASES;
public special dowhile P X;

/* Simple conditional expressions. */

ifelse P:Bool X Y	= X if P;
			= Y otherwise;

when P:Bool X		= X if P;
			= () otherwise;

unless P:Bool X		= X if not P;
			= () otherwise;

/* Multi-way conditional, similar to Lisp's cond. */

cond ((P,X)|CASES)	= X if P;
			= cond CASES otherwise;
cond _			= throw (syserr 8) otherwise;

/* Multi-way conditional function. This works like 'cond', but takes an extra
   argument Y and, iterating over the cases (P,X), yields the first X Y for
   which P X is true. */

condfun ((P,X)|CASES) Y	= X Y if P Y;
			= condfun CASES Y otherwise;
condfun _ _		= throw (syserr 8) otherwise;

/* Pattern-matching conditionals. As of Q 7.7, these are now properly
   implemented as instances of Lambda which expand to an equivalent 'condfun'
   expression. */

public type CaseFun : Lambda = special case ~X CASES, casefun CASES;
private special casesx CASES;

lambdax (case X CASES)	= '(casefun CASES X);

case X CASES		= casefun CASES X;

lambdax (casefun CASES)	= '(condfun `(casesx CASES));

casefun CASES		= condfun `(casesx CASES);

/* expand a tuple of matching cases to the corresponding condfun branches */

casesx ((P,X)|CASES)	= '((eq true.(\P.true),\P.X)|`(casesx CASES));
casesx ()		= '();
casesx _		= throw (syserr 8) otherwise;

/* A simple looping construct. */

dowhile P X		= X || dowhile P X if P;
			= () otherwise;

/* Iterate over lists and streams. */

public type DoComp : Lambda = special for Cs A;

private special forx Cs A;

lambdax (for Cs A)	= forx Cs A;

for Cs A		= `(forx Cs A);

forx () A		= 'A;
forx (X in Xs|Cs) A	= '(do (\X.`(forx Cs A)) Xs)
			    if isvar X; // will always match
			= '(do (\X.`(forx Cs A))
			    (filter (eq true.(\X.true)) Xs));
forx (X in Xs) A	= '(do (\X.A) Xs) if isvar X;
			= '(do (\X.A) (filter (eq true.(\X.true)) Xs));
forx (P|Cs) A		= '(ifelse P `(forx Cs A) ());
forx P A		= '(ifelse P A ());

/* List comprehensions. */

public type ListComp : Lambda = special listof A Cs;

private special listofx A Cs;

lambdax (listof A Cs)	= listofx A Cs;

listof A Cs		= `(listofx A Cs);

listofx A ()		= '[A];
listofx A (X in Xs|Cs)	= '(cat (map (\X.`(listofx A Cs)) Xs))
			    if isvar X; // will always match
			= '(cat (map (\X.`(listofx A Cs))
				 (filter (eq true.(\X.true)) Xs)));
listofx A (X in Xs)	= '(cat (map (\X.[A]) Xs)) if isvar X;
			= '(cat (map (\X.[A])
				 (filter (eq true.(\X.true)) Xs)));
listofx A (P|Cs)	= '(ifelse P `(listofx A Cs) []);
listofx A P		= '(ifelse P [A] []);

/* Tuple comprehensions. These are simply implemented in terms of list
   comprehensions, we just tack on a final 'tuple' call to convert the result
   to a tuple. Note that binding clauses still draw values from lists or
   streams, only the result is a tuple. */

public type TupleComp : Lambda = special tupleof A Cs;

private special tupleofx A Cs;

lambdax (tupleof A Cs)	= tupleofx A Cs;

tupleof A Cs		= `(tupleofx A Cs);

tupleofx A Cs		= '(tuple X) where 'X = listofx A Cs;

/* Stream comprehensions. */

public type StreamComp : Lambda = special streamof A Cs;

private special streamofx A Cs;

lambdax (streamof A Cs)	= streamofx A Cs;

streamof A Cs		= `(streamofx A Cs);

streamofx A ()		= '{A};
streamofx A (X in Xs|Cs)
			= '(streamcat (map (\X.`(streamofx A Cs)) Xs))
			    if isvar X; // will always match
			= '(streamcat (map (\X.`(streamofx A Cs))
				       (filter (eq true.(\X.true)) Xs)));
streamofx A (X in Xs)	= '(streamcat (map (\X.{A}) Xs)) if isvar X;
			= '(streamcat (map (\X.{A})
				       (filter (eq true.(\X.true)) Xs)));
streamofx A (P|Cs)	= '(ifelse P `(streamofx A Cs) {});
streamofx A P		= '(ifelse P {A} {});
