#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef USE_NLLIBC
#include <nllibc.h>
#endif

#include "config.h"
#include "lib.h"
#include "objlist.h"
#include "type.h"
#include "syntax.h"
#include "symbol.h"

/*****************************************************************
 * symbol_t
 */

symbol_t symbol_destroy(symbol_t symbol)
{
  if (symbol != NULL) {
    if (symbol->name != NULL)
      free(symbol->name);
    if (symbol->model != NULL)
      model_destroy(symbol->model);
    if (symbol->value != NULL)
      value_destroy(symbol->value);

    objentry_set_free_func(&symbol->entry, NULL);
    if (objentry_done(&symbol->entry) != NULL) {
      /* fail to done */
    }
    memset(symbol, 0, sizeof(*symbol));
    free(symbol);
  }

  return NULL;
}

void symbol_free(void *p)
{
  symbol_destroy((symbol_t)p);
}

symbol_t symbol_create(c_type_t type, int line, c_type_t scope,
		       char *name, model_t model, unsigned int flags,
		       c_type_t value_type)
{
  symbol_t symbol;
  char n[32];
  static int id = 0;

  symbol = malloc(sizeof(*symbol));
  if (symbol == NULL)
    goto err;
  memset(symbol, 0, sizeof(*symbol));

  if (objentry_init(&symbol->entry, symbol, 0, symbol_free) == NULL)
    goto err;

  if ((name != NULL) && !strcmp(name, SYMBOL_NAME_AUTO)) {
    sprintf(n, "symbol.%d", id);
    name = n;
  }

  if (name != NULL) {
    symbol->name = strdup(name);
    if (symbol->name == NULL)
      goto err;
  } else {
    symbol->name = NULL;
  }

  symbol->id = id++;
  symbol->type = type;
  symbol->line = line;
  symbol->scope = scope;
  symbol->model = model;
  symbol->flags = flags;

  symbol->location.type = C_TYPE_NONE;
  symbol->location.base.p = NULL;
  symbol->location.offset = -1;

  if (value_type != C_TYPE_NONE) {
    symbol->value = value_create(value_type);
    if (symbol->value == NULL)
      goto err;
  }

  return symbol;

err:
  if (symbol != NULL)
    symbol_destroy(symbol);
  return NULL;
}

static void symbol_print_args(objlist_t args, int indent)
{
  objentry_t entry;
  symbol_t symbol;

  indent_print(indent);
  printf("ARGS\n");

  indent++;
  for (entry = objlist_get_head(args);
       !objlist_is_end(args, entry);
       entry = objentry_get_next(entry)) {
    symbol = objentry_get_obj(entry);
    symbol_print_simple(symbol, indent);
  }
}

static void symbol_print_labels(objlist_t labels, int indent)
{
  objentry_t entry;
  syntax_t syntax;

  indent_print(indent);
  printf("LABELS\n");

  indent++;
  for (entry = objlist_get_head(labels);
       !objlist_is_end(labels, entry);
       entry = objentry_get_next(entry)) {
    syntax = objentry_get_syntax(entry);
    syntax_print(syntax, indent);
  }
}

void symbol_print_name(symbol_t symbol)
{
  printf("%s", (symbol->name != NULL) ? symbol->name : "NONAME");
}

void symbol_print_simple(symbol_t symbol, int indent)
{
  indent_print(indent);
  printf("SYMBOL: ");
  switch (symbol->scope) {
  case C_TYPE_STATIC: printf("static "); break;
  case C_TYPE_EXTERN: printf("extern "); break;
  default: break;
  }
  symbol_print_name(symbol);
  printf(" (%s, id:0x%x, flags:", type_get_word(symbol->type), symbol->id);
  if (symbol->flags & SYMBOL_FLAG_INITFIRST)  printf(" IF");
  if (symbol->flags & SYMBOL_FLAG_READONLY)   printf(" RO");
  if (symbol->flags & SYMBOL_FLAG_LOCATED)    printf(" LC");
  if (symbol->flags & SYMBOL_FLAG_TEMPORARY)  printf(" TP");
  if (symbol->flags & SYMBOL_FLAG_FUNCTION)   printf(" FN");
  if (symbol->flags & SYMBOL_FLAG_FUNCBLOCK)  printf(" FB");
  if (symbol->flags & SYMBOL_FLAG_FUNCVARARG) printf(" FV");
  if (symbol->flags & SYMBOL_FLAG_FUNCNOINIT) printf(" FI");
  if (symbol->flags & SYMBOL_FLAG_BITFIELD)   printf(" BF");
  printf(")\n");
}

void symbol_print_value_simple(symbol_t symbol, int indent)
{
  symbol_print_simple(symbol, indent);

  indent++;
  if (symbol->value != NULL) {
    value_print_args(symbol->value, indent);
  }
}

