%{
/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : idl.y                                                         */
/* Author   : Tom O'Reilly                                                  */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include "IdlParser.h"
#include "Stack.h"
#include "idl.h"
  
#define dprintf if (debug) fprintf
#define dprintf2 if (debug2) fprintf

bool debug = true;
bool debug2 = false;

// Function prototypes
int yyerror(char *errorBuf);
int yylex();
int yyparse();
int idlFileLineNo();


#ifndef _QNX
extern "C" 
{
  void *alloca (unsigned size);
};
#endif

const int maxStackLevels = 16;
Stack<Context *> _contextStack(maxStackLevels);

Method *_method = 0;
Enumeration *_enumeration = 0;
Context *_context = 0;
Structure *_structure = 0;
Interface *_interface = 0;
DataDefinition *_definedType;

DynamicArray<Interface *> _interfaces;
DynamicArray<Method::Argument *> _arguments;
DynamicArray<Enumeration::Value *> _enumeratedValues;
DynamicArray<Comment *> _comments;
DynamicArray<char *> _contextNames;

Method::IoSpecifier _ioSpecifier;
DataType _dataType;

char parseErrorBuf[512];

const int eofReturn = -1;
const int errorReturn = 1;
const int okReturn = 0;

%}

%union 
{
  int integer;
  char *string;
}

%token <string> CommentString
%token <string> Name
%token <integer> Integer
%token InterfaceTag StructTag EnumerationTag IncludeTag
%token InTag OutTag InOutTag 
%token FloatTag DoubleTag LongTag ShortTag BooleanTag CharTag
%token UnsignedCharTag UnsignedShortTag UnsignedLongTag VoidTag StringTag
%token SequenceTag StartBoundTag EndBoundTag 
%token TypedefTag
%token ContextDelimiterTag

%%

Interface:
{
  // Empty
  return eofReturn;
}

Interface: StartInterface InterfaceMembers '}' ';'
{
  dprintf(stderr, "Interface, no leading comments\n");
  _context = _contextStack.pop();
  YYACCEPT;
};

Interface: Comments StartInterface InterfaceMembers '}' ';'
{
  dprintf(stderr, "Interface, w/ leading comments\n");
  _context = _contextStack.pop();
  YYACCEPT;
};

Comments: CommentString 
{
  dprintf2(stderr, "Comments: %s\n", $1);
  Comment *comment = new Comment($1);
  _comments.add(&comment);
};

Comments: Comments CommentString
{
  dprintf2(stderr, "Comments: %s\n", $2);
  Comment *comment = new Comment($2);
  _comments.add(&comment);
};

StartInterface: InterfaceTag Name '{'
{
  dprintf2(stderr, "StartInterface %s\n", $2);
  _contextStack.push(_context);
  _context = _interface = new Interface($2, _context);
  _interfaces.add(&_interface);

  /* Add preceding comments */
  IdlParser::addComments(_interface, &_comments);
};


StartInterface: InterfaceTag Name ':' Name '{'
{
  dprintf2(stderr, "StartInterface %s : %s\n", $2, $4);

  _contextStack.push(_context);

  // Find parent interface
  Boolean found = False;       
  for (int i = 0; i < _interfaces.size(); i++) {
    Interface *parent;
    _interfaces.get(i, &parent);
    if (!strcmp(parent->name(), $4)) {
      found = True;
      _context = _interface = new Interface($2, _context, parent);
      _interface->referencedInterfaces.add(&parent);
      _interfaces.add(&_interface);
      break;
    }
  }
  if (!found) {
    char errorBuf[256];
    sprintf(errorBuf, "Specified parent interface \"%s\" not found", $4);
    yyerror(errorBuf);
    return errorReturn;
  }
};


InterfaceMembers: InterfaceMember
{
  dprintf2(stderr, "InterfaceMembers\n");
};


InterfaceMembers: InterfaceMembers InterfaceMember
{
  dprintf2(stderr, "InterfaceMembers\n");
};


InterfaceMember: Method {}
        | ContextMember {}
{
  dprintf2(stderr, "InterfaceMember\n");
};


ContextMember: Enumeration
{
  dprintf2(stderr, "enumeration Member\n");
};


