/* Surface
 * (C) Copyright 1995 by Ashton Mason (amason@cs.uct.ac.za)
 *
 * Permission to use, modify, copy and distribute this source code for
 * any purpose and without fee is granted, provided that this copyright
 * notice appears in all copies and supporting documentation, and that
 * credit is given where due. This source code is provided "as is" with
 * no express or implied warranty.
 */


#include "headers.h"


variables vars;				// expression variables


	// constructor


expr_stack::expr_stack(void)
{
  first = NULL;
}


	// deconstructor


expr_stack::~expr_stack(void)
{
	// could delete nodes here - do we want to?
}


	// push an expression onto the stack


int expr_stack::push(expr *pushval)
{
  enode *newnode = new enode;
  if (!newnode)
  {
    error("out of memory for colour expression");
    return 0;
  }

  newnode->val = pushval;		// point to expression

  newnode->next = first;		// link in at front
  first = newnode;

  return 1;
}


	// pop an expression from the stack


int expr_stack::pop(expr **popval)
{
  enode *p;

  if (first)
  {
    *popval = first->val;
    p = first;
    first = first->next;
    delete p;
    return 1;
  }

  popval = NULL;
  return 0;
}


	// return the top element of the stack


int expr_stack::top(expr **topval)
{
  if (first)
  {
    *topval = first->val;
    return 1;
  }

  topval = NULL;
  return 0;
}


	// constructor


parser::parser(void)
{
  expression = NULL;

  numincs = 0;
  for (int n = 0; n < MAXINCS; n++)
  {
    included[n] = NULL;
  }
}


	// deconstructor


parser::~parser(void)
{
}


	// open file for parsing


int parser::openFile(char *name)
{
  return(lex.openFile(name));
}


	// close parsed file


int parser::closeFile()
{
  return(lex.closeFile());
}


	// check whether token is an operand


int parser::isOperand(symbol token)
{
  return(symbol_type[token] == OPERAND);
}


	// return the priority of the operator


int parser::priority(symbol token)
{
  return(symbol_priority[token]);
}

	// return the number of operands of the operator


int parser::operands(symbol token)
{
  return(operand_count[token]);
}


	// build a complex subexpression and push it as an operand


int parser::buildComplex()
{
  expr *node, *operand;
  int n, subexpressions;

  operatorStack.pop(&node);			// pop operator

  subexpressions = operands(node->operation);

  for (n = 0; n < subexpressions; n++)
  {
    if (!operandStack.pop(&operand))		// pop operand
    {
      printf("operator: %s\n", keywords[node->operation]);
      error("missing operand for operator in colour expression");
      return 0;
    }

    node->addSubexpression(subexpressions - n - 1, operand);
  }

  operandStack.push(node);			// push new operand

  return 1;
}


	// parse expression from file and build expression tree


int parser::parse()
{
  symbol token;
  expr *node, *operand;

  token = lex.getSymbol();			// get first token
  while (token != NOP)				// while not EOF
  {
    if (token == LEFT_P)			// left parenthesis
    {
      node = new expr;
      node->operation = LEFT_P;
      operatorStack.push(node);			// push onto operator stack
    }
    else if (token == RIGHT_P)			// right parenthesis
    {
      while (operatorStack.top(&node) && node->operation != LEFT_P)
      {
        buildComplex();				// do stuff in brackets
      }

      if (!operatorStack.top(&node))
      {
	error("no matching left bracket for right bracket in expression");
        return 0;
      }

      operatorStack.pop(&node);			// remove left bracket
    }
    else if (isOperand(token))			// operand
    {
      switch (token)
      {
	case CONST:	        		// constant
	  node = new expr;
	  node->operation = token;
	  node->val = lex.lastValue();
	  operandStack.push(node);		// push onto operand stack
	  break;
	case NAME:				// named expression
	  node = included[lex.lastName()];	// substitute expression
	  operandStack.push(node);		// push onto operand stack
	  break;
	default:				// variable
	  node = new expr;
	  node->operation = token;
	  operandStack.push(node);		// push onto operand stack
      }
    }
    else					// operator
    {

	// while there is an operator on the stack
	// of higher *or same* priority

      while (operatorStack.top(&node) && priority(token) <= priority(node->operation))
      {
        buildComplex();
      }

      node = new expr;
      node->operation = token;
      operatorStack.push(node);		// push onto operator stack
    }

    token = lex.getSymbol();			// get next token
  }

  	// all of input read
        // now must process operator stack

  while (operatorStack.top(&node))		// while unused operators
  {
    buildComplex();
  }

  operandStack.pop(&node);			// pop complete tree

  if (operandStack.top(&operand))		// check for errors
  {
    error("extra operand in colour expression");
    return 0;
  }

  expression = node;
  return 1;
}


	// get the name of the declared expression from the
	// #define statement


int parser::getDefine(char *exprname)
{
  return(lex.getDefine(exprname));
}


	// get the name of the included file from the next
	// #include statement


int parser::getInclude(char *incfile)
{
  return(lex.getInclude(incfile));
}


	// add an included expression to the parser
        // allows that expression to be recognised
	// and substituted in the file to be parsed


int parser::include(expr *incexpr, char *incname)
{
  if (numincs == MAXINCS)
  {
    error("too many included expressions");
  }

  included[numincs] = incexpr;
  numincs++;

  lex.include(incname);			// add the name to valid tokens
  return 1;
}


	// parse an expression file
        // returns the constructed expression


expr *parseFile(char *filename, char *exprname)
{
  parser par;				// parser for this file
  char incfile[50];			// name of include file
  char incname[50];			// name of included expression

  par.openFile(filename);
  par.getDefine(exprname);		// get the 'define' statement

  printf("parsing expression `%s'\n", exprname);

  while (par.getInclude(incfile))	// get 'include' statement, if any
  {
    expr *incexpr = parseFile(incfile, incname);
    if (!incexpr)
    {
      error("null included expression - program error");
    }
    par.include(incexpr, incname);	// register the included expression
  }

  par.parse();				// parse the file, using includes
  par.closeFile();
  return(par.expression);		// return the constructed expression
}