void symbol_print(symbol_t symbol, int indent)
{
  printf("LINE: %d\n", symbol->line);
  symbol_print_simple(symbol, indent);

  indent++;
  if (symbol->model) {
    model_print(symbol->model, indent);
  }
  if (symbol->location.type != C_TYPE_NONE) {
    indent_print(indent);
    printf("LOCATION: ");
    printf("%s", type_get_word(symbol->location.type));
    switch (symbol->location.type) {
    case C_TYPE_STATIC:
    case C_TYPE_STACK:
    case C_TYPE_STRUCT:
    case C_TYPE_UNION:
      break;
    case C_TYPE_SYMBOL:
      printf(":");
      symbol_print_name(symbol->location.base.symbol);
      break;
    case C_TYPE_POINTER:
      printf(":");
      symbol_print_name(symbol->location.base.symbol);
      break;
    default: printf("(UNKNOWN)");  break;
    }
    printf(", offset:");
    if (symbol->location.offset < 0)
      printf("UNDEF");
    else
      printf("%d", symbol->location.offset);
    printf("\n");
  }
  switch (symbol->type) {
  case C_TYPE_VARIABLE:
    if (symbol->flags & SYMBOL_FLAG_BITFIELD) {
      indent_print(indent);
      printf("BITFIELD: (%d, %d)\n", symbol->param.variable.bitfield.bits,
	     symbol->param.variable.bitfield.shift);
    }
    break;
  case C_TYPE_LABEL:
    if (symbol->param.label.variable != NULL) {
      indent_print(indent);
      printf("VARIABLE: %s (%s)\n", symbol->param.label.variable->name,
	     type_get_word(symbol->param.label.variable->type));
    }
  default:
    break;
  }
  if (symbol->obj.function.args != NULL) {
    symbol_print_args(symbol->obj.function.args, indent);
  }
  if (symbol->obj.function.labels != NULL) {
    indent_print(indent);
    printf("LABEL\n");
    symbol_print_labels(symbol->obj.function.labels, indent + 1);
  }
  if (symbol->obj.function.stack != NULL) {
    indent_print(indent);
    printf("STACK\n");
    area_print_simple(symbol->obj.function.stack, indent + 1);
  }
  if (symbol->obj.function.initlabel != NULL) {
    indent_print(indent);
    printf("INIT LABEL\n");
    syntax_print_label(&symbol->obj.function.initlabel->obj.label, indent + 1);
  }
  if (symbol->obj.function.retlabel != NULL) {
    indent_print(indent);
    printf("RETURN LABEL\n");
    syntax_print_label(&symbol->obj.function.retlabel->obj.label, indent + 1);
  }
  if (symbol->value != NULL) {
    indent_print(indent);
    printf("VALUE\n");
    value_print(symbol->value, indent + 1);
  }
}

/*****************************************************************
 * library
 */

static int is_const_label(symbol_t symbol)
{
  if ((symbol->type == C_TYPE_LABEL) &&
      (symbol->flags & SYMBOL_FLAG_READONLY))
    return 1;
  return 0;
}

static int is_const_variable(symbol_t symbol)
{
  if ((symbol->type == C_TYPE_VARIABLE) &&
      (symbol->flags & SYMBOL_FLAG_READONLY))
    return 1;
  return 0;
}

int symbol_is_const_number(symbol_t symbol)
{
  if (is_const_variable(symbol) &&
      (symbol->value != NULL) &&
      (symbol->value->type == C_TYPE_NUMBER))
    return 1;
  return 0;
}

int symbol_is_const_string(symbol_t symbol)
{
  if (is_const_label(symbol) &&
      (symbol->value != NULL) &&
      (symbol->value->type == C_TYPE_STRING))
    return 1;
  return 0;
}

int symbol_is_writable(symbol_t symbol)
{
  if ((symbol->type != C_TYPE_VARIABLE) || is_const_variable(symbol))
    return 0;
  return 1;
}

/* #define MAKE_VARIABLE_TO_LABEL */
#ifdef MAKE_VARIABLE_TO_LABEL
static symbol_t make_variable(symbol_t symbol, objlist_t symbols,
			      area_t static_area, area_t stack_area)
{
  symbol_t s;

  if (symbol->type == C_TYPE_LABEL) {
    if (symbol->param.label.variable == NULL) {
      s = symbol_create(C_TYPE_VARIABLE, symbol->line, C_TYPE_STATIC,
			SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_LABEL);
      if (s == NULL)
	ERREXIT(1);
      if (symbols)
	objlist_insert_obj_tail(symbols, s, symbol_free);
      if (symbol->model != NULL) {
	s->model = model_copy(symbol->model);
	model_array_to_pointer(s->model);
      }
      s->value->obj.label.symbol = symbol;
      symbol->param.label.variable = s;
    }

#if 0
    symbol = symbol->param.label.variable;
#endif
  }

  return symbol;
}
#endif