ContextMember: Struct
{
  dprintf2(stderr, "struct Member\n");
};


ContextMember: CommentString 
{
  dprintf2(stderr, "Got comment: %s\n", $1);
  Comment *comment = new Comment($1);
  _comments.add(&comment);
};


ContextMember: Sequence
{
  dprintf2(stderr, "sequence Member\n");
};


Method: StartMethod ArgList ')' ';'
{
  /* Method with at least one argument */

  /* Add arguments */
  Method::Argument *argument;
  for (int i = 0; i < _arguments.size(); i++) {
    _arguments.get(i, &argument);
    _method->arguments.add(&argument);
  }

  _arguments.clear();
  
};


Method: StartMethod ')' ';'
{
  /* Method with no arguments */
};


StartMethod: Type Name '('
{
  dprintf(stderr, "StartMethod %s, return type = %d\n",
	  $2, _dataType);

  _method = new Method($2, new Primitive(_dataType, _context), 
		       IdlParser::overloadCount(_interface, $2));

  _interface->methods.add(&_method);

  /* Add preceding comments */
  IdlParser::addComments(_method, &_comments);
};


StartMethod: Name Name '('
{
  char *definedTypeName = $1;
  char *methodName = $2;

  DataDefinition *definedType;
  char errorBuf[256];

  if ((definedType = IdlParser::findDefinition(definedTypeName, _interface)) 
      == 0) {
    sprintf(errorBuf, "Type \"%s\" not defined", definedTypeName);
    yyerror(errorBuf);
    return errorReturn;
  }
  
  if (aggregateType(definedType->dataType())) {
    sprintf(errorBuf, "Method can't return aggregate type \"%s\"", 
	    definedTypeName);

    yyerror(errorBuf);
    return errorReturn;
  }

  dprintf(stderr, "StartMethod: returns type \"%s::%s\"\n",
	  definedType->context->name(), definedType->name());

  _method = new Method(methodName, definedType, 
		      IdlParser::overloadCount(_interface, methodName));

  _interface->methods.add(&_method);

  /* Add preceding comments */
  IdlParser::addComments(_method, &_comments);
}


StartMethod: DefinedType Name '('
{
  char *methodName = $2;

  if (aggregateType(_definedType->dataType())) {
    char errorBuf[256];
    sprintf(errorBuf, "Method can't return aggregate type \"%s\"", 
	    _definedType->name());

    yyerror(errorBuf);
    return errorReturn;
  }

  _method = new Method(methodName, _definedType, 
		      IdlParser::overloadCount(_interface, methodName));

  _interface->methods.add(&_method);

  /* Add preceding comments */
  IdlParser::addComments(_method, &_comments);
};


ArgList: Argument
{
  dprintf2(stderr, "ArgList\n");
};


ArgList: ArgList ',' Argument
{
  dprintf2(stderr, "ArgList\n");
};


Argument: IoSpecifier Type Name 
{
  /* Argument, of "primitive" type */
  dprintf2(stderr, "Argument: Name = %s\n", $3);

  /* Create Argument object and store it until method definition parsed */
  Primitive *primitive = new Primitive(_dataType, _context);

  Method::Argument *argument = 
    new Method::Argument($3, primitive, _ioSpecifier, _context);

  _arguments.add(&argument);
};


Argument: IoSpecifier Name Name 
{
  /* Argument, of "structure" or "enum" type */
  dprintf2(stderr, "Argument: Name = %s\n", $3);

  char *definedTypeName = $2;

  DataDefinition *definedType;

  if ((definedType = IdlParser::findDefinition(definedTypeName, _interface)) 
      == 0) {
    char errorBuf[256];
    sprintf(errorBuf, "Type \"%s\" not defined", definedTypeName);
    yyerror(errorBuf);
    return errorReturn;
  }

  Method::Argument *argument = 
    new Method::Argument($3, definedType, _ioSpecifier, _context);

  _arguments.add(&argument);
};



Argument: IoSpecifier DefinedType Name 
{
  /* Argument, of "structure" or "enum" type */
  dprintf2(stderr, "Argument: Name = %s\n", $3);

  Method::Argument *argument = 
    new Method::Argument($3, _definedType, _ioSpecifier, _context);

  _arguments.add(&argument);
};



