/* # skkinput (Simple Kana-Kanji Input)
 * lispparse.c --- Parse lisp code
 * This file is part of skkinput.
 * Copyright (C) 1997
 * Takashi SAKAMOTO (sakamoto@yajima.kuis.kyoto-u.ac.jp)
 *
 * This program 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.
 * 
 * This program 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 skkinput; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "commondef.h"
#include "lispparse.h"

static void print_conslist_sub( struct conslist *node ) ;

/*
 * 空白文字列を読み飛ばす関数。
 */
void skip_space( struct myChar **string )
{
  struct myChar *ptr ;
  if( string == NULL )
    return ;
  ptr = *string ;
  while( !IS_END_OF_STRING( *ptr ) ){
    if( !IS_ASCII_EQUAL( *ptr, '\t' ) &&
	!IS_ASCII_EQUAL( *ptr, ' ' ) &&
	!IS_ASCII_EQUAL( *ptr, '\n' ) &&
	!IS_ASCII_EQUAL( *ptr, '\r' ) )
      break ;
    ptr ++ ;
  }
  *string = ptr ;
  return ;
}

/*
 * 文字列を読み飛ばす関数。
 */
static void skip_string( struct myChar **string )
{
  int double_quote ;
  struct myChar *ptr ;

  if( string == NULL )
    return ;

  ptr = *string ;
  double_quote = False ;
  while( !IS_END_OF_STRING( *ptr ) ){
    if( IS_ASCII_CHARA( *ptr ) ){
      switch( ptr->chara ){
      case 0x22 :
	if( double_quote ){
	  double_quote = False ;
	} else {
	  double_quote = True ;
	}
	break ;
      case '[' :
      case ']' :
      case '(' :
      case ')' :
      case ' ' :
      case '\t' :
	if( double_quote )
	  break ;
	goto exit_skip_string ;
      default :
	break ;
      }
    }
    ptr ++ ;
  }
exit_skip_string:
  *string = ptr ;
  return ;
}

/*
 * 文字列を抜き出す関数。
 */
static struct myChar *cut_string( struct myChar **string )
{
  struct myChar *ptr, *ret ;
  int len ;

  if( string == NULL )
    return NULL ;

  ptr = *string ;
  /* 続くかもしれない文字列を取り除く(至、空白)。*/
  skip_string( &ptr ) ;
  len = ptr - *string ;
  ret = malloc( sizeof( struct myChar ) * ( len + 1 ) ) ;
  myCharStrncpy( ret, *string, len ) ;
  MYCHAR_SET_END_OF_STRING( ret[ len ] ) ;
  *string = ptr ;
  return ret ;
}

/*
 * cons list を解放するのに用いられる関数。( 注意、skkinputlisp_entity 
 * ではない。
 */
void free_conslist( struct conslist *top )
{
  if( top == NULL )
    return ;
  /* 左手を解放する。*/
  if( top->left.type == TYPE_CONSLIST_CONSPAIR ||
      top->left.type == TYPE_CONSLIST_ARRAY ){
    if( top->left.value.next != NULL ){
      free_conslist( top->left.value.next ) ;
    }
    top->left.value.next = NULL ;
  } else {
    if( top->left.value.string != NULL )
      free( top->left.value.string ) ;
    top->left.value.string = NULL ;
  }
  /* 右手を解放する。*/
  if( top->right.type == TYPE_CONSLIST_CONSPAIR ||
      top->right.type == TYPE_CONSLIST_ARRAY ){
    if( top->right.value.next != NULL ){
      free_conslist( top->right.value.next ) ;
    }
    top->right.value.next = NULL ;
  } else {
    if( top->right.value.string != NULL )
      free( top->right.value.string ) ;
    top->right.value.string = NULL ;
  }
  free( top ) ;
  return ;
}

static int string2conslist_sub_conspair
( struct myChar **ptr, struct consnode *node )
{
  node->type = TYPE_CONSLIST_CONSPAIR ;
  if( ( node->value.next = string2conslist( ptr ) ) == NULL ){
    return False ;
  }
  skip_space( ptr ) ;
  return True ;
}