symbol_t symbol_locate(symbol_t symbol, objlist_t symbols,
		       area_t static_area, area_t stack_area)
{
  if (!(symbol->flags & SYMBOL_FLAG_LOCATED)) {
    if ((symbol->scope == C_TYPE_STATIC) ||
	(symbol->flags & SYMBOL_FLAG_READONLY)) {
      symbol->flags |= SYMBOL_FLAG_INITFIRST;
      area_insert_symbol(static_area, symbol);
    } else if (symbol->scope != C_TYPE_EXTERN) {
      area_insert_symbol(stack_area, symbol);
    }
  }
#ifdef MAKE_VARIABLE_TO_LABEL
  if ((symbol->scope == C_TYPE_STATIC) || (symbol->scope == C_TYPE_EXTERN))
    symbol = make_variable(symbol, symbols, static_area, stack_area);
#endif
  return symbol;
}

symbol_t symbol_make_cast(symbol_t symbol, model_t model,
			  objlist_t symbols,
			  area_t static_area, area_t stack_area)
{
  symbol_t s;

  if (symbol_is_const_number(symbol)) {
    s = symbol_create(C_TYPE_VARIABLE, symbol->line, C_TYPE_STATIC,
		      SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
    if (s == NULL)
      return NULL;
    if (symbols)
      objlist_insert_obj_tail(symbols, s, symbol_free);
    s->value->obj.number = symbol->value->obj.number;
    s->flags |= SYMBOL_FLAG_READONLY;
    s->model = model_copy(model);
    return s;
  }

  symbol = symbol_locate(symbol, symbols, static_area, stack_area);

  s = symbol_create(C_TYPE_VARIABLE, symbol->line, C_TYPE_NONE,
		    SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_CAST);
  if (s == NULL)
    return NULL;
  if (symbols)
    objlist_insert_obj_tail(symbols, s, symbol_free);
  s->flags |= SYMBOL_FLAG_TEMPORARY;
  s->model = model_copy(model);
  s->value->obj.op.arg[0] = symbol;

  return s;
}

symbol_t symbol_change_model(symbol_t symbol, model_t model,
			     objlist_t symbols,
			     area_t static_area, area_t stack_area)
{
  symbol_t s;

  if (symbol->model == NULL) {
    symbol = symbol_set_model(symbol, model, symbols, static_area, stack_area);
  }

  if (!model_cmp(symbol->model, model))
    return symbol;

  if (model_is_pointer(model) && model_is_address(symbol->model))
    return symbol;

  s = symbol_make_cast(symbol, model, symbols, static_area, stack_area);
  if (s == NULL)
    ERREXIT(1);

  return s;
}

symbol_t symbol_change_model_type(symbol_t symbol, c_type_t type,
				  objlist_t symbols,
				  area_t static_area, area_t stack_area)
{
  model_t model;

  model = model_create(type);
  symbol = symbol_change_model(symbol, model, symbols, static_area, stack_area);
  model_destroy(model);

  return symbol;
}

static symbol_t symbol_sign_extension_type(symbol_t symbol, c_type_t type,
					   objlist_t symbols,
					   area_t static_area, area_t stack_area)
{
  model_t model;

  model = model_create(type);
  if (model_get_level(model) > model_get_level(symbol->model))
    symbol = symbol_change_model(symbol, model, symbols, static_area, stack_area);
  model_destroy(model);

  return symbol;
}

symbol_t symbol_sign_extension(symbol_t symbol, objlist_t symbols,
			       area_t static_area, area_t stack_area)
{
  return symbol_sign_extension_type(symbol, C_TYPE_INT, symbols,
				    static_area, stack_area);
}

symbol_t symbol_list_search(objlist_t list, char *name)
{
  objentry_t entry;
  symbol_t symbol;

  if (name == NULL)
    return NULL;

  for (entry = objlist_get_head(list);
       !objlist_is_end(list, entry);
       entry = objentry_get_next(entry)) {
    symbol = objentry_get_obj(entry);
    if ((symbol->name != NULL) && !strcmp(name, symbol->name))
      return symbol;
  }

  return NULL;
}

symbol_t symbol_set_model(symbol_t symbol, model_t model, objlist_t symbols,
			  area_t static_area, area_t stack_area)
{
  objentry_t entry, sentry = NULL, nentry;
  symbol_t s, ssymbol;
  objlist_t symbol_list = NULL;
  int is_struct = 0;

  symbol->model = model_copy(model);

  if ((symbol->value != NULL) && (symbol->value->type == C_TYPE_ARRAY)) {
    if ((model->type == C_TYPE_STRUCT) && (model->modifiers.number == 0)) {
      is_struct = 1;
      symbol_list = model->custom._struct->members->symbols;
      sentry = objlist_get_head(symbol_list);
    } else {
      model = model_copy(model);
      if (model_del_modifier(model) < 0)
	ERREXIT(1);
    }

    for (entry = objlist_get_head(symbol->value->obj.array.values);
	 !objlist_is_end(symbol->value->obj.array.values, entry);
	 entry = objentry_get_next(entry)) {
      s = objentry_get_obj(entry);

      if (is_struct) {
	if (objlist_is_end(symbol_list, sentry))
	  ERREXIT(1);
	ssymbol = objentry_get_obj(sentry);
	sentry = objentry_get_next(sentry);
	model = ssymbol->model;
      }

      s = symbol_set_model(s, model, symbols, static_area, stack_area);

      nentry = objentry_get_next(entry);
      objlist_extract(symbol->value->obj.array.values, entry);
      entry = objlist_insert_obj_prev(symbol->value->obj.array.values,
				      nentry, s, symbol_free);
    }
  }

  if ((symbol->type == C_TYPE_LABEL) && model_is_pointer(symbol->model)) {
    s = symbol_create(C_TYPE_VARIABLE, symbol->line, C_TYPE_NONE,
		      SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_LABEL);
    symbol = symbol_locate(symbol, symbols, static_area, stack_area);
    s->model = model_copy(symbol->model);
    if (symbol->value) {
      if ((symbol->value->type == C_TYPE_STRING) ||
	  (symbol->value->type == C_TYPE_ARRAY))
	model_pointer_to_array(symbol->model, symbol->value->obj.array.number);
    }
    s->value->obj.label.symbol = symbol;
    symbol = s;
  }

  return symbol;
}

static int search_term_arg(objentry_t entry, int arg, void *argp)
{
  syntax_t syntax;
  syntax = objentry_get_syntax(entry);
  if (syntax->type == C_TYPE_COMMA)
    return 1;
  return 0;
}

static int read_function_args(symbol_t symbol, objlist_t syntax_list,
			      objlist_t defines_list)
{
  objlist_t words;
  objentry_t entry;
  syntax_t syntax;
  symbol_t s;
  unsigned int flags = 0;
  int arg = 0;

  if (objlist_get_count(syntax_list) == 1) {
    syntax = objlist_extract_syntax_head(syntax_list);
    if (syntax->type == C_TYPE_VOID) {
      syntax_destroy(syntax);
      return 0;
    }
    objlist_insert_head(syntax_list, &syntax->entry);
  }

  while (!objlist_is_empty(syntax_list)) {
    entry = objlist_get_head(syntax_list);
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);

    entry = objlist_search_from_head(syntax_list, search_term_arg, 0, NULL);
    if (entry != NULL) {
      words = objlist_extract_list_prev(syntax_list, entry);
      objlist_extract(syntax_list, entry);
      syntax = objentry_get_syntax(entry);
      syntax_destroy(syntax);
    } else {
      words = objlist_extract_list_all(syntax_list);
    }

    entry = objlist_get_head(words);
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);

    if (syntax->type == C_TYPE_VARARG) {
      objlist_extract(words, entry);
      if (!objlist_is_empty(words) || !objlist_is_empty(syntax_list))
	ERREXIT(1);
      symbol->flags |= SYMBOL_FLAG_FUNCVARARG;
      symbol->model->flags |= MODEL_FLAG_FUNCVARARG;
      break;
    }

    if (syntax->type == C_TYPE_NLCC_TYPE_TYPE) {
      flags |= MODEL_FLAG_TYPETYPE;
      objlist_extract(words, entry);
      entry = objlist_get_head(words);
      syntax = objentry_get_syntax(entry);
      saveline(syntax->line);
    }

    if (syntax->type == C_TYPE_NLCC_TYPE_SYMBOL) {
      flags |= MODEL_FLAG_TYPESYMBOL;
      objlist_extract(words, entry);
      entry = objlist_get_head(words);
      syntax = objentry_get_syntax(entry);
      saveline(syntax->line);
    }

    s = symbol_read(words, defines_list, NULL);
    if (s == NULL)
      ERREXIT(1);

    s->model->flags |= flags;

    if (s->type == C_TYPE_LABEL) {
      s->type = C_TYPE_VARIABLE;
      model_array_to_pointer(s->model);
    }

    s->value = value_create(C_TYPE_ARG);
    if (s->value == NULL)
      ERREXIT(1);
    s->value->obj.arg.number = arg++;

    if (model_add_function_args(symbol->model, s->model) == NULL)
      ERREXIT(1);

    entry = objlist_insert_obj_tail(symbol->obj.function.args,
				    s, symbol_free);
    if (entry == NULL)
      ERREXIT(1);
  }

  return 0;
}

