[comp.sources.unix] v10i036: Interpreted Functional Programming lanuage, Part 03/07

rs@uunet.UU.NET (Rich Salz) (07/07/87)

Mod.sources: Volume 10, Number 36
Submitted by: robison@b.cs.uiuc.edu (Arch Robison)
Archive-name: ifp/Part03

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh.
# The following files will be created:
#	interp/F_arith.c
#	interp/F_misc.c
#	interp/F_pred.c
#	interp/F_seq.c
#	interp/F_ss.c
#	interp/F_string.c
#	interp/F_subseq.c
export PATH; PATH=/bin:$PATH
mkdir interp
if test -f 'interp/F_arith.c'
then
	echo shar: over-writing existing file "'interp/F_arith.c'"
fi
cat << \SHAR_EOF > 'interp/F_arith.c'

/****** F_arith.c *****************************************************/
/**                                                                  **/
/**                    University of Illinois                        **/
/**                                                                  **/
/**                Department of Computer Science                    **/
/**                                                                  **/
/**   Tool: IFP                         Version: 0.5                 **/
/**                                                                  **/
/**   Author:  Arch D. Robison          Date:   May 1, 1985          **/
/**                                                                  **/
/**   Revised by: Arch D. Robison       Date:  June 4, 1986          **/
/**                                                                  **/
/**   Principal Investigators: Prof. R. H. Campbell                  **/
/**                            Prof. W. J. Kubitz                    **/
/**                                                                  **/
/**                                                                  **/
/**------------------------------------------------------------------**/
/**   (C) Copyright 1987  University of Illinois Board of Trustees   **/
/**                       All Rights Reserved.                       **/
/**********************************************************************/

#include <stdio.h>
#include <math.h>
#include <errno.h>
#include "struct.h"
#include "node.h"

#if OPSYS!=CTSS
extern int errno;      /* exists somewhere in UNIX */
#endif

/* NOTE - function Dyadic assumes integers are in two's complement form! */

private F_Minus (), F_AddN (), Monadic (), Dyadic (), F_Sum ();

private OpDef OpArith [] = {
#if OPSYS!=CTSS
   {"ln",       0,      Monadic},
   {"exp",      1,      Monadic},
   {"sqrt",     2,      Monadic},
   {"sin",      3,      Monadic},
   {"cos",      4,      Monadic},
   {"tan",      5,      Monadic},
   {"arcsin",   6,      Monadic},
   {"arccos",   7,      Monadic},
   {"arctan",   8,      Monadic},
#endif
   {"minus",    -1,     F_Minus},
   {"add1",     1,      F_AddN},
   {"sub1",     -1,     F_AddN},
   {"+",        0,      Dyadic},
   {"-",        1,      Dyadic},
   {"*",        2,      Dyadic},
   {"%",        3,      Dyadic},
#if OPSYS!=CTSS
   {"mod",      4,      Dyadic},
   {"div",      5,      Dyadic},
#endif
   {"min",      6,      Dyadic},
   {"max",      7,      Dyadic},
#if OPSYS!=CTSS
   {"power",    8,      Dyadic},
#endif
   {"sum",      -1,     F_Sum}
};


/*
 * Monadic
 *
 * Evaluate a monadic function
 *
 * Input
 *      InOut = argument to apply function
 *      Op = operation - see array F_Name in code for values
 *
 * Output
 *      InOut = result of applying function
 */
private Monadic (InOut,Op)
   ObjectPtr InOut;
   int Op;
   {
      double X,Z;
      register int E;

      if (GetDouble (InOut,&X)) FunError ("not numeric",InOut);
      else {
	 E = 0;
	 switch (Op) {
#if OPSYS!=CTSS
	    case 0:                     /* base e log */
	       if (X <= 0) E = EDOM;
	       else Z = log (X);
	       break;
	    case 1:                     /* base e power */
	       if (X >= LNMAXFLOAT) E = ERANGE;
	       else Z = exp (X);
	       break;
	    case 2:                     /* square root */
	       if (X < 0) E = EDOM;
	       else Z = sqrt (X);
	       break;
	    case 3:                     /* sin */
	       Z = sin (X);
	       break;
	    case 4:                     /* cos */
	       Z = cos (X);
	       break;
	    case 5:                     /* tan */
	       Z = tan (X);
	       break;
	    case 6:                     /* arcsin */
	       Z = asin (X);
	       E = errno;
	       break;
	    case 7:                     /* arccos */
	       Z = acos (X);
	       E = errno;
	       break;
	    case 8:                     /* arctan */
	       Z = atan (X);
	       E = errno;
	       break;
#endif /* OPSYS!=CTSS */
	    case 9:                     /* minus */
	       Z = -X;
	       E = 0;
	       break;
	 }
	 switch (E) {
#if OPSYS!=CTSS
	    case EDOM:
	       FunError ("domain error",InOut);
	       break;
	    case ERANGE:
	       FunError ("range error",InOut);
	       break;
#endif
	    default:
	       InOut->Tag = FLOAT;
	       InOut->Float = Z;
	       break;
	 }
      }
   }


private F_Minus (InOut)
   register ObjectPtr InOut;
   {
      if (InOut->Tag == INT && InOut->Int != FPMaxInt+1)
	 InOut->Int = - InOut->Int;
      else Monadic (InOut,9);
   }


/*
 * F_Sum
 */
private F_Sum (InOut)
   ObjectPtr InOut;
   {
      Object S;
      register ListPtr P;

      switch (InOut->Tag) {
	 default:
	    FunError (ArgNotSeq,InOut);
	    return;
	 case LIST:
	    S.Tag = INT;
	    S.Int = 0;
	    for (P=InOut->List; P!=NULL; P=P->Next) {
	       if (P->Val.Tag != INT && P->Val.Tag != FLOAT) {
		  FunError ("non-numeric sequence",InOut); 
		  return;
	       }
	       if (S.Tag == INT) {
		  if (P->Val.Tag == INT) {

		     /* Both arguments are integers. See if we can avoid    */
		     /* floating arithmetic.                                */

		     FPint Zi = S.Int + P->Val.Int;
		     if ((S.Int ^ P->Val.Int) < 0 || (S.Int^Zi)) 
			 /* arithmetic overflow occured - float result */;
		     else {
			S.Int = Zi; 
			continue;
		     }
		  }
		  S.Float = S.Int; 
		  S.Tag = FLOAT;
	       }
	       S.Float += P->Val.Tag==INT ? P->Val.Int : P->Val.Float;
	    }
	    break;
      }
      RepObject (InOut,&S);
   }
 
/*
 * Dyadic
 *
 * Evaluate a dyadic function
 *
 * Input
 *      InOut = argument to apply function
 *      Op = operation - see case statement in code for possibilities
 *
 * Output
 *      InOut = result of applying function
 *
 * The author sold his anti-GOTO morals for speed.
 */
