/*  $Id: pl-op.c,v 1.16 1997/08/07 07:58:18 jan Exp $

    Copyright (c) 1990 Jan Wielemaker. All rights reserved.
    See ../LICENCE to find out about your rights.
    jan@swi.psy.uva.nl

    Purpose: operator functions and declarations
*/

#include "pl-incl.h"

forwards int	atomToOperatorType(atom_t);
forwards atom_t	operatorTypeToAtom(int);

#define operatorTable (GD->op.table)

/*  Find an operator in the table. Type is one of OP_PREFIX, OP_INFIX or
    op_POSTFIX.

 ** Wed Apr 20 10:34:55 1988  jan@swivax.UUCP (Jan Wielemaker)  */

Operator
isCurrentOperator(atom_t name, int type)
{ register int v = pointerHashValue(name, OPERATORHASHSIZE);
  register Operator op;

  for(op=operatorTable[v]; op && !isTableRef(op); op=op->next)
  { if (op->name != name)
      continue;
    if (op->priority <= 0 )
      continue;
    switch(op->type)
    { case OP_FX:
      case OP_FY:	if (type == OP_PREFIX)
			  return op;
			continue;
      case OP_XF:
      case OP_YF:	if (type == OP_POSTFIX)
			  return op;
			continue;
      case OP_XFX:
      case OP_XFY:
      case OP_YFX:
      case OP_YFY:	if (type == OP_INFIX)
			  return op;
			continue;
    }
  }

  return (Operator) NULL;
}


static int
atomToOperatorType(atom_t atom)
{ if (atom == ATOM_fx)			return OP_FX;
  else if (atom == ATOM_fy)		return OP_FY;
  else if (atom == ATOM_xfx)		return OP_XFX;
  else if (atom == ATOM_xfy)		return OP_XFY;
  else if (atom == ATOM_yfx)		return OP_YFX;
  else if (atom == ATOM_yfy)		return OP_YFY;
  else if (atom == ATOM_yf)		return OP_YF;
  else if (atom == ATOM_xf)		return OP_XF;

  return -1;
}

static atom_t
operatorTypeToAtom(int type)
{ switch(type)
  { case OP_FX:				return ATOM_fx;
    case OP_FY:				return ATOM_fy;
    case OP_XFX:			return ATOM_xfx;
    case OP_XFY:			return ATOM_xfy;
    case OP_YFX:			return ATOM_yfx;
    case OP_YFY:			return ATOM_yfy;
    case OP_YF:				return ATOM_yf;
    case OP_XF:				return ATOM_xf;
  }
  return NULL_ATOM;
}

word
pl_current_op(term_t prec, term_t type, term_t name, word h)
{ int Prec = 0;					/* not specified */
  int Type = -1;				/* not specified */
  atom_t Name = NULL_ATOM;			/* not specified */
  Operator op;
  atom_t a;

  switch( ForeignControl(h) )
  { case FRG_FIRST_CALL:
      op = operatorTable[0];
      break;
    case FRG_REDO:
      op = ForeignContextPtr(h);
      break;
    case FRG_CUTTED:
    default:
      succeed;
  }

  if ( !PL_get_integer(prec, &Prec) &&
       !PL_is_variable(prec) )
    fail;

  if ( PL_get_atom(type, &a) )
  { if ( (Type = atomToOperatorType(a)) < 0 )
      fail;
  } else if ( !PL_is_variable(type))
    fail;

  if ( !PL_get_atom(name, &Name) &&
       !PL_is_variable(name) )
    fail;

  for( ; op; op = op->next )
  { while(isTableRef(op))
    { op = unTableRef(Operator, op);
      if ( !op )
	fail;
    }
    if ( Name && Name != op->name )
      continue;
    if ( Type >= 0 && Type != op->type  )
      continue;
    if ( Prec > 0 && Prec != op->priority )
      continue;
    if ( op->priority <= 0 )
      continue;

    if ( !PL_unify_atom(name, op->name) ||
	 !PL_unify_atom(type, operatorTypeToAtom(op->type)) ||
	 !PL_unify_integer(prec, op->priority) )
      fail;

    if ( Name && Type >=0 )
      succeed;

    return_next_table(Operator, op, ;);
  }

  fail;
}

