/* $Id: pattypes.c,v 1.1.1.1 2008/10/28 13:28:21 kiesling Exp $ */

/*
  This file is part of ctalk.
  Copyright © 2005-2008  Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation, 
  Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "lex.h"
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "typeof.h"
#include "rtinfo.h"

/*
 *  Complete expression is within arrayopen.. arrayclose pair.
 */
int is_simple_subscript (MESSAGE_STACK messages, int expr_start_idx,
			 int stack_start_idx, int stack_end_idx) {
  int prev_tok_idx, next_tok_idx, expr_end_idx;
  char *exprbuf;
  
  if ((prev_tok_idx = __ctalkPrevLangMsg (messages, expr_start_idx, 
				      stack_start_idx)) == ERROR)
    return FALSE;

  if (M_TOK(messages[prev_tok_idx]) != ARRAYOPEN) return FALSE;

  exprbuf = collect_expression (messages, expr_start_idx,
				&expr_end_idx, stack_end_idx);
  free (exprbuf);
  
  if ((next_tok_idx = __ctalkNextLangMsg (messages, expr_end_idx, 
				      stack_end_idx)) == ERROR)
    return FALSE;

  if (M_TOK(messages[next_tok_idx]) != ARRAYCLOSE) return FALSE;

  return TRUE;
}

int is_char_ptr_fmt_arg (MESSAGE_STACK messages, int expr_start_idx,
			 int stack_start_idx, int stack_end_idx) {
  int i, n_th_arg;
  int paren_lvl, block_lvl, array_lvl, start_paren_lvl, start_array_lvl;
  MESSAGE *m;
  int fmt_str_length;
  char *fmt_char_ptr;
  enum {
    ptr_fmt_arg_null,
    ptr_fmt_arg_expr,
    ptr_fmt_arg_separator,
    ptr_fmt_arg_literal
  } state;

  paren_lvl = block_lvl = array_lvl = n_th_arg = 0;
  start_paren_lvl = start_array_lvl = -1;
  for (i = expr_start_idx, state = ptr_fmt_arg_null; i <= stack_start_idx; i++) {

    m = messages[i];
    if (M_ISSPACE(m)) continue;

    switch (M_TOK(m))
      {
      case LABEL:
	if ((state == ptr_fmt_arg_null) || (state == ptr_fmt_arg_separator))
	  state = ptr_fmt_arg_expr;
	break; 
      case ARGSEPARATOR:
	if (start_paren_lvl != -1) {
	  if ((paren_lvl == start_paren_lvl) && !block_lvl && !array_lvl) {
	    state = ptr_fmt_arg_separator;
	    --n_th_arg;
	  }
	} else {
	  if (!paren_lvl && !block_lvl && !array_lvl) {
	    state = ptr_fmt_arg_separator;
	    --n_th_arg;
	  }
	}
	break;
      case LITERAL:
 	if (state == ptr_fmt_arg_separator) {
 	  if ((fmt_str_length = is_printf_fmt (M_NAME(m))) != 0) {
	    int __n;
	    /*
	     *  n_th_arg is -1 for the first argument, -2 for the 
	     *  second, -3 for the third, ....
	     */
  	    if ((n_th_arg == -1) && is_char_ptr_fmt(M_NAME(m))) {
  	      return TRUE;
 	    } else {
	      fmt_char_ptr = index (M_NAME(m), '%');
 	      for (__n = -2; __n >= n_th_arg; __n--) {
		if ((fmt_char_ptr = index (fmt_char_ptr + 1, '%')) == NULL)
		  return FALSE;
		if (is_char_ptr_fmt (fmt_char_ptr) && (__n == n_th_arg)) {
		  return TRUE;
		} else {
		  if (__n == n_th_arg) {
		    return FALSE;
		  }
		}
	      }
	    }
	  }
 	} else {
	  state = ptr_fmt_arg_expr;
	}
	break;
      case OPENPAREN:
 	++paren_lvl;
 	if (state == ptr_fmt_arg_null) {
 	  start_paren_lvl = paren_lvl;
	  state = ptr_fmt_arg_expr;
	} else {
	  if (start_paren_lvl != -1) {
	    if ((paren_lvl < start_paren_lvl) && (state == ptr_fmt_arg_expr)) {
	      return FALSE;
	    }
	  } else {
	    int __prev_idx;
	    if ((__prev_idx = __ctalkPrevLangMsg (messages, i, stack_start_idx)) 
		!= ERROR) {
	      /*
	       *  Function call syntax.
	       */
	      if (M_TOK(messages[__prev_idx]) == LABEL)
		return FALSE;
	    }
	  }
	}
	break;
      case CLOSEPAREN:
 	--paren_lvl;
	if (state == ptr_fmt_arg_separator)
	  state = ptr_fmt_arg_expr;
	break;
      case OPENBLOCK:
 	++block_lvl;
	break;
      case CLOSEBLOCK:
 	--block_lvl;
	break;
      case ARRAYOPEN:
 	++array_lvl;
 	if (state == ptr_fmt_arg_null)
 	  start_array_lvl = array_lvl;
	break;
      case ARRAYCLOSE:
 	--array_lvl;
	break;
      default:
	break;
      } /* switch (M_TOK(m)) */
  }
  return TRUE;
}

int is_char_ptr_fmt (char *s) {
  int i;
  char buf[MAXMSG];
  enum {
    char_ptr_fmt_arg_null,
    char_ptr_fmt_arg_percent,
    char_ptr_fmt_arg_precision,
    char_ptr_fmt_arg_length_mod,
    char_ptr_fmt_fmt_char
  } state;

  strcpy (buf, s);
  if (*buf == '"')
    TRIM_LITERAL(buf);

  for (i = 0, state = char_ptr_fmt_arg_null; s[i]; i++) {

    switch (buf[i])
      {
      case '%':
	if (state == char_ptr_fmt_arg_null)
	  state = char_ptr_fmt_arg_percent;
	else
	  return FALSE;
	break;
      case '.':
	if (state == char_ptr_fmt_arg_percent)
	  state = char_ptr_fmt_arg_precision;
	else
	  return FALSE;
	break;
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
	if (state != char_ptr_fmt_arg_precision)
	  return FALSE;
	break;
      case 'l':
	if ((state == char_ptr_fmt_arg_percent) ||
	    (state == char_ptr_fmt_arg_precision))
	  state = char_ptr_fmt_arg_length_mod;
	else
	  return FALSE;
	break;
      case 's':
      case 'S':
	if ((state == char_ptr_fmt_arg_percent) ||
	    (state == char_ptr_fmt_arg_precision) ||
	    (state == char_ptr_fmt_arg_length_mod))
	  return TRUE;
	else
	  return FALSE;
	break;
      }
  }
  return FALSE;
}

int is_printf_fmt_msg (MESSAGE_STACK messages, int fmt_tok_idx,
		       int stack_start_idx, int stack_end_idx) {
  return is_printf_fmt (M_NAME(messages[fmt_tok_idx]));
}

int is_function_call (MESSAGE_STACK messages, int fn_label_idx,
		      int stack_start_idx, int stack_end_idx) {
  int open_paren_idx, close_paren_idx;
  if ((open_paren_idx = __ctalkNextLangMsg (messages, fn_label_idx,
					    stack_end_idx)) == ERROR)
    return FALSE;
  if (M_TOK(messages[open_paren_idx]) != OPENPAREN)
    return FALSE;
  if ((close_paren_idx = __ctalkMatchParen (messages, 
					    open_paren_idx,
					    stack_end_idx)) == ERROR)
    return FALSE;
  return TRUE;
}