static int string2conslist_sub_string
( struct myChar **ptr, struct consnode *node )
{
  node->type = TYPE_CONSLIST_STRING ;
  node->value.string = cut_string( ptr ) ;
  if( node->value.string == NULL || 
      IS_END_OF_STRING( node->value.string[ 0 ] ) ||
      !myCharCharStrcmp( node->value.string, "nil" ) ){
    if( node->value.string != NULL )
      free( node->value.string ) ;
    node->type       = TYPE_CONSLIST_CONSPAIR ;
    node->value.next = NULL ;
  }
  return True ;
}

static struct conslist *string2conslist_sub_array
( struct myChar **string )
{
  struct conslist *cnode ;
  struct myChar *ptr ;

  /* メモリを確保する。*/
  if( ( cnode = malloc( sizeof( struct conslist ) ) ) == NULL ){
    return NULL ;
  } else {
    cnode->left.type = cnode->right.type = TYPE_CONSLIST_STRING ;
    cnode->left.value.string = cnode->right.value.string = NULL ;
  }
  ptr = *string ;
  skip_space( &ptr ) ;
  if( IS_ASCII_EQUAL( *ptr, '.' ) || IS_ASCII_EQUAL( *ptr, ')' ) ){
#if defined(DEBUG)
    fprintf
      ( stderr, "Invalid read syntax: \") or . in a vector\"\n" ) ;
#endif
    return NULL ;
  } else if( IS_ASCII_EQUAL( *ptr, ']' ) ){
    ptr ++ ;
    free_conslist( cnode ) ;
    *string = ptr ;
    return NULL ;
  }
  if( IS_ASCII_EQUAL( *ptr, '[' ) ){
    ptr ++ ;
    skip_space( &ptr ) ;
    cnode->left.type = TYPE_CONSLIST_ARRAY ;
    if( !IS_ASCII_EQUAL( *ptr, ']' ) && !IS_END_OF_STRING( *ptr ) ){
      cnode->left.value.next = 
	string2conslist_sub_array( &ptr ) ;
      if( cnode->left.value.next == NULL ){
	free_conslist( cnode ) ;
	return NULL ;
      }
    } else {
      cnode->left.value.next = NULL ;
      if( IS_ASCII_EQUAL( *ptr, ']' ) )
	ptr ++ ;
    }
  } else if( IS_ASCII_EQUAL( *ptr, '(' ) ){
    ptr ++ ;
    cnode->left.type = TYPE_CONSLIST_CONSPAIR ;
    cnode->left.value.next = string2conslist( &ptr ) ;
    if( cnode->left.value.next == NULL ){
      free_conslist( cnode ) ;
      return NULL ;
    }
  } else {
    if( !string2conslist_sub_string( &ptr, &cnode->left ) ){
      free_conslist( cnode ) ;
      return NULL ;
    }
  }
  skip_space( &ptr ) ;
  cnode->right.type = TYPE_CONSLIST_ARRAY ;
  if( !IS_ASCII_EQUAL( *ptr, ']' ) && !IS_END_OF_STRING( *ptr ) ){
    cnode->right.value.next =
      string2conslist_sub_array( &ptr ) ;
    if( cnode->right.value.next == NULL ){
      free_conslist( cnode ) ;
      return NULL ;
    }
  } else {
    if( IS_ASCII_EQUAL( *ptr, ']' ) )
      ptr ++ ;
    cnode->right.value.next = NULL ;
  }
  *string = ptr ;
  return cnode ;
}

/*
 * conslist に展開する関数。
 */
struct conslist *string2conslist( struct myChar **string )
{
  struct conslist *node ;
  struct myChar *ptr = *string ;