/*  The following three functions check whether an atom is declared as
    an operator. 'type' and 'priority' are integer pointers. Their
    value is filled with the corresponding definition of the operator.

 ** Sun Apr 17 13:25:17 1988  jan@swivax.UUCP (Jan Wielemaker)  */

bool
isPrefixOperator(atom_t atom, int *type, int *priority)
{ register Operator op;

  if ((op = isCurrentOperator(atom, OP_PREFIX)) != (Operator) NULL)
  { if (op->priority != 0)
    { *priority = op->priority;
      *type = op->type;

      succeed;
    }
  }

  fail;
}

bool
isPostfixOperator(atom_t atom, int *type, int *priority)
{ Operator op;

  if ((op = isCurrentOperator(atom, OP_POSTFIX)) != (Operator) NULL)
  { if (op->priority != 0)
    { *priority = op->priority;
      *type = op->type;

      succeed;
    }
  }

  fail;
}

bool
isInfixOperator(atom_t atom, int *type, int *priority)
{ Operator op;

  if ((op = isCurrentOperator(atom, OP_INFIX)) != (Operator) NULL)
  { if (op->priority != 0)
    { *priority = op->priority;
      *type = op->type;

      succeed;
    }
  }

  fail;
}

/*  Declare a new operator. 'f' is a functor definition pointer, 'type'
    if one of OP_FX, ... and 'priority' is the priority (0-1200].

 ** Sun Apr 17 13:24:04 1988  jan@swivax.UUCP (Jan Wielemaker)  */

static bool
operator(atom_t name, int type, int priority)
{ Operator op = (Operator) NULL;

  switch(type)
  { case OP_FX:
    case OP_FY:		op = isCurrentOperator(name, OP_PREFIX);
			break;
    case OP_XF:
    case OP_YF:		op = isCurrentOperator(name, OP_POSTFIX);
			break;
    default:		op = isCurrentOperator(name, OP_INFIX);
			break;
  }

  if ( !op )
  { int v;

    v = pointerHashValue(name, OPERATORHASHSIZE);
    op = (Operator) allocHeap(sizeof(struct operator));
    op->next = operatorTable[v];
    operatorTable[v] = op;
    op->name = name;
  }
  op->priority = priority;
  op->type = type;

  succeed;
}

word
pl_op1(term_t priority, term_t type, term_t name)
{ atom_t nm;
  atom_t tp;
  int t;
  int pri;

  if ( !PL_get_atom(name, &nm) ||
       !PL_get_atom(type, &tp) ||
       !PL_get_integer(priority, &pri) ||
       pri < 0 || pri > OP_MAXPRIORITY ||
       (t = atomToOperatorType(tp)) < 0 )
    fail;

  return operator(nm, t, pri);
}

/*  Define standard system operators.

 ** Sun Apr 17 13:25:40 1988  jan@swivax.UUCP (Jan Wielemaker)  */

bool
newOp(char *name, int type, int pri)
{ return operator(lookupAtom(name), type, pri);
}

typedef struct
{ atom_t name;
  char   type;
  short  priority;
} opdef;

#define OP(a, t, p) { a, t, p }