private Dyadic (InOut,Op)
   register ObjectPtr InOut;
   register int Op;
   {
      double X,Y,Z;
      register FPint Xi,Yi,Zi;
      register ListPtr P,Q;
      static char *DivZero = "division by zero";

      if (InOut->Tag != LIST ||
	  NULL == (P=InOut->List) ||
	  NULL == (Q=P->Next) ||
	  Q->Next != NULL ||
	  NotNumPair (P->Val.Tag,Q->Val.Tag)) {

	 FunError ("not a numeric pair",InOut);
	 return;
      }

      if (IntPair (P->Val.Tag,Q->Val.Tag)) {

	 /* Both arguments are integers. See if we can avoid floating point */
	 /* arithmetic.                                                     */

	 Xi = P->Val.Int;
	 Yi = Q->Val.Int;

	 switch (Op) {

	    case 0:
	       /* assume two's complement arithmetic */
	       Zi = Xi+Yi;
	       if (((Xi ^ Yi) | ~(Xi ^ Zi)) < 0) goto RetInt;
	       break;
	       /* else arithmetic overflow occured */

	    case 1:
	       /* assume two's complement arithmetic */
	       Zi = Xi - Yi;
	       if (((Xi ^ Yi) & (Xi ^ Zi)) >= 0) goto RetInt;
	       /* else arithmetic overflow occured */
	       break;

	    case 2:
	       Zi = Xi * Yi;
	       if (Yi==0 || Zi/Yi == Xi) goto RetInt;
	       /* else arithmetic overflow occured */
	       break;

	 /* case 3: division  result always FLOAT */

#if OPSYS!=CTSS
	    case 4:                     /* mod */
	       if (Xi >= 0 && Yi > 0) {
		  Zi = Xi % Yi;
		  goto RetInt;
	       }
	       break;

	    case 5:                     /* div */
	       if (Xi >= 0 && Yi > 0) {
		  Zi = Xi / Yi;
		  goto RetInt;
	       }
	       break;
#endif /* OPSYS!=CTSS */

	    case 6:
	       Zi = Xi > Yi ? Yi : Xi;
	       goto RetInt;

	    case 7:
	       Zi = Xi < Yi ? Yi : Xi;
	       goto RetInt;

	 /* case 8: power result always FLOAT */
	 }
      }

      X = P->Val.Tag==INT ? P->Val.Int : P->Val.Float;
      Y = Q->Val.Tag==INT ? Q->Val.Int : Q->Val.Float;

      switch (Op) {
	 case 0: Z = X + Y; break;
	 case 1: Z = X - Y; break;
	 case 2: Z = X * Y; break;
	 case 3: 
	    if (Y==0.0) {
	       FunError (DivZero,InOut);
	       return;
	    }
	    Z = X / Y; 
	    break;
#if OPSYS!=CTSS
	 case 4:
	    Z = Y==0.0 ? 0.0 : X - floor (X / Y) * Y;   /* mod */
	    break;
	 case 5:
	    if (Y==0.0) {                               /* div */
	       FunError (DivZero,InOut);
	       return;
	    }
	    Z = floor (X / Y);
	    break;
#endif
	 case 6: Z = X > Y ? Y:X; break;
	 case 7: Z = X > Y ? X:Y; break;
#if OPSYS!=CTSS
	 case 8: Z = pow (X,Y);   break;
#endif
      }
      InOut->Tag = FLOAT;
      InOut->Float = Z;

   Return:
      DelLPtr (P);
      return;

   RetInt: 
      InOut->Tag = INT;
      InOut->Int = Zi;
      goto Return;
   }


/*
 * F_Add1
 */
private F_AddN (InOut,N)
   register ObjectPtr InOut;
   int N;
   {
      register FPint K;

      switch (InOut->Tag) {
	 case INT:
	    K = InOut->Int + N;
	    if (N >= 0 ? InOut->Int <= K : InOut->Int >  K) {
	       InOut->Int = K;
	       return;
	    }
	    /* else integer overflow - convert and drop down */
	    InOut->Float = ((FPfloat) InOut->Int);
	    InOut->Tag = FLOAT;
	 case FLOAT:
	    InOut->Float = InOut->Float + N;
	    break;
	 default:
	    FunError ("not a number",InOut);
	    break;
      }
   }

void D_arith ()
   {
      GroupDef (OpArith,OpCount (OpArith), ArithNode);
   }

/************************** end of F_arith.c **************************/

SHAR_EOF
if test -f 'interp/F_misc.c'
then
	echo shar: over-writing existing file "'interp/F_misc.c'"
fi
cat << \SHAR_EOF > 'interp/F_misc.c'

/****** F_misc.c ******************************************************/
/**                                                                  **/
/**                    University of Illinois                        **/
/**                                                                  **/
/**                Department of Computer Science                    **/
/**                                                                  **/
/**   Tool: IFP                         Version: 0.5                 **/
/**                                                                  **/
/**   Author:  Arch D. Robison          Date:   May 1, 1985          **/
/**                                                                  **/
/**   Revised by: Arch D. Robison       Date:  Nov 24, 1985          **/
/**                                                                  **/
/**   Principal Investigators: Prof. R. H. Campbell                  **/
/**                            Prof. W. J. Kubitz                    **/
/**                                                                  **/
/**                                                                  **/
/**------------------------------------------------------------------**/
/**   (C) Copyright 1987  University of Illinois Board of Trustees   **/
/**                       All Rights Reserved.                       **/
/**********************************************************************/

#include "struct.h"
#include <stdio.h>
#include "node.h"
#include "string.h"

/************************** miscellaneous functions *********************/

/*
 * NodeExpand
 *
 * Replace object with equivalent object not containing nodes or bottoms.
 *
 * Nodes are converted to equivalent path lists.
 * Bottoms are converted to "?".
 */
void NodeExpand (InOut)
   register ObjectPtr InOut;
   {
      register ListPtr P;
      register NodePtr N;

      switch (InOut->Tag) {

	 case LIST:
	    CopyTop (&InOut->List);
	    for (P=InOut->List; P!=NULL; P=P->Next) NodeExpand (&P->Val);
	    break;

	 case NODE:
	    N = InOut->Node;
	    RepTag (InOut,LIST);
	    InOut->List = MakePath (N);
	    break;
      }
   }

/*
 * F_Def
 *
 * Return the object representation of a function definition.
 *
 * Input
 *      *InOut = pathname list
 *
 * Output
 *      *InOut = function definition representation
 */
int F_Def (InOut)               /* imported by Compile in C_comp.c */
   register ObjectPtr InOut;
   {
      extern void ReadDef (), RepBool ();
      register DefPtr D;

      if (InOut->Tag != LIST) FunError (ArgNotSeq,InOut);
      else {
	 LinkPath (InOut,DEF);
	 if (InOut->Tag==NODE && InOut->Node->NodeType==DEF) {
	    D = &InOut->Node->NodeData.NodeDef;
	    if (D->DefCode.Tag != CODE) {
	       if (D->DefCode.Tag == BOTTOM) ReadDef ((NodePtr) NULL,InOut);
	       if (D->DefCode.Tag != BOTTOM) {
		  RepObject (InOut,&D->DefCode);
		  NodeExpand (InOut);
		  return;
	       }
	    }
	 }
      }
      RepBool (InOut,0);   /* function not defined */
   }