static int name_analyze(symbol_t symbol, objlist_t words,
			objlist_t defines_list, objlist_t symbols)
{
  syntax_t syntax;
  symbol_t s;
  model_t model;
  int n, is_pointer = 0, is_array = 0;

  while ((syntax = objlist_extract_syntax_head(words)) != NULL) {
    saveline(syntax->line);
    if (syntax->type != C_TYPE_MUL) {
      objlist_insert_head(words, &syntax->entry);
      break;
    }
    if (model_add_modifier_pointer(symbol->model) < 0)
      ERREXIT(1);
    syntax_destroy(syntax);
    is_pointer = 1;
  }

  if ((syntax = objlist_extract_syntax_tail(words)) != NULL) {
    if (syntax->type == C_TYPE_BRACKET_IN) {
      model = symbol->model;
      symbol->model = model_create(C_TYPE_FUNCTION);
      symbol->model->function.retval = model;
      symbol->obj.function.args = objlist_create(NULL);
      read_function_args(symbol, syntax->obj.words, defines_list);
      syntax_destroy(syntax);
      is_array = 1;
    } else {
      objlist_insert_tail(words, &syntax->entry);
    }
  }

  while ((syntax = objlist_extract_syntax_tail(words)) != NULL) {
    saveline(syntax->line);
    if (syntax->type != C_TYPE_BBRACKET_IN) {
      objlist_insert_tail(words, &syntax->entry);
      break;
    }
    s = value_read(syntax->obj.words, defines_list, symbols,
		   NULL, NULL, NULL, 0, NULL, 0, 0);
    if (s == NULL) {
      n = -1;
    } else {
      if ((s->type != C_TYPE_VARIABLE) ||
	  (s->value->type != C_TYPE_NUMBER))
	ERREXIT(1);
      n = s->value->obj.number;
    }
    if (model_add_modifier_array(symbol->model, n) < 0)
      ERREXIT(1);
    syntax_destroy(syntax);
    is_array = 1;
  }

  if ((syntax = objlist_extract_syntax_head(words)) != NULL) {
    saveline(syntax->line);
    switch (syntax->type) {
    case C_TYPE_WORD:
      symbol->name = strdup(syntax->obj.word);
      if (is_array)
	symbol->type = C_TYPE_LABEL;
      else if (is_pointer)
	symbol->type = C_TYPE_VARIABLE;
      break;
    case C_TYPE_BRACKET_IN:
      name_analyze(symbol, syntax->obj.words, defines_list, symbols);
      break;
    default:
      ERREXIT(1);
    }
  }

  if ((syntax = objlist_extract_syntax_head(words)) != NULL)
    ERREXITAT(syntax->line, 1);

  return 0;
}

