/******************************** -*- C -*- ****************************
 *
 *	Semantic Tree manipulation module.
 *
 *	$Revision: 1.7.5$
 *	$Date: 2000/05/28 16:56:52$
 *	$Author: pb$
 *
 ***********************************************************************/

/***********************************************************************
 *
 * Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
 * Written by Steve Byrne.
 *
 * This file is part of GNU Smalltalk.
 *
 * GNU Smalltalk 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.
 * 
 * GNU Smalltalk 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
 * GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
 *
 ***********************************************************************/


#include "gst.h"
#include "alloc.h"
#include "obstack.h"
#include "sym.h"
#include "tree.h"
#include "dict.h"
#include "lex.h"
#include <stdio.h>

char	*nilName = "(nil)"; /* how to print nil */


mst_Boolean hadError = false;

static TreeNode makeMethodNode(), makeBlockNode(), makeListNode(), 
		makeExprNode();
static TreeNode makeTreeNode();
static OOP	makeUnarySelector(), makeBinarySelector();

#ifdef prev_obstacks
/**/static void	freeNode(), freeMethodNode(), freeBlockNode(), freeExprNode(),
/**/		freeListNode(), freeConstNode();
#endif

static void	printMethodNode(), printBlockNode(), printExprNode(), 
		printListNode(), printConstNode(), printNodeType(), indent(),
		printSelector();


/* Used only for printing tree node names when debugging */
static char *nodeTypeNames[] = {
  "methodNodeType", 		/* methodNodeType */
  "unaryExprType", 		/* unaryExprType */
  "binaryExprType", 		/* binaryExprType */
  "keywordExprType", 		/* keywordExprType */
  "variableNodeType", 		/* variableNodeType */
  "keywordListType", 		/* keywordListType */
  "variableListType", 		/* variableListType */
  "statementListType", 		/* statementListType */
  "returnExprType", 		/* returnExprType */
  "assignExprType", 		/* assignExprType */
  "constExprType", 		/* constExprType */
  "symbolNodeType", 		/* symbolNodeType */
  "arrayEltListType", 		/* arrayEltListType */
  "blockNodeType", 		/* blockNodeType */
  "cascadedMessageNodeType", 	/* cascadedMessageNodeType */
  "messageListType" 		/* messageListType */
};



/*
 *	TreeNode makeArrayElt(elt)
 *
 * Description
 *
 *	Create an element of an array constant, which is a list type object.
 *	Return the element with the next field NILed out.
 *
 * Inputs
 *
 *	elt   : TreeNode array element to use
 *
 * Outputs
 *
 *	TreeNode of type arrayEltListType that contains "elt".
 */
TreeNode
makeArrayElt(elt)
     TreeNode elt;
{
  return (makeListNode(arrayEltListType, nil, elt));
}

  
/*
 *	TreeNode makeMethod(selectorExpr, temporaries, primitiveIndex,
 *			    statements)
 *
 * Description
 *
 *	Create a method node.  The method will be invoked by a selector dervied
 *	from "selectorExpr", it has (possibly nil) "temporaries" variables,
 *	and contains "statements".  If the method has a primitive associated
 *	with it, then "primitiveIndex" is non-zero.
 *
 * Inputs
 *
 *	selectorExpr: 
 *		Expression that's to be the selector for this method.
 *	temporaries: 
 *		Possibly nil list of temporary variable names.
 *	primitiveIndex:
 *		Integer.  If non-zero, this method has associated with it
 *		a primitive with index "primitiveIndex".
 *	statements: 
 *		List of statements that comprise the procedural part of this
 *		method.
 *
 * Outputs
 *
 *	TreeNode of type methodNodeType.
 */
TreeNode
makeMethod(selectorExpr, temporaries, primitiveIndex, statements)
     TreeNode selectorExpr, temporaries, statements;
     int	primitiveIndex;
{
  return (makeMethodNode(selectorExpr, temporaries,
			 primitiveIndex, statements));
}


/*
 *	TreeNode makeCascadedMessage(messageExpr, cascadedMessages)
 *
 * Description
 *
 *	Creates a node for holding a list of cascaded messages (basically an
 *	Expr node that isn't using its symbol.  "messageExpr" is the expression
 *	invoke first as it computes the receiver.  Then the remaining cascaded
 *	messages are sent to that receiver.
 *
 * Inputs
 *
 *	messageExpr: 
 *		Evaluates to the receiver of the cascaded messages
 *	cascadedMessages: 
 *		List of the cascaded messages to send to the receiver.
 *
 * Outputs
 *
 *	TreeNode of type cascadedMessageTypeNode.
 */