  /* メモリを確保する。*/
  if( ( node = malloc( sizeof( struct conslist ) ) ) == NULL ){
    return NULL ;
  } else {
    node->left.type = node->right.type = TYPE_CONSLIST_STRING ;
    node->left.value.string = node->right.value.string = NULL ;
  }
  /* 続くかもしれない空白を省く。*/
  skip_space( &ptr ) ;
  if( IS_ASCII_EQUAL( *ptr, '(' ) ){
    ptr ++ ;
    skip_space( &ptr ) ;
    if( !IS_ASCII_EQUAL( *ptr, ')' ) && !IS_END_OF_STRING( *ptr ) ){
      if( !string2conslist_sub_conspair( &ptr, &( node->left ) ) ){
	free_conslist( node ) ;
	return NULL ;
      }
    } else {
      node->left.type = TYPE_CONSLIST_CONSPAIR ;
      node->left.value.next = NULL ;
      if( IS_ASCII_EQUAL( *ptr, ')' ) )
	ptr ++ ;
    }
  } else if( IS_ASCII_EQUAL( *ptr, '[' ) ){
    ptr ++ ;
    skip_space( &ptr ) ;
    node->left.type = TYPE_CONSLIST_ARRAY ;
    if( !IS_ASCII_EQUAL( *ptr, ']' ) && !IS_END_OF_STRING( *ptr ) ){
      node->left.value.next = string2conslist_sub_array( &ptr ) ;
      if( node->left.value.next == NULL ){
	free_conslist( node ) ;
	return NULL ;
      }
    } else {
      node->left.value.next = NULL ;
      if( IS_ASCII_EQUAL( *ptr,']' ) )
	ptr ++ ;
    }
  } else {
    if( !string2conslist_sub_string( &ptr, &( node->left ) ) ){
      free_conslist( node ) ;
      return NULL ;
    }
  }
  skip_space( &ptr ) ;
  if( IS_ASCII_EQUAL( *ptr, '.' ) ){
    /* cons pair の場合。*/
    ptr ++ ;
    skip_space( &ptr ) ;
    if( IS_ASCII_EQUAL( *ptr, '(' ) ){
      ptr ++ ;
      skip_space( &ptr ) ;
      if( !IS_ASCII_EQUAL( *ptr, ')' ) ){
	if( !string2conslist_sub_conspair( &ptr, &( node->right ) ) ){
	  free_conslist( node ) ;
	  return NULL ;
	}
      } else {
	node->right.type = TYPE_CONSLIST_CONSPAIR ;
	node->right.value.next = NULL ;
	if( IS_ASCII_EQUAL( *ptr, ')' ) )
	  ptr ++ ;
      }
    } else if( IS_ASCII_EQUAL( *ptr, ')' ) ){
#if defined(DEBUG)
      fprintf( stderr, "Invalid read syntax: )\n" ) ;
#endif
      free_conslist( node ) ;
      return NULL ;
    } else {
      string2conslist_sub_string( &ptr, &( node->right ) ) ;
    }
    skip_space( &ptr ) ;
    if( IS_ASCII_EQUAL( *ptr, ')' ) ){
      ptr ++ ;
    } else if( !IS_END_OF_STRING( *ptr ) ){
#if defined(DEBUG)
      fprintf
	( stderr,
	  "Invalid read syntax: \". in wrong context\"\n" ) ;
#endif
      free_conslist( node ) ;
      return NULL ;
    }
  } else if( IS_ASCII_EQUAL( *ptr, '[' ) ){
    node->right.type = TYPE_CONSLIST_CONSPAIR ;
    if( ( node->right.value.next = string2conslist( &ptr ) ) == NULL ){
      free_conslist( node ) ;
      return NULL ;
    }
    skip_space( &ptr ) ;
  } else if( IS_ASCII_EQUAL( *ptr, ')' ) ||
	     IS_END_OF_STRING( *ptr ) ){
    /* conspair を閉じる。*/
    node->right.type = TYPE_CONSLIST_CONSPAIR ;
    node->right.value.next = NULL ;
    if( !IS_END_OF_STRING( *ptr ) )
      ptr ++ ;
  } else if( IS_ASCII_EQUAL( *ptr, '(' ) ){
    if( !string2conslist_sub_conspair( &ptr, &( node->right ) ) ){
      free_conslist( node ) ;
      return NULL ;
    }
  } else {
    if( !string2conslist_sub_conspair( &ptr, &( node->right ) ) ){
      free_conslist( node ) ;
      return NULL ;
    }
  }
  /* 文字列読んだ部分まで進めておく。*/
  *string = ptr ;
  return node ;
}