symbol_t symbol_read(objlist_t syntax_list,
		     objlist_t defines_list, objlist_t symbols)
{
  objlist_t words;
  syntax_t syntax;
  model_t model;
  symbol_t symbol;
  unsigned int flags = 0;
  c_type_t type = C_TYPE_NONE;
  c_type_t sign_type = C_TYPE_NONE;
  c_type_t scope = C_TYPE_NONE;
  typedef_t _typedef;

  syntax = objlist_extract_syntax_head(syntax_list);
  if (syntax == NULL)
    return NULL;

  saveline(syntax->line);

  model = model_create(C_TYPE_NONE);

  if (syntax->type == C_TYPE_NLCC_ATTR_FUNCNOINIT) {
    flags |= SYMBOL_FLAG_FUNCNOINIT;
    syntax_destroy(syntax);
    syntax = objlist_extract_syntax_head(syntax_list);
  }

  if ((syntax->type == C_TYPE_EXTERN) || (syntax->type == C_TYPE_STATIC)) {
    scope = syntax->type;
    syntax_destroy(syntax);
    syntax = objlist_extract_syntax_head(syntax_list);
  }

  if (syntax->type == C_TYPE_WORD) {
    _typedef = defines_list_search_typedef_name(defines_list, syntax->obj.word);
    if (_typedef != NULL) {
      syntax->type = C_TYPE_TYPEDEF;
      syntax->obj.custom._typedef = _typedef;
    }
  }

  switch (syntax->type) {
  case C_TYPE_STRUCT:
  case C_TYPE_UNION:
  case C_TYPE_ENUM:
    syntax_custom_normalize(syntax, syntax_list, defines_list);
    /* fall through */
  case C_TYPE_TYPEDEF:
    model->type = syntax->type;
    model->custom.p = syntax->obj.custom.p;
    if (syntax->type == C_TYPE_TYPEDEF)
      model = model_deploy_typedef(model);
    syntax->obj.custom.p = NULL;
    syntax_destroy(syntax);
    break;

  default:
    if ((syntax->type == C_TYPE_SIGNED) || (syntax->type == C_TYPE_UNSIGNED)) {
      sign_type = syntax->type;
      syntax_destroy(syntax);
      if (objlist_is_empty(syntax_list)) {
	syntax = NULL;
      } else {
	syntax = objlist_extract_syntax_head(syntax_list);
      }
    }

    if ((syntax != NULL) &&
	((syntax->type == C_TYPE_SHORT) || (syntax->type == C_TYPE_LONG))) {
      type = syntax->type;
      syntax_destroy(syntax);
      if (objlist_is_empty(syntax_list)) {
	syntax = NULL;
      } else {
	syntax = objlist_extract_syntax_head(syntax_list);
      }
    }

    if ((syntax != NULL) && (syntax->type == C_TYPE_LONG)) {
      if (type != C_TYPE_LONG)
	ERREXIT(1);
      type = C_TYPE_LONGLONG;
      syntax_destroy(syntax);
      if (objlist_is_empty(syntax_list)) {
	syntax = NULL;
      } else {
	syntax = objlist_extract_syntax_head(syntax_list);
      }
    }

    /* for "long unsigned" */
    if ((syntax != NULL) &&
	((syntax->type == C_TYPE_SIGNED) || (syntax->type == C_TYPE_UNSIGNED))) {
      if (sign_type != C_TYPE_NONE)
	ERREXIT(1);
      sign_type = syntax->type;
      syntax_destroy(syntax);
      if (objlist_is_empty(syntax_list)) {
	syntax = NULL;
      } else {
	syntax = objlist_extract_syntax_head(syntax_list);
      }
    }

    if (syntax != NULL) {
      if (syntax->type == C_TYPE_CHAR) {
	if (type != C_TYPE_NONE)
	  ERREXIT(1);
	type = syntax->type;
	syntax_destroy(syntax);
      } else if (syntax->type == C_TYPE_INT) {
	if (type == C_TYPE_NONE)
	  type = syntax->type;
	syntax_destroy(syntax);
      } else if (syntax->type == C_TYPE_FLOAT) {
	if (type != C_TYPE_NONE)
	  ERREXIT(1);
	type = syntax->type;
	syntax_destroy(syntax);
      } else if (syntax->type == C_TYPE_DOUBLE) {
	if (type == C_TYPE_NONE)
	  type = syntax->type;
	else if (type == C_TYPE_LONG)
	  type = C_TYPE_LONGDOUBLE;
	else
	  ERREXIT(1);
	syntax_destroy(syntax);
      } else if (syntax->type == C_TYPE_VOID) {
	if ((type != C_TYPE_NONE) || (sign_type != C_TYPE_NONE))
	  ERREXIT(1);
	type = syntax->type;
	syntax_destroy(syntax);
      } else {
	objlist_insert_head(syntax_list, &syntax->entry);
      }
    }

    if (type == C_TYPE_NONE) {
      if (sign_type == C_TYPE_NONE)
	ERREXIT(1);
      type = C_TYPE_INT;
    }

    model->type = type;
    if (sign_type == C_TYPE_UNSIGNED)
      model->flags |= MODEL_FLAG_UNSIGNED;

    break;
  }

  words = objlist_create(NULL);

  while ((syntax = objlist_extract_syntax_head(syntax_list)) != NULL) {
    if (syntax->type != C_TYPE_MUL)
      break;
    objlist_insert_tail(words, &syntax->entry);
  }

  if (syntax != NULL) {
    if ((syntax->type != C_TYPE_WORD) &&
	(syntax->type != C_TYPE_BRACKET_IN) &&
	(syntax->type != C_TYPE_BBRACKET_IN))
      ERREXITAT(syntax->line, 1);
    objlist_insert_tail(words, &syntax->entry);
  }

  while ((syntax = objlist_extract_syntax_head(syntax_list)) != NULL) {
    saveline(syntax->line);
    if (syntax->type == C_TYPE_BRACKET_IN) {
      objlist_insert_tail(words, &syntax->entry);
      break;
    } else if (syntax->type == C_TYPE_BBRACKET_IN) {
      objlist_insert_tail(words, &syntax->entry);
    } else {
      ERREXIT(1);
    }
  }

  symbol = symbol_create(C_TYPE_VARIABLE, loadline(), scope,
			 SYMBOL_NAME_NONAME, model, flags, C_TYPE_NONE);
  if (symbols)
    objlist_insert_obj_tail(symbols, symbol, symbol_free);

  name_analyze(symbol, words, defines_list, symbols);

  if (symbol->model->type == C_TYPE_FUNCTION) {
    if ((symbol->type == C_TYPE_LABEL) &&
	(symbol->model->modifiers.number == 0)) {
      model_add_modifier_array(symbol->model, 1);
      symbol->flags |= SYMBOL_FLAG_FUNCTION;
    }
  }

  objlist_destroy(words);

  return symbol;
}