TreeNode
makeCascadedMessage(messageExpr, cascadedMessages)
     TreeNode messageExpr, cascadedMessages;
{
  return (makeExprNode(cascadedMessageNodeType, messageExpr, nil,
		       cascadedMessages));
}


TreeNode
makeUnaryExpr(receiver, unarySelectorExpr)
     TreeNode receiver;
     char	 *unarySelectorExpr;
{
  OOP		selector;

  /* selectors, being interned symbols, don't need to be incubated -- symbols
   * once created are always referenced
   */
  selector = makeUnarySelector(unarySelectorExpr);
  return (makeExprNode(unaryExprType, receiver, selector, nil));
}

TreeNode
internBinOP(binaryOp)
     char	*binaryOp;
{
  return (makeExprNode(symbolNodeType, nil, makeBinarySelector(binaryOp),
		       nil));
}

TreeNode
internIdent(ident)
     char	*ident;
{
  return (makeExprNode(symbolNodeType, nil, internString(ident), nil));
}

TreeNode
makeStatementList(expression, statements)
     TreeNode expression, statements;
{
  return (makeExprNode(statementListType, expression, nilOOP, statements));
}

TreeNode
makeReturn(expression)
     TreeNode expression;
{
  return (makeExprNode(returnExprType, expression, nilOOP, nil));
}

TreeNode
makeKeywordExpr(receiver, keywordMessage)
     TreeNode receiver, keywordMessage;
{
  return (makeExprNode(keywordExprType, receiver, nilOOP, keywordMessage));
}

TreeNode
makeAssign(variables, expression)
     TreeNode variables, expression;
{
  return (makeExprNode(assignExprType, variables, nilOOP, expression));
}

TreeNode
makeKeywordList(keyword, expression)
     char	*keyword;
     TreeNode expression;
{
  return (makeListNode(keywordListType, keyword, expression));
}

/*
 *	TreeNode makeVariableList(variable)
 *
 * Description
 *
 *	Given a variable tree node, this routine returns a variable list tree
 *	node with a nil next link.  Actually, we rely on the fact that a
 *	variable is represented as a tree node of type ListNode, so  all we do
 *	is change the node tag to variableListType.
 *
 * Inputs
 *
 *	variable: 
 *		Name of variable that's to be part of the list, TreeNode.
 *
 * Outputs
 *
 *	New TreeNode.
 */
TreeNode
makeVariableList(variable)
     TreeNode variable;
{
  variable->nodeType = variableListType;
  return (variable);
}


TreeNode
makeBinaryExpr(receiver, binaryOp, argument)
     TreeNode receiver, argument;
     char	*binaryOp;
{
  OOP		selector;

  selector = makeBinarySelector(binaryOp);
  return (makeExprNode(binaryExprType, receiver, selector, argument));
}

TreeNode
makeMessageList(messageElt)
     TreeNode messageElt;
{
  return (makeListNode(messageListType, nil, messageElt));
}

/*
 *	TreeNode makeBlock(arguments, temporaries, statements)
 *
 * Description
 *
 *	Creates a block tree node and returns it.
 *
 * Inputs
 *
 *	arguments: 
 *		The arguments to the block, possibly nil.
 *	temporaries: 
 *		Possibly nil list of temporary variable names to use for this
 *		block 
 *	statements: 
 *		List of statements that are the procedure part of this block.
 *
 * Outputs
 *
 *	New tree node.
 */
TreeNode
makeBlock(arguments, temporaries, statements)
     TreeNode arguments, temporaries, statements;
{
  return (makeBlockNode(arguments, temporaries, statements));
}

TreeNode
makeVariable(name)
     char	*name;
{
  return (makeListNode(variableNodeType, name, nil));
}


TreeNode
makeIntConstant(ival)
     long	ival;
{
  TreeNode 	result;

  result = makeTreeNode(constExprType);
  result->vConst.constType = intConst;
  result->vConst.val.iVal = ival;

  return (result);
}

TreeNode
makeByteObjectConstant(boval)
     ByteObject boval;
{
  TreeNode 	result;

  result = makeTreeNode(constExprType);
  result->vConst.constType = byteObjConst;
  result->vConst.val.boVal = boval;

  return (result);
}

TreeNode
makeFloatConstant(fval)
     double	fval;
{
  TreeNode 	result;

  result = makeTreeNode(constExprType);
  result->vConst.constType = floatConst;
  result->vConst.val.fVal = fval;

  return (result);
}

TreeNode
makeCharConstant(cval)
     char	cval;
{
  TreeNode 	result;

  result = makeTreeNode(constExprType);
  result->vConst.constType = charConst;
  result->vConst.val.cVal = cval;

  return (result);
}