/*
 * F_Apply
 *
 * Apply a function to an object. 
 *
 * Input
 *     InOut = <X F> where F is a function
 *
 * Output
 *     InOut = X : F
 */
private int F_Apply (InOut)
   ObjectPtr InOut;
   {
      register ListPtr P;

      /* 
       * We don't want to use PairTest test here, since it would expand
       * the function if its a node.  This would not affect the behavior
       * at all, but would slow things down since the function must be
       * converted to its node representation anyway.
       */
      if (InOut->Tag != LIST || 2 != ListLength (InOut->List))
	 FunError ("not a pair",InOut);
      else {
	 CopyTop (&InOut->List);
	 P = InOut->List;
	 if (ApplyCheck (&P->Next->Val)) {
	    Apply (&P->Val,&P->Next->Val);
	    RepObject (InOut,&P->Val);
	 } else 
	    FunError ("invalid function",InOut);
      }
   }

void D_misc ()
   {      
      (void) PrimDef (F_Apply,"apply",SysNode);
      (void) PrimDef (F_Def,"def",SysNode);
   }

/**************************** end of F_misc ****************************/

SHAR_EOF
if test -f 'interp/F_pred.c'
then
	echo shar: over-writing existing file "'interp/F_pred.c'"
fi
cat << \SHAR_EOF > 'interp/F_pred.c'

/****** F_pred.c ******************************************************/
/**                                                                  **/
/**                    University of Illinois                        **/
/**                                                                  **/
/**                Department of Computer Science                    **/
/**                                                                  **/
/**   Tool: IFP                         Version: 0.5                 **/
/**                                                                  **/
/**   Author:  Arch D. Robison          Date:   May 1, 1985          **/
/**                                                                  **/
/**   Revised by: Arch D. Robison       Date:   Dec 1, 1985          **/
/**                                                                  **/
/**   Principal Investigators: Prof. R. H. Campbell                  **/
/**                            Prof. W. J. Kubitz                    **/
/**                                                                  **/
/**                                                                  **/
/**------------------------------------------------------------------**/
/**   (C) Copyright 1987  University of Illinois Board of Trustees   **/
/**                       All Rights Reserved.                       **/
/**********************************************************************/

#include <stdio.h>
#include <math.h>
#include "struct.h"
#include "node.h"

/************************** boolean functions **************************/

/*
 * PairTest
 *
 * Check if object is a pair of <type1,type2>
 *
 * Input
 *      X = object to test
 *      Mask1,Mask2 = masks representing type1 and type2 respectively.
 *                    E.g 1<<INT is type INT, (1<<INT)|(1<<FLOAT) is numeric.
 *
 * Output
 *      result = 1 if true, 0 if false
 */
boolean PairTest (X,Mask1,Mask2)
   ObjectPtr X;
   int Mask1,Mask2;
   {
      register ListPtr P,Q;

      if (X->Tag != LIST) 
	 if (X->Tag == NODE) NodeExpand (X);
	 else return 0;

      if ((P=X->List) == NULL || (Q=P->Next) == NULL || Q->Next!=NULL) return 0;
      if (P->Val.Tag == NODE) NodeExpand (&P->Val);
      if (Q->Val.Tag == NODE) NodeExpand (&Q->Val);
      return Mask1 >> P->Val.Tag & Mask2 >> Q->Val.Tag & 1; 
   }

/*
 * Anytime two objects are found to be equal, we can replace one with
 * the other to save memory.  Clearly the memory savings is offset by
 * a little more time, program complexity, and bringing obscure bugs
 * out of the woodwork!  Therefore the replacing action is enabled if
 * MERGE=1, disabled if MERGE=0.
 *
 * P.S. Someone should check if the merging is really worth the cost.
 */
#define MERGE 0

/*
 * BoolOp
 *
 * Boolean operation
 *
 * Input
 *      InOut = argument
 *      Op = boolean op (4-bit vector representing truth table)
 *
 * Output
 *      *A = first element of pair if result is true, undefined otherwise
 *      *B = second ...
 */
private BoolOp (InOut,Op)
   ObjectPtr InOut;
   int Op;
   {
      extern void RepBool ();
      register ListPtr P;

      if (PairTest (InOut,1<<BOOLEAN,1<<BOOLEAN)) {
	 P = InOut->List;
	 RepBool (InOut, (Op >> (P->Next->Val.Bool << 1) + P->Val.Bool) & 1);
      } else
	 FunError ("not a boolean pair",InOut);
   }


/*
 * F_Not
 *
 * Boolean negation
 */
private F_Not (InOut)
   ObjectPtr InOut;
   {
      if (InOut->Tag == BOOLEAN) InOut->Bool ^= 1;
      else FunError ("not boolean",InOut);
   }


/* 
 * F_L2
 */
private F_L2 (InOut)
   ObjectPtr InOut;
   {
      switch (InOut->Tag) {
	 case INT: RepBool (InOut,InOut->Int < 2); break;
	 case FLOAT: RepBool (InOut,InOut->Float < 2); break;
	 default: FunError ("not numeric",InOut); break;
      }
   } 

/*
 * F_False
 *
 * Check if argument is boolean false (#f).
 */
private F_False (InOut)
   ObjectPtr InOut;
   {
      if (InOut->Tag == BOTTOM)
	 FunError (ArgBottom,InOut);
      else
	 if (InOut->Tag == BOOLEAN) InOut->Bool ^= 1;
	 else RepBool (InOut,0);
   }

/*
 * F_Odd
 *
 * Check if integral argument is odd.
 */
private F_Odd (InOut)
   ObjectPtr InOut;
   {
      FPint N;

      switch (GetFPInt (InOut,&N)) {
	  case 0:
	     RepBool (InOut,(int)N & 1);
	     return;
	  case 2:
	     FunError ("not enough precision",InOut);
	     return;
	  default:
	     FunError ("not an integer",InOut);
	     return;
      }
   }

/*
 * BoolSeq
 *
 * Evaluate "any" or "all" predicate.
 *
 * Input
 *      *InOut = argument
 *      Op = identity element of operation
 *
 * Output
 *      *InOut = result
 */
private BoolSeq (InOut,Op)
   ObjectPtr InOut;
   int Op;
   {
      register boolean R;
      register ListPtr P;

      if (InOut->Tag != LIST) FunError (ArgNotSeq,InOut);
      else {
	 R = 0;
	 for (P = InOut->List; P != NULL; P=P->Next) 
	    if (P->Val.Tag == BOOLEAN) R |= P->Val.Bool ^ Op;
	    else {
	       FunError ("non-boolean element",InOut);
	       return;
	    }
	 RepBool (InOut, R ^ Op);
      }
   }


#if MERGE
/*
 * StrMerge
 *
 * Compare two strings.  Merge together if they are equal.
 *
 * Output
 *      result = 1 if equal, 0 otherwise
 */
static int StrMerge (S,T)
   register StrPtr *S,*T;
   {
      if (*S == *T) return 2;               /* strings are identical */
      else if (StrComp (*S,*T)) return 0;   /* strings are different */
      else {
	 register StrPtr *U;                /* equal and not identical */
	 if ((*S)->SRef < (*T)->SRef) 
	    U=S, S=T, T=U;
	 if ((*S)->SRef + 1) {              /* S has larger SRef */
	    DelSPtr (*T);
	    *T = *S;
	    (*S)->SRef++;
	 }
	 return 1;
      }
   }