static int search_term_define(objentry_t entry, int arg, void *argp)
{
  syntax_t syntax;
  syntax = objentry_get_syntax(entry);
  if ((syntax->type == C_TYPE_EQ) ||
      (syntax->type == C_TYPE_COMMA) ||
      (syntax->type == C_TYPE_COLON) ||
      (syntax->type == C_TYPE_SEMICOLON) ||
      (syntax->type == C_TYPE_MBRACKET_IN))
    return 1;
  return 0;
}

static int search_term_value(objentry_t entry, int arg, void *argp)
{
  syntax_t syntax;
  syntax = objentry_get_syntax(entry);
  if ((syntax->type == C_TYPE_COMMA) ||
      (syntax->type == C_TYPE_SEMICOLON))
    return 1;
  return 0;
}

static int add_prefix(objlist_t words, objlist_t prefix)
{
  objentry_t entry;
  syntax_t syntax, copy;

  for (entry = objlist_get_tail(prefix);
       !objlist_is_end(prefix, entry);
       entry = objentry_get_prev(entry)) {
    syntax = objentry_get_syntax(entry);
    copy = syntax_create(syntax->type, syntax->line, 0, NULL);
    switch (syntax->type) {
    case C_TYPE_STRUCT:
    case C_TYPE_UNION:
    case C_TYPE_ENUM:
    case C_TYPE_TYPEDEF:
      copy->obj.custom.p = syntax->obj.custom.p;
      break;
    default:
      break;
    }
    objlist_insert_head(words, &copy->entry);
  }

  return 0;
}