TreeNode
makeStringConstant(sval)
     char	*sval;
{
  TreeNode 	result;

  result = makeTreeNode(constExprType);
  result->vConst.constType = stringConst;
  result->vConst.val.sVal = sval;

  return (result);
}

TreeNode
makeSymbolConstant(symbolNode)
     TreeNode symbolNode;
{
  TreeNode 	result;

  result = makeTreeNode(constExprType);
  result->vConst.constType = symbolConst;
  if (symbolNode) {
    result->vConst.val.symVal = symbolNode->vExpr.selector;
#ifdef prev_obstacks
/**/    freeNode(symbolNode);
#endif
  } else {
    result->vConst.val.symVal = nilOOP;
  }

  return (result);
}

/* This function converts an Array constant's format (linked list of its
 * elements) to a ByteArray constant's format (ByteObject struct).  The code
 * itself is awful and the list is extremely space inefficient, but consider
 * that:
 * a) it makes the parser simpler (Arrays and ByteArrays are treated in almost
 *    the same way; only, the latter call this function and the former don't).
 * b) a list is indeed an elegant solution because we don't know the size of
 *    the byte array until we have parsed it all (that is, until we call this
 *    function.
 * c) the ByteObject is the best format for ByteArrays: first, it is the one
 *    which makes it easiest to make a full-fledged object out of the parse
 *    tree; second, it is logical to choose it since LargeIntegers use it,
 *    and ByteArrays are represented exactly the same as LargeIntegers.
 */
TreeNode
makeByteArrayConstant(aval)
     TreeNode aval;
{
  TreeNode      arrayElt, ival;
  int           len;
  ByteObject	bo;
  Byte		*data;

  for(len = 0, arrayElt = aval; arrayElt;
      len++, arrayElt = arrayElt->vList.next);

  bo = (ByteObject)obstack_alloc(compilationObstack,
    sizeof(ByteObjectStruct) + len);

  bo->class = byteArrayClass;
  bo->size = len;
  data = bo->body;

  /* Now extract the node for each integer constant, storing its value
   * into the ByteObject */
  for (arrayElt = aval; arrayElt; arrayElt = arrayElt->vList.next) {
    ival = arrayElt->vList.value;
    *data++ = ival->vConst.val.iVal;
  }

#ifdef prev_obstacks
/**/    freeNode(aval);
#endif
  return (makeByteObjectConstant(bo));
}

TreeNode
makeArrayConstant(aval)
     TreeNode aval;
{
  TreeNode 	result;

  result = makeTreeNode(constExprType);
  result->vConst.constType = arrayConst;
  result->vConst.val.aVal = aval;

  return (result);
}

/*
 *	void addNode(n1, n2)
 *
 * Description
 *
 *	adds node "n2" onto a list of nodes headed by "n1".  "n1" contains
 *	the address of the last "next" field in the chain, so storing "n2" into
 *	there indirectly and then making that "next" field point to "n2"'s
 *	"next" field works properly.
 *
 * Inputs
 *
 *	n1    : head of list of nodes, of type listNode.
 *	n2    : node to be added, of type listNode.
 *
 */
void
addNode(n1, n2)
     TreeNode n1, n2;
{
  *(n1->vList.nextAddr) = n2;
  n1->vList.nextAddr = n2->vList.nextAddr; /* since they're all created this
					    * way anyway, we might as well
					    * use it to our advantage */
}

void
freeTree(tree)
     TreeNode tree;
{
  if (tree == nil) {
    return;
  }

  obstack_free(compilationObstack, NULL);
  obstack_init(compilationObstack);

#ifdef prev_obstacks
/**/  switch (tree->nodeType) {
/**/  case methodNodeType:
/**/    freeMethodNode(tree);
/**/    break;
/**/    
/**/  case blockNodeType:
/**/    freeBlockNode(tree);
/**/    break;
/**/
/**/  case symbolNodeType:
/**/  case unaryExprType:
/**/  case binaryExprType:
/**/  case keywordExprType:
/**/  case cascadedMessageNodeType:
/**/  case statementListType:
/**/  case returnExprType:
/**/  case assignExprType:
/**/    freeExprNode(tree);
/**/    break;
/**/    
/**/  case variableNodeType:
/**/  case keywordListType:
/**/  case variableListType:
/**/  case arrayEltListType:
/**/  case messageListType:
/**/    freeListNode(tree);
/**/    break;
/**/
/**/  case constExprType:
/**/    freeConstNode(tree);
/**/    break;
/**/
/**/  }
#endif
}