#endif

/*
 * ObEqual
 *
 * Compare two objects.  A comparison tolerance is used for floating point
 * comparisons.
 *
 * Output
 *       result = 0 if objects are not equal
 *                1 if objects are equal within comparison tolerance
 */
boolean ObEqual (X,Y)
   ObjectPtr X,Y;
   {
      if (X->Tag != Y->Tag) {

	 switch (X->Tag) {

	    case INT:
	       return Y->Tag==FLOAT && 
		      !FloatComp ((double) X->Int,(double) Y->Float);

	    case FLOAT:
	       return Y->Tag==INT && 
		      !FloatComp ((double) X->Float,(double) Y->Int);

	    case NODE:
	       NodeExpand (X);
	       break;

	    case LIST:
	       if (Y->Tag==NODE) NodeExpand (Y); 
	       break;

	    default: return 0;
	 }
      }
      switch (X->Tag) {

	 case BOTTOM:  return 1;
	 case BOOLEAN: return X->Bool == Y->Bool;
	 case INT:     return X->Int == Y->Int;
	 case FLOAT:   return !FloatComp ((double) X->Float, (double) Y->Float);
	 case STRING:
#if MERGE
	    return StrMerge (&X->String,&Y->String);
#else
	    return !StrComp (X->String,Y->String);
#endif
	 case LIST: {
	    register ListPtr P=X->List, Q=Y->List;
	    while (1) {
	       if (P == NULL) return Q == NULL;
	       if (Q == NULL || !ObEqual (&P->Val,&Q->Val)) return 0;
	       P = P->Next; Q = Q->Next;
	    }
	 }
	 case NODE: return X->Node == Y->Node; 
	 default:   return 0; /* Tag error */
      }
   }

#define max(A,B) ((A) > (B) ? (A) : (B))

/*
 * FloatComp
 *
 * X ~= Y if abs(X-Y) / max(abs(X),abs(Y)) <= comparison tolerance.
 *
 * Output
 *      result = -1 if X < Y
 *                0 if X ~= Y
 *                1 if X > Y
 */
int FloatComp (X,Y)
   double X,Y;
   {
      double Xm,Ym,D;
      Xm = fabs (X);
      Ym = fabs (Y);
      D = X-Y;
      if (fabs (D) <= CompTol*max(Xm,Ym)) return 0;
      else return D>0 ? 1 : -1;
   }

/*
 * F_Equal
 *
 * Object comparison for equality or inequality
 */
private F_Equal (InOut,Not)
   ObjectPtr InOut;
   int Not;
   {
      if (!PairTest (InOut,~0,~0))
	 FunError ("argument not a pair",InOut);
      else 
	 RepBool (InOut, Not ^ (0 < ObEqual (&InOut->List->Val,
					     &InOut->List->Next->Val)));
   }


/*
 * F_Null
 *
 * Null sequence test
 */
private F_Null (InOut)
   ObjectPtr InOut;
   {
      switch (InOut->Tag) {
	 case LIST:
	    RepBool (InOut, InOut->List == NULL);
	    break;
	 default: 
	    FunError (ArgNotSeq,InOut);
	    break;
      }
   }


/*
 * F_Pair
 *
 * Check if argument is a pair.
 */
private F_Pair (InOut)
   ObjectPtr InOut;
   {
      RepBool (InOut, PairTest (InOut,~0,~0));
   }


/*
 * F_Tag
 *
 * Check for specified tag
 */
private F_Tag (InOut,TagSet)
   ObjectPtr InOut;
   {
      if (InOut->Tag) 
	 RepBool (InOut,TagSet >> InOut->Tag & 1);
      else 
	 FunError (ArgBottom,InOut);
   }


/*
 * CompAtom
 *
 * Compare two atoms for <,<=,=>, or >
 *
 * Strings are ordered lexigraphically.
 * Numbers are ordered in increasing value.
 *
 * Input
 *      *InOut = <X,Y>
 *      Op = comparison bit vector [>,=,<]
 *
 * Output
 *      *InOut = sign (X - Y) or BOTTOM
 */
private CompAtom (InOut,Op)
   ObjectPtr InOut;
   int Op;
   {
      register ObjectPtr X,Y;
      int D,E;
      static char *ErrMessage [3] = {
	 "not an atomic pair",
	 "booleans not comparable",
	 "strings and numbers not comparable"
      };

      E = 0;
      if (!PairTest (InOut,ATOMIC,ATOMIC)) E = 1;
      else {
	 X = &InOut->List->Val;
	 Y = &InOut->List->Next->Val;
	 if (X->Tag == BOOLEAN || Y->Tag == BOOLEAN) E = 2;
	 else if (X->Tag == STRING || Y->Tag == STRING) {
	    if (X->Tag != Y->Tag) E = 3;
	    else {
	       D = StrComp (X->String,Y->String);
	       if (D) D = (D>0) ? 1 : -1;
	    }
	 } else
	    if (X->Tag == INT)
	       if (Y->Tag == INT)
		  D = (X->Int > Y->Int) - (X->Int < Y->Int);
	       else
		  D = FloatComp ((double) X->Int,(double) Y->Float);
	    else
	       if (Y->Tag == INT)
		  D = FloatComp ((double) X->Float,(double) Y->Int);
	       else
		  D = FloatComp ((double) X->Float,(double) Y->Float);
	 }
      if (E) FunError (ErrMessage [E-1],InOut);
      else RepBool (InOut, (Op >> (D+1)) & 1);
   }


/*
 * CompLength
 *
 * Compare the length of two sequences.
 *
 * Input
 *      InOut = argument
 *      Shorter = if 0 then "longer" comparison, "shorter" otherwise.
 */
private CompLength (InOut,Shorter)
   ObjectPtr InOut;
   int Shorter;
   {
      register ListPtr P,Q;

      if (!PairTest (InOut,1<<LIST,1<<LIST))
	 FunError ("not a pair of sequences",InOut);
      else {
	 P = InOut->List;
	 Q = P->Next->Val.List;
	 P = P->Val.List;
	 while (P != NULL && Q != NULL) {
	    P = P->Next;
	    Q = Q->Next;
	 }
	 RepBool (InOut, (Shorter ? Q : P) != NULL);
      }
   }

/*
 * F_Member
 */
private F_Member (InOut)
   ObjectPtr InOut;
   {
      register ListPtr P;
      register ObjectPtr X;

      if (! PairTest (InOut,1 << LIST,~0))

	 FunError (ArgSeqOb,InOut);

      else {

	 P = InOut->List;
	 X = & P->Next->Val;
	 for (P = P->Val.List; P!=NULL; P=P->Next)
	    if (ObEqual (& P->Val,X)) break;
	 RepBool (InOut, P != NULL);
      }
   }

