/*
 * cccc_ast.C
 * Abstract syntax tree implemenation for the cccc project
 */
#include <iostream.h>
#include <iomanip.h>
#include <string.h>
#include <ctype.h>
#include "cccc_ast.h"
#include "cccc_met.h"

#define NEW_PAGE "\f\r"

#define CANONICAL_NAME_WIDTH 60
#define LINE_NUMBER_WIDTH 4

/* static variables */
char *AST::szCurrentRuleName;
int asts_alloc1=0, asts_alloc2=0, asts_alloc3=0;
int asts_freed=0;

int isidchar(int c) {
  /* 
  ** tests for a valid character for an identifier 
  ** NB input parameter c is an int to imitate the interface of isalnum() 
  */

  /* underscore, alphabetic and numerics are legal in identifiers ... */
  if ( c=='_' ) return TRUE;
  /* else */
  return isalnum(c);
}

/*
** indent provides indentation to a level on an output stream
*/
void indent(ostream& os, int nLevel) {
  const char *indentstring="  ";
  os << endl;
  int i;
  for(i=0;i<nLevel;i++) {
    os<< indentstring;
  }
}

/*
** AppendInBuffer appends a string to another string in a fixed size buffer.
** It is designed to avoid overrunning the buffer length, and to insert a 
** single space between the two strings if they would change their meaning
** to a C compiler if appended directly.  Basically this means that if 
** both buffers are identifiers, numbers or keywords a space is inserted,
** while operator symbols such as * :: -> etc. are allowed to be attached
** directly
*/
char *AppendInBuffer(char *Dest, char *Src, int DestBufLen) {

  /* unless Src is nonzero length, no change needs to be made */
  if( strcmp(Src,"") ) {
    if ( strcmp(Dest,"") ) {
      if ( isidchar(Dest[strlen(Dest)-1]) && isidchar(Src[0]) ) {
        /* 
        ** The last character in the existing buffer and the first in the
        ** token being appended are characters which are legal an identifier, 
        ** so we need a space to indicate where the last token ends and 
        ** the next begins (NB the actual tokens may be identifiers, or they
        ** may be keywords or numeric constants)
        */
        strncat(Dest," ",DestBufLen-strlen(Dest)-1);
        strncat(Dest,Src,DestBufLen-strlen(Dest)-1);
     } else {
        strncat(Dest,Src,DestBufLen-strlen(Dest)-1);
     }
    } else {
      /* Dest has zero length */
      strncpy(Dest,Src,DestBufLen-1);
    }
  }

  /* ensure that Dest is null-terminated */
  Dest[DestBufLen-1]='\0';
  return Dest;
}

/*
** AST::AST(ANTLRTokenPtr t) is required by the PCCTS internal routines
*/
AST::AST(ANTLRTokenPtr t) {
  _right = _down = 0;
  szRuleName=szCurrentRuleName;
  token = ( * mytoken(t) ); 
  bPrintNode=0; 
  bCanonicalElement=1; 
  // DbgMsg(MEMORY,cerr,*this);
  asts_alloc1++;
} 

/*
** copy constructor for AST
*/
AST::AST(AST& ast) {
  
  szRuleName=szCurrentRuleName; 
  token = ast.token;
  bPrintNode=ast.bPrintNode;
  bCanonicalElement=ast.bCanonicalElement;
  asts_alloc2++;
}

/*
** default constructor for AST
*/
AST::AST() { 
  _right = _down = 0;
  szRuleName=szCurrentRuleName;
  asts_alloc3++;
}

/*
** destructor for AST
*/
AST::~AST() {
  free_subtrees();
  asts_freed++;
  DbgMsg(MEMORY,cerr,
    "freeing AST rooted at " << token.getText() 
	 << " on line " << token.getLine()
	 << " c1:" << asts_alloc1 << " c2:" << asts_alloc2 
	 << " c3:" << asts_alloc3 << " freed: " << asts_freed << endl);
}

/*
** assignment operator for AST
*/
AST& AST::operator=(AST& ast) {
  free_subtrees();
  szRuleName=ast.szRuleName; 
  token = ast.token;
  bPrintNode=ast.bPrintNode;
  bCanonicalElement=ast.bCanonicalElement;
  ((ASTBase) (*this))=((ASTBase) ast);
  return *this;
}