IoSpecifier: InTag      { _ioSpecifier = Method::In; }
             | OutTag   { _ioSpecifier = Method::Out; }
             | InOutTag { _ioSpecifier = Method::InOut; }



Struct: StartStruct StructMembers '}' ';'
{
  _context = _contextStack.pop();
};


StartStruct: StructTag Name '{'
{
  _contextStack.push(_context);
  _structure = new Structure($2, _context);
  _context->structures.add(&_structure);
  _context = _structure;

  /* Add preceding comments */
  IdlParser::addComments(_structure, &_comments);
};


StructMembers: StructMembers StructMember
{
  dprintf2(stderr, "StructMembers\n");
};


StructMembers: StructMember
{
  dprintf2(stderr, "StructMembers\n");
};


StructMember: DataInstance {}
           | ContextMember {}
{
};


DataInstance: Type Name ';'
{
  /* Element of "primitive" data type */

  dprintf2(stderr, "DataInstance %s\n", $2);
  DataDefinition *definition = new DataDefinition($2, _dataType, _structure);
  DataInstance *instance = new DataInstance($2, definition, _structure);

  _structure->instances.add(&instance);

  /* Add preceding comments */
  IdlParser::addComments(instance, &_comments);
};


DataInstance: Name Name ';'
{
  DataDefinition *definition = 0;
  const char *dataDefName = $1;

  // Search enclosing contexts for data definition
  Context *context = _context;
  while (context) {

    dprintf(stderr, "Checking context \"%s\" for data type \"%s\"\n",
	    context->name(), dataDefName);

    if ((definition = IdlParser::findDefinition(dataDefName, context)) != 0) {
      break;
    }
    context = context->context;
  }

  if (!definition) {
    // Couldn't find data definition
    char errorBuf[100];
    sprintf(errorBuf, "Type \"%s\" not defined.", dataDefName);
    yyerror(errorBuf);
    return errorReturn;
  }

  DataInstance *instance = new DataInstance($2, definition, _structure);

  /* Add preceding comments */
  IdlParser::addComments(instance, &_comments);

  _structure->instances.add(&instance);
};


DataInstance: DefinedType Name ';'
{
  DataInstance *instance = new DataInstance($2, _definedType, _structure);

  /* Add preceding comments */
  IdlParser::addComments(instance, &_comments);

  _structure->instances.add(&instance);
};


Sequence: TypedefTag SequenceTag StartBoundTag Type ',' Integer EndBoundTag Name ';'
{
  /* Sequence of primitive type */
  Primitive *primitive = new Primitive(_dataType, _context);
  Sequence *sequence = new Sequence($8, primitive, $6, _context);
  _context->sequences.add(&sequence);
};



Sequence: TypedefTag SequenceTag StartBoundTag Name ',' Integer EndBoundTag Name ';'
{
  /* Sequence of compound type */
  DataDefinition *definition;
  if ((definition = IdlParser::findDefinition($4, _context)) 
      == 0) {
    char errorBuf[100];
    sprintf(errorBuf, "Type \"%s\" not defined.", $4);
    yyerror(errorBuf);
    return errorReturn;
  }

  Sequence *sequence = new Sequence($8, definition, $6, _context);

  _context->sequences.add(&sequence);
}


Sequence: TypedefTag SequenceTag StartBoundTag DefinedType ',' Integer EndBoundTag Name ';'
{
  /* Sequence of compound type */
  Sequence *sequence = new Sequence($8, _definedType, $6, _context);

  _context->sequences.add(&sequence);
}


Enumeration: StartEnumeration EnumerationList '}' ';'
{
  dprintf2(stderr, "Enumeration\n");
}


StartEnumeration: EnumerationTag Name '{'
{
  dprintf2(stderr, "StartEnumeration %s in context %s\n", $2, _context->name());
  _enumeration = new Enumeration($2, _context);
  _interface->enumerations.add(&_enumeration);

  /* Add preceding comments */
  IdlParser::addComments(_enumeration, &_comments);
}


EnumerationList: EnumeratedValue
{
};


EnumerationList: EnumerationList ',' EnumeratedValue
{
};


EnumeratedValue: Name '=' Integer
{
  /* Enumeration member with explicit value */
  _enumeration->addValue($1, false, $3);
};


