/******************************************************************************

			UNSW Prolog (version 4)

			Written by Claude Sammut
		     Department of Computer Science
		     University of New South Wales
		   (and St. Joseph's U., Philadelphia)

		   Copyright (c)  1983 - Claude Sammut

******************************************************************************/





		/*	The basic Prolog predicates	*/


#include "pred.h"

extern integer *stack_int;

short arith_failure = FALSE;


	/* Issue warning when a predefined predicate fails */

warning(s)
char *s;
{
	fprintf(stderr, "\nWARNING - %s\n", s);
	arith_failure = TRUE;
}


		/*	ARITH evaluates arithmetic expressions	*/


int arith(term, frame)
pval term;
binding *frame;
{
	int i, x, arg[4];
	extern pval termb;
	extern binding *frameb;

	arith_failure = FALSE;
	switch (TYPE(term))
	{
	   case INT:	if (term == (pval) stack_int) return((int) frame);
			else return(((integer *) term) -> int_val);
	   case VAR:	if (isbound(term, frame))
				return(arith(termb, frameb));
			else warning("Unbound variable in arithmetic expression");
			break;
	   case FN:	if (TYPE(term -> c.term[0]) != PREDEF)
			{
				warning("Unrecognized arithmetic operator");
				break;
			}
			if (SIZE(term) > 4)
				warning("ARITH - TOO MANY ARGUMENTS");
			for (i = 0; i < SIZE(term); i++)
				arg[i] = arith(term -> c.term[i + 1], frame);
			switch (VAL(term -> c.term[0]))
			{
			   case PLUS:	return(arg[0] + arg[1]);
			   case MINUS:	return(arg[0] - arg[1]);
			   case TIMES:	return(arg[0] * arg[1]);
			   case DIV:	return(arg[0] / arg[1]);
			   case MOD:	return(arg[0] % arg[1]);
			   case POW:	if (arg[1] < 0)
					{
						warning("Negative powers not permitted");
						break;
					}
					x = 1;
					for (i = arg[1]; i != 0; i--)
						x *= arg[0];
					return(x);
			   case UPLUS:	return(arg[0]);
			   case UMINUS:	return(- arg[0]);
			   default:	warning("Unknown arithmetic operation");
					break;
			}
	    default:	warning("Incorrect arithmetic expression");
			break;
	}
}


static
is PREDICATE
{
	int i;

	i = arith(arg[1], frame[1]);
	if (arith_failure) return(FALSE);
	return(unify(arg[0], frame[0], stack_int, i));
}


static
int compare(arg, frame)
pval arg[];
binding **frame;
{
	int x, y;

	if (isatom(arg[0]) && isatom(arg[1]))
		return(strcmp(NAME(arg[0]), NAME(arg[1])));
	x = arith(arg[0], frame[0]);
	if (arith_failure) return(FALSE);
	y = arith(arg[1], frame[1]);
	if (arith_failure) return(FALSE);
	return(x - y);
}


static
lt PREDICATE
{
	return((! arith_failure) && (compare(arg, frame) < 0));
}


static
le PREDICATE
{
	return((! arith_failure) && (compare(arg, frame) <= 0));
}

static
gt PREDICATE
{
	return((! arith_failure) && (compare(arg, frame) > 0));
}

static
ge PREDICATE
{
	return((! arith_failure) && (compare(arg, frame) >= 0));
}

static
eq PREDICATE
{
	return((! arith_failure) && (compare(arg, frame) == 0));
}


static
neq PREDICATE
{
	return((! arith_failure) && (compare(arg, frame) != 0));
}

static
is_int PREDICATE
{
	return(isinteger(arg[0]));
}

static
is_atom PREDICATE
{
	return(isatom(arg[0]));
}


static
quoted PREDICATE
{
	return(TYPE(arg[0]) == STRING);
}


static
atomic PREDICATE
{
	switch (TYPE(arg[0]))
	{
	   case ATOM:
	   case STRING:
	   case PREDEF:
	   case INT:	return(TRUE);
	   default: return(FALSE);
	}
}


static
is_var PREDICATE
{
	return(isvariable(arg[0]));
}


static
nonvar PREDICATE
{
	return(! isvariable(arg[0]));
}


static
length PREDICATE
{
	extern atom *nil;
	extern pval termb;	/* 9/9/86 - WEN - needed for VAX C */
	extern binding *frameb;	/* 9/9/86 - WEN - needed for VAX C */
	register i;

	if (arg[0] == (pval) nil) i = 0;
	else if (TYPE(arg[0]) == LIST)
	{
		i = 0;
		termb = arg[0];
		frameb = frame[0];
		repeat
		{
			if (TYPE(termb) == VAR || TYPE(termb) != LIST)
				break;
			else i++;
			unbind(termb -> c.term[1], frameb);
		}
	}
	else {
		warning("Length - first argument must be a list");
		return(FALSE);
	}
	if (isinteger(arg[1])) return(i == INT_VAL(1));
	if (isvariable(arg[1]))
	{
		bind_num(1, i);
		return(TRUE);
	}
	else return(FALSE);
}


atom_table p_basic =
{
	SET_PRED(XFX, 700, 2, ">", gt),
	SET_PRED(XFX, 700, 2, "<", lt),
	SET_PRED(XFX, 700, 2, "<=", le),
	SET_PRED(XFX, 700, 2, ">=", ge),
	SET_PRED(XFX, 700, 2, "==", eq),
	SET_PRED(XFX, 700, 2, "<>", neq),
	SET_PRED(XFX, 700, 2, "<>", neq),
	SET_PRED(XFX, 700, 2, "is", is),
	SET_PRED(NONOP, 0, 1, "integer", is_int),
	SET_PRED(NONOP, 0, 1, "atom", is_atom),
	SET_PRED(NONOP, 0, 1, "quoted", quoted),
	SET_PRED(NONOP, 0, 1, "atomic", atomic),
	SET_PRED(NONOP, 0, 1, "nonvar", nonvar),
	SET_PRED(NONOP, 0, 2, "length", length),
	SET_PRED(NONOP, 0, 1, "var", is_var),
	END_MARK
};
