/* parse.c
 *
 * Written by Matt Levy
 * Modified 20 Aug 1999 by Neil Moore <neil@cs.uky.edu>
 * Modified 15 Jun 1999 by Neil Moore <neil@cs.uky.edu>
 *   Fixed a buffer overflow in ParseDefaults/readFormula.
 *
 * Part of lderes.  See COPYRIGHT for copyright information.
 *
 */

#ifndef _PARSE_C
#define _PARSE_C	1

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include "parse.h"
#include "global.h"
#include "token.h"
#include "hash.h"

int currentID = 1;
void ParseAssertions(FILE *knowFile, int *tokenString, int *formulaCnt, 
					 int *assertionCnt, int *pointer);
void ParseDefaults(FILE *defFile, int *tokenString, int *formulaCnt, 
					 int *defaultCnt, int *pointer);
short getNextToken(FILE *file, int *varId);
int *ParseTheory(FILE *knowFile, FILE *defFile, long size, int *formulaCnt,
    int *defaultCnt, int *assertionCnt)
{
	int *tokenString = (int *) calloc(size, int);
	int pointer = 0;
	
	*formulaCnt = *defaultCnt = *assertionCnt = 0;
	ParseAssertions(knowFile, tokenString, formulaCnt, assertionCnt, &pointer);
	ParseDefaults(defFile, tokenString, formulaCnt, defaultCnt, &pointer);
	return tokenString;
}

void skipCommentAndWhitespace(FILE *file)
{
  char currentChar;

  currentChar = getc(file);
  while (!feof(file) && IS_SKIPPABLE(currentChar)){
     if (IS_COMMENT(currentChar)){ /* Need to skip whole line */
	currentChar = getc(file);
	while (!feof(file) && currentChar != '\n')
	  currentChar = getc(file);
     }
     currentChar = getc(file);
  }
  if (!feof(file))
     ungetc(currentChar, file);
}

int readFormula(FILE *file, int *tokenString, int *pointer, short endToken,
    short altEndToken)
{
  short nextToken;
  int varId;

  nextToken = getNextToken(file, &varId);
  tokenString[(*pointer)++] = nextToken;
  if (nextToken == VAR)
     tokenString[(*pointer)++] = varId;
  while (!feof(file) && (nextToken != endToken) && (nextToken != altEndToken) && (nextToken != 0)){
     nextToken = getNextToken(file, &varId);
     tokenString[(*pointer)++] = nextToken;
     if (nextToken == VAR)
	tokenString[(*pointer)++] = varId;
  }
  if (nextToken == endToken)
     return 1;
  if ((nextToken == altEndToken) || (nextToken == 0))
     return -1;
  return 0;
}

void ParseAssertions(FILE *knowFile, int *tokenString, int *formulaCnt, 
					int *assertionCnt, int *pointer)
{
  int oldPointer;
  
  skipCommentAndWhitespace(knowFile);
  while (!feof(knowFile)){
     oldPointer = *pointer;
     readFormula(knowFile, tokenString, pointer, SEMI_COLON, NULL_TOKEN);
     if (*pointer - oldPointer > 1){
	(*formulaCnt)++;
	(*assertionCnt)++;
     }
  }
}

void ParseDefaults(FILE *defFile, int *tokenString, int *formulaCnt, 
				 int *defaultCnt, int *pointer)
{
  int oldPointer;
  
  skipCommentAndWhitespace(defFile);
  while (!feof(defFile)){
     oldPointer = *pointer;
     readFormula(defFile, tokenString, pointer, COLON, NULL_TOKEN);
     if (*pointer - oldPointer > 1)
	(*formulaCnt)++;
     oldPointer = *pointer;
     while (readFormula(defFile, tokenString, pointer, COMMA, ENTAIL) > 0){
	if (*pointer - oldPointer > 1)
	  (*formulaCnt)++;
	oldPointer = *pointer;
     }
     if (*pointer - oldPointer > 1)
	(*formulaCnt)++;
     oldPointer = *pointer;
     readFormula(defFile, tokenString, pointer, SEMI_COLON, NULL_TOKEN);
     if (*pointer - oldPointer > 1)
	(*formulaCnt)++;
     (*defaultCnt)++;
  }
}

#define isillegalidchar(c) (iscntrl(c) || c == '&' || c == '|' || \
     c == '#' || c == '%' || c == '!' || c == '=' || c == '>' || \
     c == '<' || c == '{' || c == '}' || c == ':' || c == '.' || \
     c == ',' || c == ')' || c == ';' || c == '-')

int readIdentifier(FILE *file)
{
  char c, *name;
  int currentChar = 0, location;
  
  name = malloc(MAX_IDENTIFIER_LENGTH+1);
  c = getc(file);
  /* First read identifier */
  while (!feof(file) && !isillegalidchar(c) && c != '(' && !isspace(c)) {
    if (currentChar < MAX_IDENTIFIER_LENGTH)
      name[currentChar++] = c;
    c = getc(file);
  }
  if (currentChar > 0)
     ungetc(c, file);
  skipCommentAndWhitespace(file);
  c = getc(file);
  /* Now read arguments */
  if (c == '('){
     if (currentChar < MAX_IDENTIFIER_LENGTH)
	name[currentChar++] = c;
     c = getc(file);
     while (!feof(file) && c != ')'){
	skipCommentAndWhitespace(file);
	if (currentChar < MAX_IDENTIFIER_LENGTH)
	  name[currentChar++] = c;
	c = getc(file);
     }
     if (currentChar < MAX_IDENTIFIER_LENGTH)
	name[currentChar++] = c;
     c = getc(file);
  }
  ungetc(c, file);
  name[currentChar] = '\0';
  if ((location = lookupLocation(name)) >= 0)
     return location;
  /* Not in hash table, so must add */
  location = currentID++;
  if (insert(name, location) < 0)
     return -1;
  return location;
}

short getNextToken(FILE *file, int *varId)
{
  char currentChar;
  short returnValue;
  
  currentChar = getc(file);
  switch(currentChar) {
  case '-':
    currentChar = getc(file);
    if (currentChar != '>')  /* Illegal, not an 'entails' character */
      fatal_error("Illegal character in input theory");
    returnValue = ENTAIL;
    break;
  case ',':
    returnValue = COMMA;
    break;
  case ';':
    returnValue = SEMI_COLON;
    break;
  case '&':
    currentChar = getc(file);
    if (currentChar != '&')  /* Illegal, not an '&' character */
      fatal_error("Illegal character in input theory");
    returnValue = AND_SYMB;
    break;
  case '|':
    currentChar = getc(file);
    if (currentChar != '|')  /* Illegal, not an '|' character */
      fatal_error("Illegal character in input theory");
    returnValue = OR_SYMB;
    break;
  case '!':
    returnValue = NOT_SYMB;
    break;
  case ':':
    returnValue = COLON;
    break;
  default:
    ungetc(currentChar, file);
    if ((*varId = readIdentifier(file)) < 0)
	fatal_error("Hash table overflow");
    returnValue = VAR;
  }
  skipCommentAndWhitespace(file);
  return returnValue;
}
#endif /* _PARSE_C */