EnumeratedValue: Name
{
  /* Enumeration member with implicit value */
  _enumeration->addValue($1);
};


DefinedType: ContextPath Name
{
  char definedTypeName[256];

  sprintf(definedTypeName, "%s%s", 
	  IdlParser::buildContextPath(&_contextNames), $2);

  _contextNames.clear();
	  
  if ((_definedType = IdlParser::findDefinition(definedTypeName, _context,
						&_interfaces)) == 0) {
    char errorBuf[256];
    sprintf(errorBuf, "Type \"%s\" not defined", definedTypeName);
    yyerror(errorBuf);
    return errorReturn;
  }

  dprintf(stderr, "Found DefinedType: %s\n", definedTypeName);

  // Add defining interface for this type to this interface's 
  // referenced interface list
  Interface *definingInterface;
  bool found = false;
  if ((definingInterface = _definedType->definingInterface()) != _interface) {
    // Is it already in list?
    for (int i = 0; i < _interface->referencedInterfaces.size(); i++) {
      Interface *interface;
      _interface->referencedInterfaces.get(i, &interface);
      if (interface == definingInterface) {
	// Already in list
	found = true;
	break;
      }
    }
    
    if (!found) {
      // Defining interface not yet in list; add it
      _interface->referencedInterfaces.add(&definingInterface);
    }
  }

}


ContextPath: ContextName 
           | ContextPath ContextName ;


ContextName: Name ContextDelimiterTag
{
  char *name = strdup($1);
  _contextNames.add(&name);
}



Type: FloatTag           { _dataType = FloatType; }
      | DoubleTag        { _dataType = DoubleType; }
      | LongTag          { _dataType = LongType; }
      | ShortTag         { _dataType = ShortType; }
      | BooleanTag       { _dataType = BooleanType; }
      | CharTag          { _dataType = CharType; }
      | UnsignedCharTag  { _dataType = UnsignedCharType; }
      | UnsignedShortTag { _dataType = UnsignedShortType; }
      | UnsignedLongTag  { _dataType = UnsignedLongType; }
      | VoidTag          { _dataType = VoidType; }



%%

const char *IdlParser::buildContextPath(DynamicArray<char *> *names)
{
  static char contextPath[256];

  char *name;
  char *pathPtr = contextPath;

  for (int i = 0; i < names->size(); i++) {
    names->get(i, &name);
    sprintf(pathPtr, "%s::", name);
    pathPtr += strlen(pathPtr);
  }

  return contextPath;
}


void IdlParser::addComments(Base *base, DynamicArray<Comment *> *comments)
{
  /* Add preceding comments to base object */
  Comment *comment;
  for (int i = 0; i < comments->size(); i++) {
    comments->get(i, &comment);
    base->comments.add(&comment);
  }

  _comments.clear();
}

int IdlParser::overloadCount(Interface *interface, const char *methodName) 
{
  /* Determine if method is overloaded; if so, need to keep count */
  int count = 0;

  for (int i = 0; i < interface->methods.size(); i++) {
    Method *method;
    interface->methods.get(i, &method);
    if (!strcmp(method->name(), methodName)) {
      /* Method is overloaded */
      count++;
    }
  }
  return count;
}


extern char *yytext;

int yyerror(char *errorMsg)
{
  sprintf(parseErrorBuf, 
	  "Line %d: %s\noffending text: \"%s\"", 
	  idlFileLineNo(), errorMsg, yytext);
  
  return 0;
}


Interface *IdlParser::nextInterface(bool *error)
{
  int ret;
  extern FILE *yyin;
  extern char parseErrorBuf[];
  bool debug = true;

  dprintf(stderr, "***** IdlParser::nextInterface() *****\n");

  _interfaces = this->_interfaces;
  yyin = _idlFile;
  
  if ((ret = yyparse()) != okReturn)
  {
    /* Error or end-of-file */
    if (ret == errorReturn) {
      fprintf(stderr, "IdlParser::nextInterface() - error from yyparse():\n");
      fprintf(stderr, "IdlParser::nextInterface() - %s\n", parseErrorBuf);
      *error = true;
    }
    return NULL;
  }
  
  return _interface;
}