private OpDef LogicOps [] = {
   {"all",      1,      BoolSeq},
   {"and",      0x8,    BoolOp},
   {"any",      0,      BoolSeq},
   {"atom",     ATOMIC, F_Tag},
   {"boolean",  1<<BOOLEAN,     F_Tag},
   {"false",    -1,     F_False},
   {"imply",    0xD,    BoolOp},
   {"longer",   0,      CompLength},
   {"member",   -1,     F_Member},
   {"null",     -1,     F_Null},
   {"numeric",  NUMERIC,F_Tag},
   {"odd",      -1,     F_Odd},
   {"or",       0xE,    BoolOp},
   {"pair",     -1,     F_Pair},
   {"shorter",  1,      CompLength},
   {"xor",      0x6,    BoolOp},
   {"=",        0,      F_Equal},
   {"~=",       1,      F_Equal},
   {"~",        -1,     F_Not},
   {">",        0x4,    CompAtom},
   {"<",        0x1,    CompAtom},
   {">=",       0x6,    CompAtom},
   {"<=",       0x3,    CompAtom},
   {"l2",	0,	F_L2}
};

void D_pred ()
   {
      GroupDef (LogicOps, OpCount (LogicOps), LogicNode);
   }

/******************************* end of F_pred *******************************/

SHAR_EOF
if test -f 'interp/F_seq.c'
then
	echo shar: over-writing existing file "'interp/F_seq.c'"
fi
cat << \SHAR_EOF > 'interp/F_seq.c'

/****** F_seq.c *******************************************************/
/**                                                                  **/
/**                    University of Illinois                        **/
/**                                                                  **/
/**                Department of Computer Science                    **/
/**                                                                  **/
/**   Tool: IFP                         Version: 0.5                 **/
/**                                                                  **/
/**   Author:  Arch D. Robison          Date:   May 1, 1985          **/
/**                                                                  **/
/**   Revised by: Arch D. Robison       Date:   Aug 5, 1986          **/
/**                                                                  **/
/**   Principal Investigators: Prof. R. H. Campbell                  **/
/**                            Prof. W. J. Kubitz                    **/
/**                                                                  **/
/**                                                                  **/
/**------------------------------------------------------------------**/
/**   (C) Copyright 1987  University of Illinois Board of Trustees   **/
/**                       All Rights Reserved.                       **/
/**********************************************************************/

/******************* sequence (structural) functions ******************/

#include <stdio.h>
#include "struct.h"
#include "node.h"

/*
 * F_Cat
 *
 * Sequence catenation
 */
private F_Cat (InOut)
   register ObjectPtr InOut;
   {
      register MetaPtr E;
      register ListPtr P;

      if (InOut->Tag != LIST) {
	 FunError (ArgNotSeq,InOut);
	 return;
      }
      P = InOut->List;
      if (P == NULL) return;

      do
	 if (P->Val.Tag != LIST) {
	    FunError ("elements not sequences",InOut);
	    return;
	 }
      while ((P=P->Next)!=NULL);

      Copy2Top (& InOut->List);
      if (SysError) return;

      P = InOut->List;
      E = &P->Val.List;
      for (P=P->Next; P!=NULL; P=P->Next) {
	 while (*E!=NULL) E = &(*E)->Next;
	 *E = P->Val.List;
	 P->Val.Tag = BOTTOM;
      }
      E = &InOut->List;
      RepLPtr (E,(*E)->Val.List);
   }


/*
 * F_Iota
 *
 * Generate <1...id>
 */
private F_Iota (InOut)
   register ObjectPtr InOut;
   {
      FPint N;
      register FPint K;
      register ListPtr Pr;

      switch (GetFPInt (InOut,&N)) {
	 case 1: FunError ("not an integer",InOut); return;
	 case 2: FunError ("too big"       ,InOut); return;
	 case 0:
	    if (N < 0) FunError ("negative",InOut);
	    else {
	       InOut->Tag = LIST;
	       InOut->List = NULL;  
	       NewList (&InOut->List,N);
	       if (SysError) return;
	       for (Pr=InOut->List,K=0; Pr!=NULL; Pr=Pr->Next) 
		  Pr->Val.Tag = INT,
		  Pr->Val.Int = ++K;
	    }
	    return;
      }
   }


/*
 * F_Id
 */
private F_Id ()
   {
      return; /* do nothing */;
   }


/*
 * F_Length
 *
 * Find sequence length
 */
private F_Length (InOut)
   ObjectPtr InOut;
   {
      register FPint N;

      switch (InOut->Tag) {
	 default:
	    FunError (ArgNotSeq,InOut);
	    return;
	 case LIST:
	    N = ListLength (InOut->List);
	    break;
      }
      RepTag (InOut,INT);
      InOut->Int = N;
   }

/*
 * F_LApnd
 *
 *           +--------+
 * InOut --->|  list  |
 *           +----+---+ A
 *                |     |
 *                V     V
 *           +------------+      +------------+
 *           | object | o-+----->|  list  |///|
 *           +------------+      +---+--------+
 *                                   |
 *                                   V
 *                                  ...
 */
private F_LApnd (InOut)
   ObjectPtr InOut;
   {
      MetaPtr A;
      if (! PairTest (InOut, ~0, SEQUENCE))
	 FunError (ArgObSeq,InOut);
      else {
	 CopyTop (&InOut->List);
	 A = & InOut->List->Next;
	 RepLPtr (A,(*A)->Val.List);
      }
   }


/*
 * F_RApnd
 *
 *           +--------+
 * InOut --->|  list  |
 *           +----+---+
 *                |
 *                V
 *           +------------+      +------------+
 *           |  list  | o-+----->| object |///|
 *           +------------+      +------------+
 *                |
 *                V
 *               ...
 *
 */
private F_RApnd (InOut)
   ObjectPtr InOut;
   {
      register MetaPtr E;
      ListPtr P;

      if (! PairTest (InOut,1 << LIST,~0))
	 FunError (ArgSeqOb,InOut);

      else {
	 Copy2Top (& InOut->List);
	 if (SysError) return;
	 P = InOut->List;
	 for (E = &P->Val.List; (*E)!=NULL; E = &(*E)->Next) continue;
	 *E = P->Next;
	 P->Next=NULL;
	 RepLPtr (&InOut->List,P->Val.List);
	 /* No system error possible since source is fresh list */
      }
   }

/*
 * F_LDist
 *
 * Distribute from left
 */
private F_LDist (InOut)
   ObjectPtr InOut;
   {
      ListPtr R=NULL;
      register ListPtr P1,P2,P3,PT;
      long N;

      if (!PairTest (InOut, ~0, SEQUENCE))

	 FunError (ArgObSeq,InOut);

      else {

	 Copy2Top (&InOut->List);
	 if (SysError) return;
	 P1 = InOut->List;         	/* P1 = pointer to arg list     */
	 P2 = P1->Next;
	 P3 = P2->Val.List; 		/* P3 = pointer to 2nd arg list */
	 P2->Val.List = NULL;
	 N = ListLength (P3); 
	 NewList (&R,N);		/* R = pointer to result list   */
	 if (SysError) return;
	 P2 = Repeat (&P1->Val,N);	/* P2 = pointer to 1st arg list */
	 if (SysError) {DelLPtr (R); return;}

	 for (P1=R; P1!=NULL; P1=P1->Next) {
	    P1->Val.Tag = LIST;
	    P1->Val.List = P2;
	    PT = P2;
	    P2 = P2->Next;
	    PT->Next = P3;
	    PT = P3;
	    P3 = P3->Next;
	    PT->Next = NULL;
	 }

	 DelLPtr (InOut->List);
	 InOut->List = R;
      }
   }