static const opdef operators[] = {
  OP(ATOM_star,		OP_YFX,		400),		/* * */
  OP(ATOM_plus,		OP_FX,		500),		/* + */
  OP(ATOM_plus,		OP_YFX,		500),
  OP(ATOM_comma,	OP_XFY,	       1000),		/* , */
  OP(ATOM_minus,	OP_FX,		500),		/* - */
  OP(ATOM_minus,	OP_YFX,		500),
  OP(ATOM_grammar,	OP_XFX,	       1200),		/* --> */
  OP(ATOM_ifthen,	OP_XFY,	       1050),		/* -> */
  OP(ATOM_softcut,	OP_XFY,	       1050),		/* *-> */
  OP(ATOM_divide,	OP_YFX,		400),		/* / */
  OP(ATOM_div,		OP_YFX,		400),		/* // */
  OP(ATOM_and,		OP_YFX,		500),		/* /\ */
  OP(ATOM_module,	OP_XFY,		600),		/* : */
  OP(ATOM_prove,	OP_FX,	       1200),		/* :- */
  OP(ATOM_prove,	OP_XFX,	       1200),
  OP(ATOM_semicolon,	OP_XFY,	       1100),		/* ; */
  OP(ATOM_bar,		OP_XFY,	       1100),		/* | */
  OP(ATOM_smaller,	OP_XFX,		700),		/* < */
  OP(ATOM_lshift,	OP_YFX,		400),		/* << */
  OP(ATOM_equals,	OP_XFX,		700),		/* = */
  OP(ATOM_univ,		OP_XFX,		700),		/* =.. */
  OP(ATOM_ar_equals,	OP_XFX,		700),		/* =:= */
  OP(ATOM_smaller_equal,OP_XFX,		700),		/* =< */
  OP(ATOM_larger_equal,	OP_XFX,		700),		/* >= */
  OP(ATOM_strick_equal,	OP_XFX,		700),		/* == */
  OP(ATOM_ar_not_equal,	OP_XFX,		700),		/* =\= */
  OP(ATOM_larger,	OP_XFX,		700),		/* > */
  OP(ATOM_rshift,	OP_YFX,		400),		/* >> */
  OP(ATOM_obtain,	OP_FX,		500),		/* ? */
  OP(ATOM_query,	OP_FX,	       1200),		/* ?- */
  OP(ATOM_at_smaller,	OP_XFX,		700),		/* @< */
  OP(ATOM_at_smaller_eq,OP_XFX,		700),		/* @=< */
  OP(ATOM_at_larger,	OP_XFX,		700),		/* @> */
  OP(ATOM_at_larger_eq,	OP_XFX,		700),		/* @>= */
  OP(ATOM_backslash,	OP_FX,		500),		/* \ */
  OP(ATOM_not_provable,	OP_FY,		900),		/* \+ */
  OP(ATOM_or,		OP_YFX,		500),		/* \/ */
  OP(ATOM_not_equals,	OP_XFX,		700),		/* \= */
  OP(ATOM_not_strickt_equals,OP_XFX,	700),		/* \== */
  OP(ATOM_at_equals,	OP_XFX,		700),		/* =@= */
  OP(ATOM_at_not_equals,OP_XFX,		700),		/* \=@= */
  OP(ATOM_hat,		OP_XFY,		200),		/* ^ */
  OP(ATOM_doublestar,	OP_XFX,		200), 		/* ** */
  OP(ATOM_discontiguous,OP_FX,	       1150),		/* discontiguous */
  OP(ATOM_dynamic,	OP_FX,	       1150),		/* dynamic */
  OP(ATOM_volatile,	OP_FX,	       1150), 		/* volatile */
  OP(ATOM_initialization,OP_FX,	       1150), 		/* initialization */
  OP(ATOM_is,		OP_XFX,		700),		/* is */
  OP(ATOM_mod,		OP_YFX,		400),		/* mod */
  OP(ATOM_rem,		OP_YFX,		400),		/* rem */
  OP(ATOM_module_transparent,OP_FX,    1150),		/* module_transparent */
  OP(ATOM_multifile,	OP_FX,	       1150),		/* multifile */
  OP(ATOM_not,		OP_FY,		900),		/* not */
  OP(ATOM_xor,		OP_YFX,		400),		/* xor */
/*OP(ATOM_tilde,	OP_FX,		900),*/		/* ~ */

  OP(NULL_ATOM,		0,		0)
};


void
initOperators(void)
{ { Operator *op;
    int n;

    for(n=0, op=operatorTable; n < (OPERATORHASHSIZE-1); n++, op++)
      *op = makeTableRef(op+1);

    *op = NULL;
  }

  { const opdef *op;

    for( op = operators; op->name; op++ )
      operator(op->name, op->type, op->priority);
  }
}


word
pl_reset_operators()
{ int n;

  for(n=0; n<OPERATORHASHSIZE; n++)
  { Operator op = operatorTable[n];
    Operator next;

    for( ; op && !isTableRef(op); op = next )
    { next = op->next;

      freeHeap(op, sizeof(*op));
    }
  }

  initOperators();

  succeed;
}