/*
** AST::free_subtrees recursively deletes the current AST's subtrees
*/
void AST::free_subtrees() {
  if(this != NULL)
    {
  delete (AST*) _right;
  delete (AST*) _down;
  _right=0;
  _down=0;
    }
}

/*
** each token in the AST contains a record of the current value of each of 
** the counts (comment lines, non-comment lines of code, McCabes VG) when 
** that token was created by the lexer. 
** AST::getCount traverses an AST finding the difference between the value
** of one of these counts between the first and last tokens in the AST
*/
int AST::getCount(TokenCount tc) 
{
  return last_token()->getCount(tc)-first_token()->getCount(tc);
}

#define MAX_TOK 10

/*
** AST::canonical_name generates a canonical name for the extent of code
** represented by the AST by traversal of the tree accumulating tokens
** using AppendInBuffer.  Note that the names created do not include
** strings generated by parser constructs where tree generation is
** disabled in cccc.g.m4 using the PCCTS ! operator.  This enables
** the stripping of the contents of all parenthesis, brackets and braces,
** argument names and initializers etc.
*/
CCCC_String& AST::canonical_name() {
  static CCCC_String retval;
  char NameBuffer[CANONICAL_NAME_WIDTH+1];
  NameBuffer[0]='\0';
  AST *tree=this;

  /* otherwise */  
  while( ( tree != NULL ) && ( strlen(NameBuffer) < CANONICAL_NAME_WIDTH ) ) {
    if ( tree->_down == NULL ) {
      AppendInBuffer(
        NameBuffer,
        tree->token.getText(),
        CANONICAL_NAME_WIDTH
      );
    } else {
      char *pCanonicalName= ( (AST*) (tree->_down) )->canonical_name();
      AppendInBuffer(
        NameBuffer,
        pCanonicalName,
        CANONICAL_NAME_WIDTH
      );
      free(pCanonicalName);
    }
    tree = ( (AST*) (tree->_right) );
  }
  NameBuffer[CANONICAL_NAME_WIDTH]='\0';

  retval=NameBuffer;
  return retval;
}  

// AST::first_token() returns a pointer to the first token in the tree
ANTLRToken *AST::first_token()
{
  AST *child = (AST*) this;
  while( child->_down != 0 ) 
  {
    child = (AST*) child->_down;
  }
  return &(child->token);
}

// AST::last_token() returns a pointer to the last token in the tree
ANTLRToken *AST::last_token()
{
  AST *child = this;

  // go as far right as possible
  while ( child->_right != 0) 
  {
    child = (AST*) child->_right;
  }

  // if the rightmost subtree is a leaf node, return a pointer to it
  // otherwise recurse onto the subtree
  if ( child->_down ==0 ) 
  {
    return &(child->token);
  } 
  else 
  {
    return ((AST *) (child->_down))->last_token();
  }
}

/* 
** AST::nStartLine returns the line number where the first token is found
*/  
int AST::nStartLine() 
{
   return first_token()->getLine();
}

/*
** AST::nEndLine returns the line number where the last token is found
*/
int AST::nEndLine() 
{
  return last_token()->getLine();
}

int nDepth=0;

/* 
** the virtual preorder traversal method defined in the PCCTS base
** classes for AST was used to implement a formatted output for AST objects
** This has now been superseeded by the method ostream& operator <<() below
*/
void AST::preorder(ostream& os) {}
void AST::preorder_action() {}
void AST::preorder_before_action() {}
void AST::preorder_after_action() {}

/* 
** formatted output for AST's
*/
ostream& operator << (ostream& os, AST& ast) {
  static int nDepth=0;
  indent(os,nDepth);
  os << "AST: " << ast.token;
  nDepth++;
  if(ast._down!=0) {
    indent(os,nDepth);
    os << "down:" << endl;
    os << *( (AST*) (ast._down) );
  } 
  if(ast._right!=0) {
    indent(os,nDepth);
    os << "right:" << endl;
    os << *( (AST*) (ast._right) );
  } 
  nDepth--;
  os << endl;
  return os;
}