/*
 * F_RDist
 *
 * Distribute from right
 */
private F_RDist (InOut)
   ObjectPtr InOut;
   {
      ListPtr R,P,P1,P2;
      long N;

      if (! PairTest (InOut, SEQUENCE, ~0))

	 FunError (ArgSeqOb,InOut);

      else {

	 Copy2Top (&InOut->List);
	 if (SysError) return;
	 P = InOut->List;            /* P = pointer to arg list */
	 P2 = P->Val.List;        /* P2 = pointer to first arg list */
	 P->Val.Tag = BOTTOM;
	 P = P->Next;                     /* P = pointer to 2nd arg */
	 N = ListLength (P2);
	 R = NULL; NewList (&R,N);        /* R = pointer to result list */
	 if (SysError) return;

	 for (P1=R; P1!=NULL; P1=P1->Next) {
	    P1->Val.Tag = LIST;
	    P1->Val.List = CopyLPtr (P);
	    if (SysError) {DelLPtr (R); return;}
	    Rot3 (&P1->Val.List,&P2,&P2->Next);
	 }
	 RepLPtr (&InOut->List,R);
	 DelLPtr (R);
      }
   }


/*
 * F_Reverse
 *
 * Reverse a list
 */
F_Reverse (InOut)     /* Imported by F_RInsert in forms.c */
   ObjectPtr InOut;
   {
      ListPtr P,Q;

      switch (InOut->Tag) {
	 default:
	    FunError (ArgNotSeq,InOut);
	    break;
	 case LIST:
	    P = InOut->List;
	    CopyTop (&P);
	    if (SysError) return;
	    for (Q=NULL; P!=NULL; Rot3 (&P,&P->Next,&Q)) continue; 
	    InOut->List = Q;
	    break;
      }
   }


/*
 * TransCheck
 *
 * Check that InOut is matrix
 *
 * Input
 *     InOut = pointer to object
 *
 * Output
 *     result = NULL iff a matrix, error code otherwise.
 *     *Cols = number of columns
 */
private char *TransCheck (InOut,Cols)
   ObjectPtr InOut;
   long *Cols;
   {
      register ListPtr V,VR;

      if (InOut->Tag != LIST)
	 return "argument not a sequence.";
      else if (NULL == (VR = InOut->List))
	 return "argument is empty sequence.";
      else
	 for (V = VR; V !=NULL; V = V->Next)
	    if (V->Val.Tag != LIST)
	       return "argument subelements must be sequences.";
	    else if (V==VR) *Cols = ListLength (V->Val.List);
	    else if (*Cols != ListLength (V->Val.List))
	       return "argument not rectangular.";
	    else continue;
      return NULL;
   }


/*
 * F_Trans
 *
 * Transpose a matrix (sequence of sequences)
 */
private F_Trans (InOut)
   ObjectPtr InOut;
   {
      char *E; long Cols;
      ListPtr VR,HR,H;
      register ListPtr U,V;
      register MetaPtr A;

      /* Check for rectangularness */
      if (NULL != (E = TransCheck (InOut,&Cols))) {
	 FunError (E,InOut);
	 return;
      }

      /* Make fresh copy of vertical top level  and rows */
      Copy2Top (&InOut->List);
      if (SysError) return;
      else VR = InOut->List;
	
      /* Make horizontal top level */
      HR = NULL;
      NewList (&HR,Cols);

      /* Transpose matrix column by column */
      for (H=HR; H!=NULL; H=H->Next) {
	 H->Val.Tag = LIST;
	 H->Val.List = VR->Val.List;

	 /* Relink the column and advance the VR list to the next column */
	 for (V=VR; V!=NULL; V=U) {
	    U = V->Next;
	    A = &V->Val.List->Next;
	    V->Val.List = *A;
	    *A = U==NULL ? NULL : U->Val.List;
	 }
      }
      /* Delete the old vertical top level and return new matrix */
      DelLPtr (VR); InOut->List = HR;
   }


/*
 * F_Tail
 */
private F_Tail (InOut)
   ObjectPtr InOut;
   {
      register ListPtr P;
      switch (InOut->Tag) {
	 default:
	    FunError (ArgNotSeq,InOut);
	    break;
	 case LIST:
	    if (NULL == (P = InOut->List)) FunError (ArgNull,InOut);
	    else RepLPtr (&InOut->List,P->Next);
	    break;
      }
   }


/*
 * F_RTail
 *
 * Drop last element
 */
private F_RTail (InOut)
   ObjectPtr InOut;
   {
      register MetaPtr A;
      if (InOut->Tag != LIST)
	 FunError (ArgNotSeq,InOut);
      else if (NULL == InOut->List)
	 FunError (ArgNull,InOut);
      else {
	 CopyTop (A = &InOut->List);
	 if (SysError) return;
	 while ((*A)->Next != NULL) A = &(*A)->Next;
	 RepLPtr (A,(ListPtr) NULL);
      }
   }


OpDef SeqOps [] = {
   {"apndl",    -1,     F_LApnd},
   {"apndr",    -1,     F_RApnd},
   {"cat",      -1,     F_Cat},
   {"distl",    -1,     F_LDist},
   {"distr",    -1,     F_RDist},
   {"id",       -1,     F_Id},
   {"iota",     -1,     F_Iota},
   {"length",   -1,     F_Length},
   {"reverse",  -1,     F_Reverse},
   {"tl",       -1,     F_Tail},
   {"tlr",      -1,     F_RTail},
   {"trans",    -1,     F_Trans}
};

void D_seq ()
   {
      GroupDef (SeqOps, OpCount (SeqOps), SysNode);
   }  

/************************** end of F_seq **************************/

SHAR_EOF
if test -f 'interp/F_ss.c'
then
	echo shar: over-writing existing file "'interp/F_ss.c'"
fi
cat << \SHAR_EOF > 'interp/F_ss.c'

/****** F_ss.c ********************************************************/
/**                                                                  **/
/**                    University of Illinois                        **/
/**                                                                  **/
/**                Department of Computer Science                    **/
/**                                                                  **/
/**   Tool: IFP                         Version: 0.5                 **/
/**                                                                  **/
/**   Author:  Arch D. Robison          Date:   May 1, 1985          **/
/**                                                                  **/
/**   Revised by: Arch D. Robison       Date:  July 4, 1985          **/
/**                                                                  **/
/**   Principal Investigators: Prof. R. H. Campbell                  **/
/**                            Prof. W. J. Kubitz                    **/
/**                                                                  **/
/**                                                                  **/
/**------------------------------------------------------------------**/
/**   (C) Copyright 1987  University of Illinois Board of Trustees   **/
/**                       All Rights Reserved.                       **/
/**********************************************************************/

#include <stdio.h>
#include "struct.h"
#include "node.h"

/*************************** Searching and Sorting ***************************/