DataDefinition *IdlParser::findDefinition(const char *name, 
                                          Context *referringContext, 
					  DynamicArray<Interface *> *interfaces)
{
  Structure *structure;
  Enumeration *enumeration;
  Sequence *sequence;
  DataDefinition *definition;
  bool debug = true;

  Context *context = referringContext;

  bool found = false;
  char *endPtr;
  char *namePtr = (char *)name;

  if ((endPtr = strstr(namePtr, "::")) == 0) {
    // No context specifiers in type name - look in current context and
    // its parents

    while (context != 0) {

      if ((definition = findDefinition(name, context)) != 0)
	return definition;

      context = context->parentClass;
    }

    // Couldn't find defintion
    return 0;
  }
  else {

    // Fully-qualified type name was specified; find it
    dprintf(stderr, "findDef() - look for \"%s\"\n", name);

    char contextName[256];
    strncpy(contextName, namePtr, endPtr - namePtr);
    contextName[endPtr - namePtr] = '\0';
    namePtr = endPtr + strlen("::");

    // Find interface
    dprintf(stderr, "findDef() - look for interface \"%s\"\n", contextName);
    for (int i = 0; i < interfaces->size(); i++) {
      Interface *interface;
      interfaces->get(i, &interface);
      dprintf(stderr, "findDef() - interface #%d, name: %s\n", 
	      i, interface->name());
 
      if (!strcmp(contextName, interface->name())) {
	dprintf(stderr, "findDef() - found interface\n");
	found = True;
	context = interface;
	break;
      }
    }

    if (!found) {
      dprintf(stderr, "findDef() - couldn't find interface \"%s\"!\n",
	      contextName);

      return 0;
    }

    bool parsing = true;

    while (true) {

      if ((endPtr = strstr(namePtr, "::")) != 0) {

	strncpy(contextName, namePtr, endPtr - namePtr);
	contextName[endPtr-namePtr] = '\0';
	namePtr = endPtr + strlen("::");
      }
      else {
	parsing = false;
	strcpy(contextName, namePtr);
      }

      if ((definition = findDefinition(contextName, context)) == 0) {
	return 0;
      }

      if (!parsing) {
	return definition;
      }

      context = (Context *)definition;
    }

    /* Couldn't find it */
    return 0;  
  }
}



DataDefinition *IdlParser::findDefinition(const char *name, 
					  Context *startingContext)
{
  // Search starting context and ancestors for named data type
  for (Context *context = startingContext; context != 0; 
       context = context->parentClass) {
    int i;
    Structure *structure;
    Enumeration *enumeration;
    Sequence *sequence;

    /* Check structures */
    for (i = 0; i < context->structures.size(); i++) {
      context->structures.get(i, &structure);
      if (!strcmp(name, structure->name())) {
	return structure;
      }
    }

    // Check enumerations
    for (i = 0; i < context->enumerations.size(); i++) {
      context->enumerations.get(i, &enumeration);
      if (!strcmp(name, enumeration->name())) {
	return enumeration;
      }
    }

    // Check sequences
    for (i = 0; i < context->sequences.size(); i++) {
      context->sequences.get(i, &sequence);
      if (!strcmp(name, sequence->name())) {
	return sequence;
      }
    }
  }

  // Couldn't find it
  return 0;
}



const char *IdlParser::errorMsg()
{
  return (const char *)parseErrorBuf;
}


extern FILE *yyin;

#ifdef TESTING
main(int argc, char **argv)
{
  FILE *fp;

  if (argc != 2)
  {
    fprintf(stderr, "Usage: %s <inputfile>\n", argv[0]);
    exit(1);
  }
  char *inputFile = argv[1];  

  if ((fp = fopen(inputFile, "r")) == NULL)
  {
    fprintf(stderr, "Couldn't open file \"%s\"\n", inputFile);
    exit(1);
  }
  
  yyin = fp;

  int ret;

  while (1)
  {
    if ((ret = yyparse()) != okReturn)
      break;
  }
  
  if (ret == errorReturn)
  {
    fprintf(stderr, "Error from yyparse():\n%s\n", parseErrorBuf);
  }
  else if (ret == eofReturn)
    printf("All done parsing file %s\n", inputFile);

  
  exit(0);
}

#endif