static int regist_args(objlist_t args, objlist_t defines_list,
		       objlist_t symbols, area_t stack_area)
{
  objlist_t list;
  objentry_t entry;
  symbol_t symbol;

  list = defines_list_get_symbol_list(defines_list);

  for (entry = objlist_get_head(args);
       !objlist_is_end(args, entry);
       entry = objentry_get_next(entry)) {
    symbol = objentry_get_obj(entry);
    if (symbol_list_search(list, symbol->name) != NULL)
      ERREXIT(1);
    defines_list_insert_symbol(defines_list, symbol);
    objlist_insert_obj_tail(symbols, symbol, symbol_free);
    area_insert_symbol(stack_area, symbol);
  }

  return 0;
}

objlist_t symbol_define_read(objlist_t syntax_list, objlist_t sentence_list,
			     objlist_t defines_list, objlist_t symbols,
			     area_t static_area, area_t stack_area)
{
  objlist_t list, prefix, words;
  objentry_t entry;
  syntax_t syntax;
  symbol_t symbol, s;
  model_modifier_t modifier;
  typedef_t _typedef;

  list = objlist_create(NULL);
  prefix = objlist_create(NULL);

  syntax = objlist_extract_syntax_head(syntax_list);
  if (syntax != NULL)
    saveline(syntax->line);

  if ((syntax != NULL) && (syntax->type == C_TYPE_NLCC_ATTR_FUNCNOINIT)) {
    objlist_insert_tail(prefix, &syntax->entry);
    syntax = objlist_extract_syntax_head(syntax_list);
  }

  if ((syntax != NULL) &&
      ((syntax->type == C_TYPE_EXTERN) || (syntax->type == C_TYPE_STATIC))) {
    objlist_insert_tail(prefix, &syntax->entry);
    syntax = objlist_extract_syntax_head(syntax_list);
  }

  if ((syntax != NULL) && (syntax->type == C_TYPE_WORD)) { /* for typedef */
    _typedef = defines_list_search_typedef_name(defines_list, syntax->obj.word);
    if (_typedef != NULL) {
      syntax->type = C_TYPE_TYPEDEF;
      syntax->obj.custom._typedef = _typedef;
    }
  }

  if (syntax != NULL)
    objlist_insert_head(syntax_list, &syntax->entry);

  while ((syntax = objlist_extract_syntax_head(syntax_list)) != NULL) {
    saveline(syntax->line);

    switch (syntax->type) {
    case C_TYPE_EXTERN:
    case C_TYPE_STATIC:
    case C_TYPE_NLCC_ATTR_FUNCNOINIT:
      ERREXIT(1);

    case C_TYPE_STRUCT:
    case C_TYPE_UNION:
    case C_TYPE_ENUM:
      syntax_custom_normalize(syntax, syntax_list, defines_list);
      /* fall through */
    case C_TYPE_TYPEDEF:
    case C_TYPE_SIGNED:
    case C_TYPE_UNSIGNED:
    case C_TYPE_SHORT:
    case C_TYPE_LONG:
    case C_TYPE_CHAR:
    case C_TYPE_INT:
    case C_TYPE_FLOAT:
    case C_TYPE_DOUBLE:
    case C_TYPE_VOID:
      objlist_insert_tail(prefix, &syntax->entry);
      continue;

    default:
      objlist_insert_head(syntax_list, &syntax->entry);
      break;
    }
    break;
  }

  if (objlist_is_empty(prefix)) {
    objlist_destroy(prefix);
    return NULL;
  }

  while (1) {
    entry = objlist_search_from_head(syntax_list, search_term_define, 0, NULL);
    if (entry == NULL)
      ERREXIT(1);

    words = objlist_extract_list_prev(syntax_list, entry);
    add_prefix(words, prefix);
    symbol = symbol_read(words, defines_list, symbols);
    objlist_insert_tail(list, &symbol->entry);

    objlist_extract(syntax_list, entry);
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);

    if (syntax->type == C_TYPE_COLON) {
      s = value_read(syntax_list, defines_list, symbols, static_area, stack_area,
		     search_term_define, 0, NULL, 0, 1);
      if (s == NULL)
	ERREXIT(1);
      if ((s->value == NULL) || (s->value->type != C_TYPE_NUMBER))
	ERREXIT(1);
      symbol->flags |= SYMBOL_FLAG_BITFIELD;
      symbol->param.variable.bitfield.bits = s->value->obj.number;
      syntax = objlist_extract_syntax_head(syntax_list);
      saveline(syntax->line);
      break;
    }

    if (syntax->type == C_TYPE_EQ) {
      s = value_read(syntax_list, defines_list, symbols, static_area, stack_area,
		     search_term_value, 0, NULL, 0, 1);
      if (s == NULL)
	ERREXIT(1);

      if ((s->value != NULL) && model_is_array(symbol->model)) {
	modifier = model_get_modifier_last(symbol->model);
	if (modifier->param.array.number < 0) {
	  if ((s->value->type != C_TYPE_STRING) &&
	      (s->value->type != C_TYPE_ARRAY))
	    ERREXIT(1);
	  modifier->param.array.number = s->value->obj.array.number;
	}
      }

      s = symbol_change_model(s, symbol->model, symbols,
			      static_area, stack_area);

      syntax_destroy(syntax);

      while (model_is_address(s->model) &&
	     (s->type == C_TYPE_VARIABLE) && (s->value->type == C_TYPE_CAST)) {
	s = s->value->obj.op.arg[0];
      }

      if (symbol_is_const_number(s)) {
	symbol->value = value_create(C_TYPE_NUMBER);
	symbol->value->obj.number = s->value->obj.number;
      } else if (symbol->type == C_TYPE_LABEL) {
	symbol->value = s->value;
	s->value = NULL;
      } else if ((symbol->type == C_TYPE_VARIABLE) &&
		 (s->type == C_TYPE_VARIABLE) &&
		 (s->value->type == C_TYPE_ADDRESS) &&
		 ((s->value->obj.op.arg[0]->scope == C_TYPE_EXTERN) ||
		  (s->value->obj.op.arg[0]->location.type == C_TYPE_STATIC))) {
	symbol->value = s->value;
	s->value = NULL;
      } else if ((symbol->type == C_TYPE_VARIABLE) &&
		 (symbol->model->type == C_TYPE_STRUCT) &&
		 (s->type == C_TYPE_LABEL) &&
		 (s->value->type == C_TYPE_ARRAY)) {
	symbol->value = s->value;
	s->value = NULL;
      } else {
	s = symbol_locate(s, symbols, static_area, stack_area);
	if ((symbol->type == C_TYPE_VARIABLE) &&
	    (s->type == C_TYPE_LABEL) &&
	    s->value &&
	    ((s->value->type == C_TYPE_STRING) ||
	     (s->value->type == C_TYPE_ARRAY)) &&
	    ((s->scope == C_TYPE_EXTERN) ||
	     (s->location.type == C_TYPE_STATIC))) {
	  s->flags &= ~SYMBOL_FLAG_TEMPORARY;
	  symbol->value = value_create(C_TYPE_LABEL);
	  if (symbol->value == NULL)
	    ERREXIT(1);
	  symbol->value->obj.label.symbol = s;
	} else {
	  if (sentence_list == NULL)
	    ERREXIT(1);
	  syntax = syntax_create(C_TYPE_SYMBOL, symbol->line, 0, NULL);
	  if (syntax == NULL)
	    ERREXIT(1);
	  syntax->type = C_TYPE_SYMBOL;
	  syntax->obj.symbol = value_make_op2(C_TYPE_EQ, symbol, s, defines_list,
					      symbols, static_area, stack_area);
	  if (syntax->obj.symbol == NULL)
	    ERREXIT(1);
	  objlist_insert_tail(sentence_list, &syntax->entry);
	}
      }

      syntax = objlist_extract_syntax_head(syntax_list);
      saveline(syntax->line);
    }

    if (syntax->type == C_TYPE_MBRACKET_IN) {
      words = syntax->obj.words;
      syntax->obj.words = NULL;
      syntax->type = C_TYPE_BLOCK;
      syntax->obj.block.defines = defines_create();
      if (syntax->obj.block.defines == NULL)
	ERREXIT(1);
      syntax->obj.block.symbols = objlist_create(NULL);
      if (syntax->obj.block.symbols == NULL)
	ERREXIT(1);
      syntax->obj.block.syntaxes = words;

      symbol->obj.function.labels = objlist_create(NULL);
      if (symbol->obj.function.labels == NULL)
	ERREXIT(1);
      symbol->obj.function.stack = area_create(C_TYPE_STACK);
      if (symbol->obj.function.stack == NULL)
	ERREXIT(1);
      symbol->obj.function.initlabel =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      symbol->obj.function.retlabel =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);

      objlist_insert_head(defines_list, &syntax->obj.block.defines->entry);
      regist_args(symbol->obj.function.args,
		  defines_list, syntax->obj.block.symbols,
		  symbol->obj.function.stack);
      objlist_extract(defines_list, &syntax->obj.block.defines->entry);

      symbol->value = value_create(C_TYPE_BLOCK);
      if (symbol->value == NULL)
	ERREXIT(1);
      symbol->value->obj.block.syntax = syntax;
      symbol->flags |= (SYMBOL_FLAG_READONLY | SYMBOL_FLAG_FUNCBLOCK);
      break;
    }

    if (syntax->type == C_TYPE_COMMA) {
      syntax_destroy(syntax);
      continue;
    }

    if (syntax->type == C_TYPE_SEMICOLON) {
      syntax_destroy(syntax);
      break;
    }
  }

  return list;
}