/*
 * F_Assoc
 *
 * Just like LISP assoc, except that #f is returned if the key is not found.
 *
 * [association-list,key] | assoc == element of association list or #f
 */
private F_Assoc (InOut)
   ObjectPtr InOut;
   {
      register ListPtr P;
      register ObjectPtr Key;

      if (!PairTest (InOut,1<<LIST,~0))
	  FunError (ArgSeqOb,InOut);

      else {

	 P = InOut->List;
	 Key = &P->Next->Val;

	 for (P = P->Val.List; P != NULL; P=P->Next)
	    if (P->Val.Tag != LIST) {
	       FunError ("element not sequence",InOut);
	       return;
	    } else
	       if (ObEqual (&P->Val.List->Val,Key)) {
		  RepObject (InOut,&P->Val);
		  return;
	       }

	 RepBool (InOut,0);     /* key not found, return #f */
      }
   }


void D_ss ()
   {
      (void) PrimDef (F_Assoc,"assoc",SysNode);
   }

/******************************* end of F_ss.c *******************************/

SHAR_EOF
if test -f 'interp/F_string.c'
then
	echo shar: over-writing existing file "'interp/F_string.c'"
fi
cat << \SHAR_EOF > 'interp/F_string.c'

/****** F_string.c ****************************************************/
/**                                                                  **/
/**                    University of Illinois                        **/
/**                                                                  **/
/**                Department of Computer Science                    **/
/**                                                                  **/
/**   Tool: IFP                         Version: 0.5                 **/
/**                                                                  **/
/**   Author:  Arch D. Robison          Date:   May 1, 1985          **/
/**                                                                  **/
/**   Revised by: Arch D. Robison       Date:  July 5, 1985          **/
/**                                                                  **/
/**   Principal Investigators: Prof. R. H. Campbell                  **/
/**                            Prof. W. J. Kubitz                    **/
/**                                                                  **/
/**                                                                  **/
/**------------------------------------------------------------------**/
/**   (C) Copyright 1987  University of Illinois Board of Trustees   **/
/**                       All Rights Reserved.                       **/
/**********************************************************************/

#include <stdio.h>
#include "struct.h"
#include "string.h"
#include "node.h"

/*
 * F_Patom
 *
 * Convert an atom to it's string representation.
 */
private F_Patom (InOut)
   register ObjectPtr InOut;
   {
      CharPtr U;
      char Buf[255];
      StrPtr S;
      register char *T;
      extern char *sprintf();

      T = Buf;
      switch (InOut->Tag) {
	 case INT:
	    (void) sprintf (T,"%d",InOut->Int);
	    break;
	 case FLOAT:
	    (void) sprintf (T,"%g",InOut->Float);
	    break;
	 case BOOLEAN:
	    (void) sprintf (T,InOut->Bool ? "t":"f");
	    break;
	 case STRING:
	    return;
	 default:
	    FunError ("not atomic",InOut);
	    return;
      }
      S = NULL;
      CPInit (&U,&S);
      do CPAppend (&U,*T); while (*T++);
      RepTag (InOut,STRING);
      InOut->String = S;
   }


/*
 * F_Explode
 *
 * Convert a string to a list of characters
 */
private F_Explode (InOut)
   ObjectPtr InOut;
   {
      ListPtr Result = NULL;
      MetaPtr A = &Result;
      CharPtr U;
      char C[2];

      if (InOut->Tag != STRING)
	 FunError ("not a string",InOut);
      else {
	 CPInit (&U,&InOut->String);
	 while (CPRead (&U,C,2)) {
	    NewList (A,1L);
	    if (SysError) {DelLPtr (Result); return;}
	    (*A)->Val.Tag = STRING;
	    (*A)->Val.String = CopySPtr (CharString [C[0] & 0x7F]);
	    A = &(*A)->Next;
	 }
	 RepTag (InOut,LIST);
	 InOut->List = Result;
      }
   }


/*
 * F_Implode
 *
 * Catenate a list of strings into a single string.
 */
private F_Implode (InOut)
   ObjectPtr InOut;
   {
      CharPtr U,V;
      char C[2];
      ListPtr P;
      StrPtr S;

      if (InOut->Tag != LIST)
	 FunError ("not a sequence",InOut);
      else {
	 S = NULL;
	 CPInit (&U,&S);
	 for (P = InOut->List; P != NULL; P=P->Next) {
	    if (P->Val.Tag != STRING) {
	       FunError ("non-string in sequence",InOut);
	       CPAppend (&U,'\0');
	       DelSPtr (S);
	       return;
	    } else {
	       CPInit (&V,&P->Val.String);
	       while (CPRead (&V,C,2)) CPAppend (&U,C[0]);
	    }
	 }
	 CPAppend (&U,'\0');
	 RepTag (InOut,STRING);
	 InOut->String = S;
      }
   }


void D_string ()
   {                             
      (void) PrimDef (F_Explode,"explode",SysNode);
      (void) PrimDef (F_Implode,"implode",SysNode);
      (void) PrimDef (F_Patom,"patom",SysNode);
   }

/************************** end of F_string **************************/

SHAR_EOF
if test -f 'interp/F_subseq.c'
then
	echo shar: over-writing existing file "'interp/F_subseq.c'"
fi
cat << \SHAR_EOF > 'interp/F_subseq.c'

/****** F_subseq.c ****************************************************/
/**                                                                  **/
/**                    University of Illinois                        **/
/**                                                                  **/
/**                Department of Computer Science                    **/
/**                                                                  **/
/**   Tool: IFP                         Version: 0.5                 **/
/**                                                                  **/
/**   Author:  Arch D. Robison          Date:   May 1, 1985          **/
/**                                                                  **/
/**   Revised by: Arch D. Robison       Date:  Apr 28, 1986          **/
/**                                                                  **/
/**   Principal Investigators: Prof. R. H. Campbell                  **/
/**                            Prof. W. J. Kubitz                    **/
/**                                                                  **/
/**                                                                  **/
/**------------------------------------------------------------------**/
/**   (C) Copyright 1987  University of Illinois Board of Trustees   **/
/**                       All Rights Reserved.                       **/
/**********************************************************************/

#include <stdio.h>    /* defines NULL */
#include "struct.h"
#include "node.h"

/*
 * ListIndex
 *
 * Check an argument to make sure it is of the form <sequence integer>
 *
 * Input
 *      InOut = argument
 *
 * Output
 *      *L = sequence or array if no error
 *      result = -1 if error occurred, index otherwise
 */
private long ListIndex (InOut,L)
   ObjectPtr InOut;
   ListPtr *L;
   {
      register ListPtr P;
      FPint N;
 
      if (!PairTest (InOut, SEQUENCE, NUMERIC)) {
	 FunError ("not <sequence number>",InOut);
	 return -1;
      } else {
	 P = InOut->List;
	 *L = P->Val.List;
	 P = P->Next;
	 switch (GetFPInt (&P->Val,&N)) {
	    default: /* actually case 0, but we need to keep lint happy */
	       if (N >= 0) return N;
	       else {
		  FunError ("negative index",InOut);
		  return -1;
	       }
	    case 1: 
	       FunError ("index not integral",InOut); 
	       return -1;
	    case 2:
	       FunError ("index too big",InOut);
	       return -1;
	 }
      }
   }