static void print_consnode( struct consnode *node )
{
  if( node == NULL )
    return ;
  if( node->type == TYPE_CONSLIST_STRING ){
#if 0
    printf( "%s", node->value.string ) ;
#else
    printf( "Currently I cannot show node->value.string.\n" ) ;
#endif
  } else if( node->type == TYPE_CONSLIST_CONSPAIR ){
    if( node->value.next == NULL ){
      printf( "nil" ) ;
    } else {
      printf( "(" ) ;
      print_conslist_sub( node->value.next ) ;
      printf( ")" ) ;
    }
  } else if( node->type == TYPE_CONSLIST_ARRAY ){
    if( node->value.next == NULL ){
      printf( "[]" ) ;
    } else {
      printf( "[" ) ;
      print_conslist_sub( node->value.next ) ;
      printf( "]" ) ;
    }
  }
  return ;
}

/*
 * cons list を表示するのに利用される関数。
 */
static void print_conslist_sub( struct conslist *node )
{
  if( node == NULL )
    return ;
  print_consnode( &node->left ) ;
  printf( " . " ) ;
  print_consnode( &node->right ) ;
  return ;
}

void print_conslist( struct conslist *node )
{
  if( node == NULL )
    return ;
  printf( "(" ) ;
  print_conslist_sub( node ) ;
  printf( ")" ) ;
  return ;
}

int count_string_length( struct myChar *string )
{
  struct myChar *tmpptr ;
  int double_quote, next_is_ignore ;
  int length ;

  double_quote = next_is_ignore = False ;
  length = 0 ;

  while( !IS_END_OF_STRING( *string ) ){
    if( IS_ASCII_EQUAL( *string, '\\' ) ){
      next_is_ignore = True ;
      length ++ ;
      string ++ ;
      continue ;
    }
    if( IS_ASCII_EQUAL( *string, 0x22 ) &&
	!next_is_ignore ){
      double_quote = ( double_quote )? False : True ;
    }
    if( IS_ASCII_EQUAL( *string, '(' ) &&
	!double_quote && !next_is_ignore ){
      tmpptr = string ;
      string ++ ;
      skip_space( &string ) ;
      if( IS_ASCII_EQUAL( *string, ')' ) ||
	  IS_END_OF_STRING( *string ) ){
	length += 5 ;
	if( IS_ASCII_EQUAL( *string, ')' ) )
	  string ++ ;
	continue ;
      }
      string = tmpptr ;
    }
    if( IS_ASCII_EQUAL( *string, 0x27 ) &&
	!next_is_ignore && !double_quote ){
      length += 7 ;
    }
    length ++ ;
    string ++ ;
    if( next_is_ignore )
      next_is_ignore = False ;
  }
  return length ;
}

struct myChar *convert_quote( struct myChar *string )
{
  int double_quote, next_is_ignore ;
  int length, nest_level ;
  struct myChar *dest, *dest_top, *tmpptr ;
  unsigned char nest_check[ MAX_NEST_LEVEL ] ;

  double_quote = next_is_ignore = False ;
  skip_space( &string ) ;
  length = count_string_length( string ) ;
  
  if( ( dest = malloc
	( sizeof( struct myChar ) * ( length + 1 ) ) ) == NULL )
    return NULL ;
  
  for( nest_level = 0 ; nest_level < MAX_NEST_LEVEL ; nest_level ++ )
    nest_check[ nest_level ] = False ;
  nest_level = 0 ;