/***********************************************************************
 *
 * Internal tree construction routines.
 *
 ***********************************************************************/


static TreeNode
makeMethodNode(selectorExpr, temporaries, primitiveIndex, statements)
     TreeNode selectorExpr, temporaries, statements;
     int	primitiveIndex;
{
  TreeNode	result;

  result = makeTreeNode(methodNodeType);
  result->vMethod.selectorExpr = selectorExpr;
  result->vMethod.temporaries = temporaries;
  result->vMethod.primitiveIndex = primitiveIndex;
  result->vMethod.statements = statements;
  return (result);
}

static TreeNode
makeBlockNode(arguments, temporaries, statements)
     TreeNode arguments, temporaries, statements;
{
  TreeNode	result;

  result = makeTreeNode(blockNodeType);
  result->vBlock.arguments = arguments;
  result->vBlock.temporaries = temporaries;
  result->vBlock.statements = statements;
  return (result);
}

static TreeNode
makeListNode(nodeType, name, value)
     NodeType nodeType;
     char	*name;
     TreeNode value;
{
  TreeNode result;

  result = makeTreeNode(nodeType);
  result->vList.name = name;
  result->vList.value = value;
  result->vList.next = nil;
  result->vList.nextAddr = &result->vList.next;
  return (result);
}

static TreeNode
makeExprNode(nodeType, receiver, selector, expression)
     NodeType nodeType;
     TreeNode receiver, expression;
     OOP	selector;
{
  TreeNode result;

  result = makeTreeNode(nodeType);
  result->vExpr.receiver = receiver;
  result->vExpr.selector = selector;
  result->vExpr.expression = expression;
  return (result);
}

static TreeNode
makeTreeNode(nodeType)
     NodeType nodeType;
{
  TreeNode result;

  result = (TreeNode)obstack_alloc(compilationObstack,
    sizeof(struct TreeNodeStruct));

  result->nodeType = nodeType;
  return (result);
}

/* ### these should probably be moved over into the symbol table module, yes?*/

static OOP
makeUnarySelector(name)
     char	*name;
{
  return (internString(name));
}

static OOP
makeBinarySelector(binaryOp)
     char	*binaryOp;
{
  return (internString(binaryOp));
}




/***********************************************************************
 *
 * Internal tree destruction routines.
 *
 ***********************************************************************/

#ifdef prev_obstacks
/**/static void
/**/freeMethodNode(node)
/**/     TreeNode node;
/**/{
/**/  freeTree(node->vMethod.selectorExpr);
/**/  freeTree(node->vMethod.temporaries);
/**/  freeTree(node->vMethod.statements);
/**/  freeNode(node);
/**/}
/**/
/**/static void
/**/freeBlockNode(node)
/**/     TreeNode node;
/**/{
/**/  freeTree(node->vBlock.arguments);
/**/  freeTree(node->vBlock.temporaries);
/**/  freeTree(node->vBlock.statements);
/**/  freeNode(node);
/**/}
/**/
/**/static void
/**/freeExprNode(node)
/**/     TreeNode node;
/**/{
/**/  freeTree(node->vExpr.receiver);
/**/  freeTree(node->vExpr.expression);
/**/  freeNode(node);
/**/}
/**/
/**/static void
/**/freeListNode(node)
/**/     TreeNode node;
/**/{
/**/  freeTree(node->vList.value);
/**/  freeTree(node->vList.next);
/**/  if (node->vList.name) {
/**/    xfree(node->vList.name);
/**/  }
/**/
/**/  freeNode(node);
/**/}
/**/
/**/static void
/**/freeConstNode(node)
/**/     TreeNode node;
/**/{
/**/  switch (node->vConst.constType) {
/**/  case intConst:
/**/  case floatConst:
/**/  case charConst:
/**/  case symbolConst:
/**/    /* these have no storage of their own */
/**/    break;
/**/
/**/  case byteObjConst:
/**/    if (node->vConst.val.boVal) {
/**/      xfree(node->vConst.val.boVal);
/**/    } else {
/**/      errorf("Internal error: badly formed tree for byte object constant"); 
/**/    }
/**/    break;
/**/
/**/  case stringConst:
/**/    if (node->vConst.val.sVal) {
/**/      xfree(node->vConst.val.sVal);
/**/    } else {
/**/      errorf("Internal error: badly formed tree for string constant"); 
/**/    }
/**/    break;
/**/
/**/  case arrayConst:
/**/    freeTree(node->vConst.val.aVal);
/**/    break;
/**/
/**/  default:
/**/    errorf("Internal error: corrupted tree structure"); 
/**/  }
/**/
/**/  freeNode(node);
/**/}
/**/
/**/
/**/static void
/**/freeNode(node)
/**/     TreeNode node;
/**/{
/**/  xfree(node);
/**/}
/**/
#endif