#define SCATTER_STORE 0

#if SCATTER_STORE
/*
 * F_Scatter
 *
 * Scatter store function
 *
 * Input
 *      <<D1 D2 ... Dn> <<V1 I1> <V2 I2> ... <Vm Im>>>
 *
 * Output
 *      <E1 E2 ... En>
 *
 * Ek = Dk if there is no Ij == k
 *      Vj if Ij == k
 *
 * Result is BOTTOM if Ij==Ik for j!=k or Ij < 1 or Ij > n
 *
 * Perversions: uses LRef field for markers
 */
private F_Scatter (InOut)
   ObjectPtr InOut;
   {
      register ListPtr P1,P2,Q,R;
      register long N;
      FPint M;

      if (!PairTest (InOut,1<<LIST,1<<LIST))
	 FunError ("not <sequence sequence>",InOut);

      else {

	 Copy2Top (&InOut->List); /* only need fresh first element */
	 P1 = InOut->List;
	 R = P1->Val.List;
	 N = ListLength (R);

	 for (P1 = P1->Next->Val.List; P1!=NULL; P1=P1->Next) {
	    if (!PairTest (&P1->Val,~0,NUMERIC)) {
	       FunError ("invalid store pair",InOut);
	       return;
	    }
	    P2 = P1->Val.List;
	    if (GetFPInt (&P2->Next->Val,&M) || M < 1 || M > N) {
	       FunError ("invalid index",InOut);
	       return;
	    }
	    for (Q=R; --M; Q=Q->Next) continue;
	    if (++Q->LRef > 2) {
	       for (Q=R; Q!=NULL; Q=Q->Next) Q->LRef = 1;
	       FunError ("duplicate index",InOut);
	       return;
	    }
	    RepObject (&Q->Val,&P2->Val);
	 }
	 for (Q=R; Q!=NULL; Q=Q->Next) Q->LRef = 1;
	 RepObject (InOut,&InOut->List->Val);
      }
   }
#endif

/*
 * F_Pick
 * 
 * Pick the nth element of a sequence
 *
 * Input
 *      InOut = pointer to <sequence number>
 */
private F_Pick (InOut)
   ObjectPtr InOut;
   {
      register FPint N;
      ListPtr P; 

      if ((N = ListIndex (InOut,&P)) >= 0) {
	 if (N <= 0) {
	    FunError ("non-positive index",InOut);
	 } else if (P == NULL) FunError ("empty sequence",InOut);
	 else {
	    while (--N > 0)
	       if ((P = P->Next) == NULL) {
		  FunError ("index out of bounds",InOut);
		  return;
	       }
	    RepObject (InOut,&P->Val);
	 }
      }
   }


/*
 * F_Repeat
 *
 * Create a repetition of an item.
 *
 * E.g. <x 8> == <x x x x x x x x>
 */
private F_Repeat (InOut)
   register ObjectPtr InOut;
   {
      FPint N;
      register ListPtr P;

      if (!PairTest (InOut,~0,NUMERIC))
	 FunError ("not <object number>",InOut);

      else {
	 P = InOut->List;
	 switch (GetFPInt (&P->Next->Val,&N)) {
	    case 1:
	       FunError ("repetition value not integer",InOut);
	       break;
	    case 2:
	       FunError ("repetition value too big",InOut);
	       break;
	    case 0:
	       if (N < 0) FunError ("negative repetition",InOut);
	       else {
		  P = Repeat (&P->Val,(long) N);
		  DelLPtr (InOut->List);
		  InOut->List = P;
	       }
	       break;
	    }
      }
   }


/*
 * F_RDrop
 *
 * Drop the last n elements from a sequence
 *
 * Input
 *      InOut = pointer to <sequence number>
 */
private F_RDrop (InOut)
   ObjectPtr InOut;
   {
      register FPint N;
      ListPtr P,Result;
      register ListPtr R;

      if ((N = ListIndex (InOut,&P)) >= 0) 
	 if ((N = ListLength (P) - N) < 0) 
	    FunError ("sequence too short",InOut);
	 else {
	    Result = NULL;
	    NewList (&Result,N);
	    for (R = Result; R!=NULL; P=P->Next,R=R->Next) 
	       CopyObject (&R->Val,&P->Val);
	    DelLPtr (InOut->List);
	    InOut->List = Result;
	 }
   }


/*
 * F_LDrop
 *
 * Drop the first n elements from a sequence
 *
 * Input
 *      InOut = pointer to <sequence number>
 */
private F_LDrop (InOut)
   ObjectPtr InOut;
   {
      register FPint N;   
      ListPtr P; 

      if ((N = ListIndex (InOut,&P)) >= 0) {
	 for (; --N >= 0; P = P->Next)
	    if (P == NULL) {
	       FunError ("sequence too short",InOut);
	       return;
	    }
	 RepLPtr (&InOut->List,P);
      }
   }


/*
 * F_LTake
 *
 * Take the first n elements from a sequence
 *
 * Input
 *      InOut = pointer to <sequence number>
 */
private F_LTake (InOut)
   ObjectPtr InOut;
   {
      register long N;
      ListPtr P,Result;
      register ListPtr R;

      if ((N = ListIndex (InOut,&P)) >= 0) {
	 Result = NULL;
	 NewList (&Result,N);
	 for (R = Result; R!=NULL; P=P->Next, R=R->Next)
	    if (P != NULL)
	       CopyObject (&R->Val,&P->Val);
	    else {
	       FunError ("sequence too short",InOut);
	       DelLPtr (Result);
	       return;
	    } 
	 DelLPtr (InOut->List);
	 InOut->List = Result;
      }
   }


/*
 * F_RTake
 *
 * Take the last n elements from a sequence
 *
 * Input
 *      InOut = pointer to <sequence number>
 */
private F_RTake (InOut)
   ObjectPtr InOut;
   {
      register FPint N;
      ListPtr P;

      if ((N = ListIndex (InOut,&P)) >= 0) 
	 if ((N = ListLength (P) - N) < 0)
	    FunError ("sequence too short",InOut);
	 else {
	    while (--N >=0) P = P->Next;
	    RepLPtr (&InOut->List,P);
	 }
   }

private OpDef SubSeqOps [] = {
   {"dropl",    -1,     F_LDrop},
   {"dropr",    -1,     F_RDrop},
   {"pick",     -1,     F_Pick},
   {"repeat",   -1,     F_Repeat},
   {"takel",    -1,     F_LTake},
   {"taker",    -1,     F_RTake}
#if SCATTER_STORE
   {"scatter",  -1,     F_Scatter},
#endif
};

void D_subseq ()
   {
      GroupDef (SubSeqOps, OpCount (SubSeqOps), SysNode);
   }

/************************** end of F_subseq **************************/

SHAR_EOF
#	End of shell archive
exit 0

-- 

Rich $alz			"Anger is an energy"
Cronus Project, BBN Labs	rsalz@pineapple.bbn.com
Moderator, comp.sources.unix	sources@uunet.unctunc