  dest_top = dest ;
  while( !IS_END_OF_STRING( *string ) ){
    if( IS_ASCII_EQUAL( *string, '\\' ) ){
      next_is_ignore = True ;
      *dest ++ = *string ++ ;
      continue ;
    }
    if( IS_ASCII_EQUAL( *string, 0x22 ) && !next_is_ignore ){
      double_quote = ( double_quote )? False : True ;
    }
    if( !next_is_ignore && !double_quote ){
      if( IS_ASCII_CHARA( *string ) ){
	switch( string->chara ){
	case '(' :
	  tmpptr = string ;
	  string ++ ;
	  skip_space( &string ) ;
	  if( IS_ASCII_EQUAL( *string, ')' ) ||
	      IS_END_OF_STRING( *string ) ){
	    myCharCharStrcpy( dest, " nil " ) ;
	    dest += 4 ;
	    if( IS_ASCII_EQUAL( *string, ')' ) )
	      string ++ ;
	    break ;
	  }
	  string = tmpptr ;
	case '[' :
	  nest_level ++ ;
	  if( nest_level > MAX_NEST_LEVEL ){
	    fprintf( stderr, "skkinputlisp: too many ('s\n" ) ;
	    free( dest_top ) ;
	    return NULL ;
	  }
	  break ;
	case ']' :
	case ')' :
	  if( nest_check[ nest_level ] ){
	    MYCHAR_SET_CHARA( *dest, ')' ) ;
	    dest ++ ;
	    nest_check[ nest_level ] = False ;
	  }
	  nest_level -- ;
	  break ;
	case 0x20 :
	case '\t' :
	case '\0' :
	  if( nest_check[ nest_level ] ){
	    MYCHAR_SET_CHARA( *dest, ')' ) ;
	    dest ++ ;
	    nest_check[ nest_level ] = False ;
	  }
	  break ;
	case 0x27 :
	  nest_check[ nest_level ] = True ;
	  myCharCharStrcpy( dest, "(quote " ) ;
	  dest += 7 ;
	  string ++ ;
	  continue ;
	default :
	  break ;
	}
      }
    }
    *dest ++ = *string ++ ;
    if( next_is_ignore )
      next_is_ignore = False ;
  }
  MYCHAR_SET_END_OF_STRING( *dest ) ;
  return dest_top ;
}

/*
 * consnode をコピーする関数。
 */
static int copy_consnode( struct consnode *dest, struct consnode *src )
{
  dest->type = src->type ;
  switch( src->type ){
  case TYPE_CONSLIST_ARRAY :
  case TYPE_CONSLIST_CONSPAIR :
    if( src->value.next == NULL ){
      dest->value.next = NULL ;
    } else {
      dest->value.next = copy_conspair( src->value.next ) ;
      if( dest->value.next == NULL )
	return False ;
    }
    break ;
  case TYPE_CONSLIST_STRING :
    if( src->value.string == NULL ){
      dest->value.string = NULL ;
    } else {
      dest->value.string = malloc
	( sizeof( struct myChar ) * 
	  ( myCharStrlen( src->value.string ) + 1 ) ) ;
      if( dest->value.string == NULL )
	return False ;
      myCharStrcpy( dest->value.string, src->value.string ) ;
    }
    break ;
  default :
    break ;
  }
  return True ;
}

/*
 * conspair のコピーを作って返す関数。
 *-----
 * lambda 式の評価の時にこっそり使う予定。
 */
struct conslist *copy_conspair( struct conslist *top )
{
  struct conslist *newTop ;
  if( top == NULL )
    return NULL ;
  if( ( newTop = malloc( sizeof( struct conslist ) ) ) == NULL )
    return NULL ;
  newTop->left.type = newTop->right.type = TYPE_CONSLIST_STRING ;
  newTop->left.value.string = newTop->right.value.string = NULL ;
  if( !copy_consnode( &newTop->left, &top->left ) ){
    free_conslist( newTop ) ;
    return NULL ;
  }
  if( !copy_consnode( &newTop->right, &top->right ) ){
    free_conslist( newTop ) ;
    return NULL ;
  }
  return newTop ;
}

int delete_comment_string( struct myChar *buffer )
{
  struct myChar *wptr ;
  int double_quote = False, next_is_ignore = False ;
  struct myChar pchara ;

  MYCHAR_SET_END_OF_STRING( pchara ) ;
  wptr = buffer ; 
  while( !IS_END_OF_STRING( *wptr ) ){
    if( !next_is_ignore && !double_quote ){
      if( IS_ASCII_EQUAL( *wptr, '\\' ) ){
	next_is_ignore = True ;
	wptr ++ ;
	continue ;
      }
    }
    if( IS_ASCII_EQUAL( *wptr, 0x22 ) && !next_is_ignore ){
      if( double_quote ){
	double_quote = False ;
      } else {
	double_quote = True ;
      }
      wptr ++ ;
      continue ;
    }
    if( !next_is_ignore && !double_quote && 
	!IS_ASCII_EQUAL( pchara, '?' ) &&
	IS_ASCII_EQUAL( *wptr, ';' ) ){
      MYCHAR_SET_END_OF_STRING( *wptr ) ;
      break ;
    }
    if( next_is_ignore ){
      next_is_ignore = False ;
    }
    pchara = *wptr ++ ;
  }
  return True ;
}