/***********************************************************************
 *
 *	Printing routines.
 *
 ***********************************************************************/


void
printTree(node, level)
     TreeNode node;
     int level;
{
  if (node == nil) {
    indent(level);
    printf("%s\n", nilName);
    return;
  }

  switch (node->nodeType) {
  case methodNodeType:
    printMethodNode(node, level);
    break;
    
  case blockNodeType:
    printBlockNode(node, level);
    break;

  case symbolNodeType:
  case unaryExprType:
  case binaryExprType:
  case keywordExprType:
  case cascadedMessageNodeType:
  case statementListType:
  case returnExprType:
  case assignExprType:
    printExprNode(node, level);
    break;
    
  case variableNodeType:
  case keywordListType:
  case variableListType:
  case arrayEltListType:
  case messageListType:
    printListNode(node, level);
    break;

  case constExprType:
    printConstNode(node, level);
    break;

  default:
    errorf("Unknown tree note type %d\n", node->nodeType);
  }
}


static void
printListNode(node, level)
     TreeNode node;
     int level;
{
  printNodeType(node, level);
  indent(level+1);
  printf("name: %s\n", node->vList.name ? node->vList.name : nilName);
  indent(level+1);
  printf("value:\n");
  printTree(node->vList.value, level+2);
  indent(level+1);
  printf("next:\n");
  printTree(node->vList.next, level);
}

static void
printExprNode(node, level)
     TreeNode node;
     int level;
{
  printNodeType(node, level);
  indent(level+1);
  printf("selector: ");
  if (!isNil(node->vExpr.selector)) {
    printSelector(node->vExpr.selector);
  } else {
    printf("%s", nilName);
  }
  printf("\n");
    
  indent(level+1);
  printf("receiver:\n");
  printTree(node->vExpr.receiver, level+2);
  /* ??? don't print the expression for unary type things, and don't print
     the receiver for symbol nodes */
  indent(level+1);
  printf("expression:\n");
  printTree(node->vExpr.expression, level+2);
}

static void printMethodNode(node, level)
TreeNode node;
int level;
{
  printNodeType(node, level);
  indent(level+1);
  printf("selectorExpr: ");
  printTree(node->vMethod.selectorExpr, level+2);
  indent(level+1);
  /* ??? don't print the temporaries label if there are no temporaries */
  printf("temporaries:\n");
  printTree(node->vMethod.temporaries, level+2);
  indent(level+1);
  printf("statements:\n");
  printTree(node->vMethod.statements, level+2);
}

static void
printBlockNode(node, level)
     TreeNode node;
     int level;
{
  printNodeType(node, level);
  indent(level+1);
  printf("arguments:\n");
  printTree(node->vBlock.arguments, level+2);
  indent(level+1);
  /* ??? don't print the temporaries label if there are no temporaries */
  printf("temporaries:\n");
  printTree(node->vBlock.temporaries, level+2);
  indent(level+1);
  printf("statements:\n");
  printTree(node->vBlock.statements, level+2);
}

static void
printConstNode(node, level)
     TreeNode node;
     int level;
{
  indent(level);
  switch (node->vConst.constType) {
  case intConst:
    printf("int: %ld\n", node->vConst.val.iVal);
    break;

  case floatConst:
    printf("float: %g\n", node->vConst.val.fVal);
    break;

  case charConst:
    printf("char: %c\n", node->vConst.val.cVal);
    break;

  case stringConst:
    printf("string: \"%s\"\n", node->vConst.val.sVal);
    break;

  case symbolConst:
    printf("symbol: ");
    printSymbol(node->vConst.val.symVal);
    printf("\n");
    break;

  case arrayConst:
    printf("array:\n");
    printTree(node->vConst.val.aVal, level+1);
    break;
    
  default:
    errorf("Unknown constant type %d", node->vConst.constType);
  }
}


static void
printNodeType(node, level)
     TreeNode node;
     int level;
{
  indent(level);
  printf("%s\n", nodeTypeNames[ENUM_INT(node->nodeType)]);
}

/*
 *	static void indent(level)
 *
 * Description
 *
 *	Indent the output by level*2 spaces.
 *
 * Inputs
 *
 *	level : Indentation level.  C integer.
 *
 */
static void
indent(level)
     int level;
{
  for (; level > 0; level--) {
    printf("  ");
  }
}

static void
printSelector(selector)
     OOP	selector;
{
  printSymbol(selector);
}

