#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 "lex.h"
#include "symbol.h"
#include "model.h"
#include "value.h"
#include "area.h"

/*****************************************************************
 * syntax_t
 */

syntax_t syntax_destroy(syntax_t syntax)
{
  if (syntax != NULL) {
    switch (syntax->type) {
    case C_TYPE_WORD:
      if (syntax->obj.word != NULL)
	free(syntax->obj.word);
      break;

    case C_TYPE_WORDS:
      if (syntax->obj.words != NULL)
	objlist_destroy(syntax->obj.words);
      break;

    case C_TYPE_BLOCK:
      if (syntax->obj.block.defines != NULL)
	defines_destroy(syntax->obj.block.defines);
      if (syntax->obj.block.symbols != NULL)
	objlist_destroy(syntax->obj.block.symbols);
      if (syntax->obj.block.syntaxes != NULL)
	objlist_destroy(syntax->obj.block.syntaxes);
      break;

    case C_TYPE_SYMBOL:
      if (syntax->obj.symbol != NULL)
	symbol_destroy(syntax->obj.symbol);
      break;

    case C_TYPE_BRACKET_IN:
    case C_TYPE_BBRACKET_IN:
      if (syntax->obj.words != NULL)
	objlist_destroy(syntax->obj.words);
      break;

    default:
      if (syntax->obj.p)
	free(syntax->obj.p);
      break;
    }

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

  return NULL;
}

static void syntax_free(void *p)
{
  syntax_destroy((syntax_t)p);
}

syntax_t syntax_create(c_type_t type, int line, int val, void *obj)
{
  syntax_t syntax;
  char *s;

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

  if (objentry_init(&syntax->entry, syntax, 0, syntax_free) == NULL)
    goto err;

  syntax->type = type;
  syntax->line = line;

  switch (type) {
  case C_TYPE_WORD:
    if ((s = strdup(obj)) == NULL)
      goto err;
    syntax->type = C_TYPE_WORD;
    syntax->obj.word = s;
    break;

  case C_TYPE_WORDS:
    syntax->obj.words = obj;
    break;

  case C_TYPE_SYMBOL:
    syntax->obj.symbol = obj;
    break;

  case C_TYPE_BRACKET_IN:
  case C_TYPE_BBRACKET_IN:
    syntax->obj.words = obj;
    break;

  case C_TYPE_LABEL:
    switch (syntax->obj.label.type) {
    case C_TYPE_LABEL:
      if (syntax->obj.label.obj.label.string != NULL)
	free(syntax->obj.label.obj.label.string);
      break;
    case C_TYPE_CASE:
    case C_TYPE_DEFAULT:
    default:
      break;
    }
    if (syntax->obj.label.name != NULL)
      free(syntax->obj.label.name);
    break;

  default:
    syntax->obj.p = obj;
    break;
  }

  return syntax;

err:
  if (syntax != NULL)
    syntax_destroy(syntax);
  return NULL;
}

syntax_t syntax_create_label(syntax_t syntax, char *name, int line,
			     c_type_t type, long number, char *string)
{
  static int id = 0;
  char n[32];
  char *s = NULL, *str = NULL;

  if (name == NULL) {
    sprintf(n, "%s%d", DEFAULT_LABEL_PREFIX, id);
    name = n;
  }

  if ((s = strdup(name)) == NULL)
    goto err;

  if (string == NULL)
    string = s;
  if ((str = strdup(string)) == NULL)
    goto err;

  if (syntax == NULL) {
    syntax = syntax_create(C_TYPE_LABEL, line, 0, NULL);
    if (syntax == NULL)
      goto err;
  } else {
    syntax->type = C_TYPE_LABEL;
  }

  syntax->obj.label.type = type;
  syntax->obj.label.name = s;
  syntax->obj.label.id = id++;
  switch (type) {
  case C_TYPE_LABEL: syntax->obj.label.obj.label.string = str;    break;
  case C_TYPE_CASE:  syntax->obj.label.obj._case.number = number; break;
  case C_TYPE_DEFAULT:
  default:
    break;
  }

  return syntax;

err:
  if (s != NULL)
    free(s);
  if (str != NULL)
    free(str);
  return NULL;
}

syntax_t objentry_get_syntax(objentry_t entry)
{
  if (entry == NULL)
    return NULL;
  return (syntax_t)objentry_get_obj(entry);
}

syntax_t objlist_extract_syntax(objlist_t list, syntax_t syntax)
{
  return objentry_get_syntax(objlist_extract(list, &syntax->entry));
}

syntax_t objlist_extract_syntax_next(objlist_t list, syntax_t from)
{
  return objentry_get_syntax(objlist_extract_next(list, &from->entry));
}

syntax_t objlist_extract_syntax_prev(objlist_t list, syntax_t from)
{
  return objentry_get_syntax(objlist_extract_prev(list, &from->entry));
}

syntax_t objlist_extract_syntax_head(objlist_t list)
{
  return objentry_get_syntax(objlist_extract_head(list));
}

syntax_t objlist_extract_syntax_tail(objlist_t list)
{
  return objentry_get_syntax(objlist_extract_tail(list));
}

syntax_t syntax_insert_tail(objlist_t list, c_type_t type, int line,
			    model_t model, long val, void *obj)
{
  syntax_t syntax;
  symbol_t symbol;
  char *s;

  switch (type) {
  case C_TYPE_NUMBER:
    type = C_TYPE_SYMBOL;
    symbol = symbol_create(C_TYPE_VARIABLE, line, C_TYPE_STATIC,
			   SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
    symbol->value->obj.number = val;
    symbol->flags |= SYMBOL_FLAG_READONLY;
    symbol->model = model;
    obj = symbol;
    break;

  case C_TYPE_STRING:
    if ((s = malloc(val + 1)) == NULL)
      return NULL;
    memcpy(s, obj, val);
    s[val] = '\0';
    type = C_TYPE_SYMBOL;
    symbol = symbol_create(C_TYPE_LABEL, line, C_TYPE_STATIC,
			   SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_STRING);
    symbol->value->obj.string.size = val + 1;
    symbol->value->obj.string.s = s;
    symbol->flags |= SYMBOL_FLAG_READONLY;
    symbol->model = model;
    model_add_modifier_array(symbol->model, val + 1);
    obj = symbol;
    break;

  default:
    break;
  }

  syntax = syntax_create(type, line, val, obj);
  if (syntax == NULL)
    return NULL;

  return objentry_get_syntax(objlist_insert_tail(list, &syntax->entry));
}

void syntax_print_label(syntax_label_t label, int indent)
{
  indent_print(indent);
  printf("LABEL: %s (%s, id: 0x%x)", type_get_word(label->type),
	 (label->name != NULL) ? label->name : "UNDEF", label->id);
  switch (label->type) {
  case C_TYPE_LABEL:
    printf(" (%s)\n", label->obj.label.string);
    break;
  case C_TYPE_CASE:
    printf(" (%ld, ", label->obj._case.number);
    symbol_print_name(label->obj._case.symbol);
    printf(")\n");
    break;
  case C_TYPE_DEFAULT:
    printf("\n");
    break;
  default:
    printf("\n");
    break;
  }
}

void syntax_print_block(syntax_block_t block, int indent)
{
  objentry_t entry;
  syntax_t syntax;
  symbol_t symbol;
  struct_t _struct;
  union_t _union;
  enum_t _enum;
  typedef_t _typedef;

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

  indent++;

  if (block->defines != NULL) {
    indent_print(indent);
    printf("DEFINES\n");

    for (entry = objlist_get_head(&block->defines->struct_list);
	 !objlist_is_end(&block->defines->struct_list, entry);
	 entry = objentry_get_next(entry)) {
      _struct = objentry_get_obj(entry);
      struct_print(_struct, indent + 1);
    }

    for (entry = objlist_get_head(&block->defines->union_list);
	 !objlist_is_end(&block->defines->union_list, entry);
	 entry = objentry_get_next(entry)) {
      _union = objentry_get_obj(entry);
      union_print(_union, indent + 1);
    }

    for (entry = objlist_get_head(&block->defines->enum_list);
	 !objlist_is_end(&block->defines->enum_list, entry);
	 entry = objentry_get_next(entry)) {
      _enum = objentry_get_obj(entry);
      enum_print(_enum, indent + 1);
    }

    for (entry = objlist_get_head(&block->defines->typedef_list);
	 !objlist_is_end(&block->defines->typedef_list, entry);
	 entry = objentry_get_next(entry)) {
      _typedef = objentry_get_obj(entry);
      typedef_print(_typedef, indent + 1);
    }

    for (entry = objlist_get_head(&block->defines->symbol_list);
	 !objlist_is_end(&block->defines->symbol_list, entry);
	 entry = objentry_get_next(entry)) {
      symbol = objentry_get_obj(entry);
      symbol_print_simple(symbol, indent + 1);
    }
  }

  if (block->symbols != NULL) {
    indent_print(indent);
    printf("SYMBOLS\n");

    for (entry = objlist_get_head(block->symbols);
	 !objlist_is_end(block->symbols, entry);
	 entry = objentry_get_next(entry)) {
      symbol = objentry_get_obj(entry);
      symbol_print(symbol, indent + 1);
    }
  }

  if (block->syntaxes != NULL) {
    indent_print(indent);
    printf("SYNTAXES\n");

    for (entry = objlist_get_head(block->syntaxes);
	 !objlist_is_end(block->syntaxes, entry);
	 entry = objentry_get_next(entry)) {
      syntax = objentry_get_syntax(entry);
      syntax_print(syntax, indent + 1);
    }
  }
}

void syntax_print_ifgoto(syntax_ifgoto_t ifgoto, int indent)
{
  indent_print(indent);
  printf("IFGOTO\n");

  indent++;
  if (ifgoto->cond) {
    indent_print(indent); printf("COND\n");
    symbol_print_value_simple(ifgoto->cond, indent + 1);
  }

  if (ifgoto->label) {
    indent_print(indent); printf("LABEL\n");
    syntax_print(ifgoto->label, indent + 1);
  }
}

void syntax_print_ifngoto(syntax_ifngoto_t ifngoto, int indent)
{
  indent_print(indent);
  printf("IFNGOTO\n");

  indent++;
  if (ifngoto->cond) {
    indent_print(indent); printf("COND\n");
    symbol_print_value_simple(ifngoto->cond, indent + 1);
  }

  if (ifngoto->label) {
    indent_print(indent); printf("LABEL\n");
    syntax_print(ifngoto->label, indent + 1);
  }
}

void syntax_print_if(syntax_if_t _if, int indent)
{
  indent_print(indent);
  printf("IF\n");

  indent++;
  if (_if->cond) {
    indent_print(indent); printf("COND\n");
    symbol_print_value_simple(_if->cond, indent + 1);
  }

  if (_if->label.true) {
    indent_print(indent); printf("LABEL_TRUE\n");
    syntax_print(_if->label.true, indent + 1);
  }
  if (_if->label.false) {
    indent_print(indent); printf("LABEL_FALSE\n");
    syntax_print(_if->label.false, indent + 1);
  }
  if (_if->label.end) {
    indent_print(indent); printf("LABEL_END\n");
    syntax_print(_if->label.end, indent + 1);
  }

  if (_if->true) {
    indent_print(indent); printf("TRUE\n");
    syntax_print(_if->true, indent + 1);
  }
  if (_if->false) {
    indent_print(indent); printf("FALSE\n");
    syntax_print(_if->false, indent + 1);
  }
}

void syntax_print_for(syntax_for_t _for, int indent)
{
  indent_print(indent);
  printf("FOR\n");

  indent++;
  if (_for->init) {
    indent_print(indent); printf("INIT\n");
    symbol_print_value_simple(_for->init, indent + 1);
  }
  if (_for->cond) {
    indent_print(indent); printf("COND\n");
    symbol_print_value_simple(_for->cond, indent + 1);
  }
  if (_for->next) {
    indent_print(indent); printf("NEXT\n");
    symbol_print_value_simple(_for->next, indent + 1);
  }

  if (_for->label.start) {
    indent_print(indent); printf("LABEL_START\n");
    syntax_print(_for->label.start, indent + 1);
  }
  if (_for->label.loop) {
    indent_print(indent); printf("LABEL_LOOP\n");
    syntax_print(_for->label.loop, indent + 1);
  }
  if (_for->label.next) {
    indent_print(indent); printf("LABEL_NEXT\n");
    syntax_print(_for->label.next, indent + 1);
  }
  if (_for->label.end) {
    indent_print(indent); printf("LABEL_END\n");
    syntax_print(_for->label.end, indent + 1);
  }

  if (_for->loop) {
    indent_print(indent); printf("LOOP\n");
    syntax_print(_for->loop, indent + 1);
  }
}

void syntax_print_while(syntax_while_t _while, int indent)
{
  indent_print(indent);
  printf("WHILE\n");

  indent++;
  if (_while->cond) {
    indent_print(indent); printf("COND\n");
    symbol_print_value_simple(_while->cond, indent + 1);
  }

  if (_while->label.start) {
    indent_print(indent); printf("LABEL_START\n");
    syntax_print(_while->label.start, indent + 1);
  }
  if (_while->label.loop) {
    indent_print(indent); printf("LABEL_LOOP\n");
    syntax_print(_while->label.loop, indent + 1);
  }
  if (_while->label.end) {
    indent_print(indent); printf("LABEL_END\n");
    syntax_print(_while->label.end, indent + 1);
  }

  if (_while->loop) {
    indent_print(indent); printf("LOOP\n");
    syntax_print(_while->loop, indent + 1);
  }
}

void syntax_print_do(syntax_do_t _do, int indent)
{
  indent_print(indent);
  printf("DO\n");

  indent++;
  if (_do->loop) {
    indent_print(indent); printf("LOOP\n");
    syntax_print(_do->loop, indent + 1);
  }

  if (_do->label.start) {
    indent_print(indent); printf("LABEL_START\n");
    syntax_print(_do->label.start, indent + 1);
  }
  if (_do->label.next) {
    indent_print(indent); printf("LABEL_NEXT\n");
    syntax_print(_do->label.next, indent + 1);
  }
  if (_do->label.end) {
    indent_print(indent); printf("LABEL_END\n");
    syntax_print(_do->label.end, indent + 1);
  }

  if (_do->cond) {
    indent_print(indent); printf("COND\n");
    symbol_print_value_simple(_do->cond, indent + 1);
  }
}

void syntax_print_switch(syntax_switch_t _switch, int indent)
{
  objentry_t entry;
  syntax_t syntax;

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

  indent++;
  if (_switch->value) {
    indent_print(indent); printf("VALUE\n");
    symbol_print_value_simple(_switch->value, indent + 1);
  }

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

  for (entry = objlist_get_head(_switch->conds);
       !objlist_is_end(_switch->conds, entry);
       entry = objentry_get_next(entry)) {
    syntax = objentry_get_syntax(entry);
    syntax_print(syntax, indent + 1);
  }

  if (_switch->label.end) {
    indent_print(indent); printf("LABEL_END\n");
    syntax_print(_switch->label.end, indent + 1);
  }

  if (_switch->block != NULL) {
    indent_print(indent);
    printf("BLOCK\n");
    syntax_print_block(&_switch->block->obj.block, indent + 1);
  }
}

void syntax_print_goto(syntax_goto_t _goto, int indent)
{
  indent_print(indent);
  printf("GOTO: %s\n", (_goto->string != NULL) ? _goto->string : "NONAME");

  if (_goto->label != NULL) {
    syntax_print(_goto->label, indent + 1);
  }
}

void syntax_print_break(syntax_break_t _break, int indent)
{
  indent_print(indent);
  printf("BREAK:\n");

  if (_break->label != NULL) {
    syntax_print(_break->label, indent + 1);
  }
}

void syntax_print_continue(syntax_continue_t _continue, int indent)
{
  indent_print(indent);
  printf("CONTINUE:\n");

  if (_continue->label != NULL) {
    syntax_print(_continue->label, indent + 1);
  }
}

void syntax_print_return(syntax_return_t _return, int indent)
{
  indent_print(indent);
  printf("RETURN: (");
  symbol_print_name(_return->function);
  printf(")\n");

  if (_return->value != NULL) {
    symbol_print_value_simple(_return->value, indent + 1);
  }
}

void syntax_print(syntax_t syntax, int indent)
{
  printf("LINE: %d\n", syntax->line);
  switch (syntax->type) {
  case C_TYPE_NONE:
    indent_print(indent);
    printf("NONE\n");
    break;
  case C_TYPE_WORD:
    indent_print(indent);
    printf("WORD: %s\n", syntax->obj.word);
    break;
  case C_TYPE_WORDS:
    indent_print(indent);
    printf("WORDS:\n");
    break;
  case C_TYPE_MODEL:
    model_print(syntax->obj.model, indent);
    break;
  case C_TYPE_SYMBOL:
    symbol_print_value_simple(syntax->obj.symbol, indent);
    break;
  case C_TYPE_LABEL:
    syntax_print_label(&syntax->obj.label, indent);
    break;
  case C_TYPE_BLOCK:
    syntax_print_block(&syntax->obj.block, indent);
    break;
  case C_TYPE_BREAK:
    syntax_print_break(&syntax->obj._break, indent);
    break;
  case C_TYPE_CONTINUE:
    syntax_print_continue(&syntax->obj._continue, indent);
    break;
  case C_TYPE_NOP:
    indent_print(indent);
    printf("NOP\n");
    break;
  case C_TYPE_IFGOTO:
    syntax_print_ifgoto(&syntax->obj.ifgoto, indent);
    break;
  case C_TYPE_IFNGOTO:
    syntax_print_ifngoto(&syntax->obj.ifngoto, indent);
    break;
  case C_TYPE_IF:
    syntax_print_if(&syntax->obj._if, indent);
    break;
  case C_TYPE_FOR:
    syntax_print_for(&syntax->obj._for, indent);
    break;
  case C_TYPE_WHILE:
    syntax_print_while(&syntax->obj._while, indent);
    break;
  case C_TYPE_DO:
    syntax_print_do(&syntax->obj._do, indent);
    break;
  case C_TYPE_SWITCH:
    syntax_print_switch(&syntax->obj._switch, indent);
    break;
  case C_TYPE_GOTO:
    syntax_print_goto(&syntax->obj._goto, indent);
    break;
  case C_TYPE_RETURN:
    syntax_print_return(&syntax->obj._return, indent);
    break;
  case C_TYPE_BRACKET_IN:
    indent_print(indent);
    printf("BRACKET:\n");
    break;
  case C_TYPE_MBRACKET_IN:
    indent_print(indent);
    printf("MBRACKET:\n");
    break;
  case C_TYPE_BBRACKET_IN:
    indent_print(indent);
    printf("BBRACKET:\n");
    break;
  default:
    indent_print(indent);
    printf("UNKNOWN:\n");
    break;
  }
}

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

int syntax_init_builtin(objlist_t defines_list, objlist_t symbols,
			area_t static_area)
{
  return 0;
}

static objlist_t read_comment(objlist_t words)
{
  objlist_t list;
  syntax_t syntax;
  int comment = 0;

  list = objlist_create(NULL);

  while ((syntax = objlist_extract_syntax_head(words)) != NULL) {
    saveline(syntax->line);
    switch (syntax->type) {
    case C_TYPE_COMMENT_IN:
      comment = 1;
      syntax_destroy(syntax);
      break;

    case C_TYPE_COMMENT_OUT:
      comment = 0;
      syntax_destroy(syntax);
      break;

    default:
      if (comment) {
	syntax_destroy(syntax);
	break;
      }
      objlist_insert_tail(list, &syntax->entry);
      break;
    }
  }

  return list;
}

static objlist_t read_extension(objlist_t words)
{
  objentry_t entry, pentry, nentry;
  syntax_t syntax, psyntax, nsyntax;
  int val;

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) {
    pentry = objentry_get_prev(entry);
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    switch (syntax->type) {
    case C_TYPE_CONST:
    case C_TYPE_RESTRICT:
    case C_TYPE_REGISTER:
    case C_TYPE_VOLATILE:
    case C_TYPE_EXT_EXTENSION:
    case C_TYPE_EXT_CONST:
    case C_TYPE_EXT_RESTRICT:
    case C_TYPE_EXT_DEAD2:
    case C_TYPE_EXT_PURE2:
    case C_TYPE_EXT_UNUSED:
    case C_TYPE_EXT_USED:
    case C_TYPE_EXT_PACKED:
    case C_TYPE_EXT_WEAK_SYMBOL:
    case C_TYPE_C11_NORETURN:
    case C_TYPE_C11_THREAD_LOCAL:
    case C_TYPE_CLANG_NONNULL:
    case C_TYPE_CLANG_NULLABLE:
      /* discard */
      objlist_extract(words, entry);
      entry = pentry;
      break;

    case C_TYPE_EXT_ATTRIBUTE:
    case C_TYPE_EXT_ASM:
    case C_TYPE_EXT_ALIGNED:
    case C_TYPE_EXT_SECTION:
    case C_TYPE_C11_ALIGNAS:
    case C_TYPE_C11_ATOMIC:
    case C_TYPE_C11_STATIC_ASSERT:
    case C_TYPE_BUILTIN_EXPECT:
    case C_TYPE_BUILTIN_PREFETCH:
      /* discard arguments */
      nentry = objentry_get_next(entry);
      if (objlist_is_end(words, nentry))
	ERREXIT(1);
      nsyntax = objentry_get_syntax(nentry);
      if (nsyntax->type != C_TYPE_BRACKET_IN)
	ERREXIT(1);
      objlist_extract(words, nentry);
      objlist_extract(words, entry);
      entry = pentry;
      break;

    case C_TYPE_EXT_ALIGNOF:
    case C_TYPE_C11_ALIGNOF:
      val = 4;
      goto convert;
    case C_TYPE_BUILTIN_CONSTANT_P:
    case C_TYPE_BUILTIN_FRAME_ADDRESS:
    case C_TYPE_BUILTIN_RETURN_ADDRESS:
      val = 0;
    convert:
      /* discard arguments and convert to value */
      nentry = objentry_get_next(entry);
      if (objlist_is_end(words, nentry))
	ERREXIT(1);
      nsyntax = objentry_get_syntax(nentry);
      if (nsyntax->type != C_TYPE_BRACKET_IN)
	ERREXIT(1);
      objlist_extract(words, nentry);

      syntax->type = C_TYPE_SYMBOL;
      syntax->obj.symbol = symbol_create(C_TYPE_VARIABLE, syntax->line, C_TYPE_STATIC,
					 SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
      syntax->obj.symbol->value->obj.number = val;
      syntax->obj.symbol->flags |= SYMBOL_FLAG_READONLY;
      syntax->obj.symbol->model = model_create(C_TYPE_INT);
      break;

    case C_TYPE_INLINE:
    case C_TYPE_EXT_INLINE:
      /* convert inline to static */
      nentry = objentry_get_next(entry);
      psyntax = !objlist_is_end(words, pentry) ? objentry_get_syntax(pentry) : NULL;
      nsyntax = !objlist_is_end(words, nentry) ? objentry_get_syntax(nentry) : NULL;
      if (((psyntax != NULL) && (psyntax->type == C_TYPE_STATIC)) ||
	  ((nsyntax != NULL) && (nsyntax->type == C_TYPE_STATIC))) {
	objlist_extract(words, entry);
	entry = pentry;
	break;
      }
      syntax->type = C_TYPE_STATIC;
      break;

    default:
      break;
    }
  }

  return words;
}

static objlist_t read_bracket(objlist_t words, c_type_t type)
{
  objlist_t list;
  syntax_t syntax;

  list = objlist_create(NULL);

  while ((syntax = objlist_extract_syntax_head(words)) != NULL) {
    saveline(syntax->line);
    switch (syntax->type) {
    case C_TYPE_MBRACKET_IN: /* { */
    case C_TYPE_BRACKET_IN: /* ( */
    case C_TYPE_BBRACKET_IN: /* [ */
      syntax->obj.words = read_bracket(words, syntax->type);
      break;

    case C_TYPE_BRACKET_OUT: /* ) */
      if ((type == C_TYPE_NONE) || (type != C_TYPE_BRACKET_IN))
	ERREXIT(1);
      syntax_destroy(syntax);
      goto ret;

    case C_TYPE_MBRACKET_OUT: /* } */
      if ((type == C_TYPE_NONE) || (type != C_TYPE_MBRACKET_IN))
	ERREXIT(1);
      syntax_destroy(syntax);
      goto ret;

    case C_TYPE_BBRACKET_OUT: /* ] */
      if ((type == C_TYPE_NONE) || (type != C_TYPE_BBRACKET_IN))
	ERREXIT(1);
      syntax_destroy(syntax);
      goto ret;

    default:
      break;
    }
    objlist_insert_tail(list, &syntax->entry);
  }

ret:
  list = read_extension(list);

  return list;
}

static symbol_t insert_symbol(objlist_t defines_list, symbol_t symbol)
{
  objlist_t list;
  symbol_t s;

  list = defines_list_get_symbol_list(defines_list);
  s = symbol_list_search(list, symbol->name);

  if (s != NULL) {
    if ((s->flags ^ symbol->flags) & SYMBOL_FLAG_FUNCTION)
      ERREXIT(1);
    if ((s->scope != C_TYPE_EXTERN) && (symbol->scope != C_TYPE_EXTERN)) {
      if (s->flags & SYMBOL_FLAG_FUNCTION) {
	if ((s->scope != C_TYPE_STATIC) || (symbol->scope != C_TYPE_STATIC))
	  ERREXIT(1);
	if ((s->flags & symbol->flags) & SYMBOL_FLAG_FUNCBLOCK)
	  ERREXIT(1);
      } else {
	ERREXIT(1);
      }
    }
    if ((s->scope == C_TYPE_STATIC) && (symbol->scope != C_TYPE_STATIC))
      ERREXIT(1);
    if ((s->scope != C_TYPE_STATIC) && (symbol->scope == C_TYPE_STATIC))
      ERREXIT(1);
    if (model_cmp(s->model, symbol->model))
      ERREXIT(1);
  }

  defines_list_insert_symbol(defines_list, symbol);

  return symbol;
}

static syntax_t search_label(objlist_t labels, char *string)
{
  objentry_t entry;
  syntax_t label;

  for (entry = objlist_get_head(labels);
       !objlist_is_end(labels, entry);
       entry = objentry_get_next(entry)) {
    label = objentry_get_syntax(entry);
    if ((label->obj.label.type == C_TYPE_LABEL) &&
	!strcmp(label->obj.label.obj.label.string, string))
      return label;
  }

  return NULL;
}

static objlist_t change_static_symbol_name(objlist_t list)
{
  objentry_t entry;
  symbol_t symbol;
  char *name;
  static int count = 0;

  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) || (symbol->scope != C_TYPE_STATIC))
      continue;
    name = malloc(strlen(symbol->name) + 16);
    sprintf(name, "%s.s.%d", symbol->name, count++);
    free(symbol->name);
    symbol->name = name;
  }

  return list;
}

static objlist_t read_structure(objlist_t syntax_list, symbol_t function,
				objlist_t syntaxes, objlist_t defines_list,
				objlist_t symbols, objlist_t labels,
				area_t static_area, area_t stack_area);

static objlist_t read_sentence(objlist_t syntax_list, symbol_t function,
			       objlist_t syntaxes, objlist_t defines_list,
			       objlist_t symbols, objlist_t labels,
			       area_t static_area, area_t stack_area)
{
  objlist_t words, symbol_list;
  objentry_t entry;
  syntax_t syntax, s;
  symbol_t symbol;
  char *word;
  int is_typedef = 0;
  typedef_t _typedef;

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

    /* for typedef */
    switch (syntax->type) {
    case C_TYPE_TYPEDEF:
      if (is_typedef)
	ERREXIT(1);
      is_typedef = 1;
      continue;
    case 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;
      }
      break;
    default:
      break;
    }

    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:
    case C_TYPE_EXTERN:
    case C_TYPE_STATIC:
    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:
    case C_TYPE_NLCC_ATTR_FUNCNOINIT:
      objlist_insert_head(syntax_list, &syntax->entry);
      symbol_list = symbol_define_read(syntax_list, syntaxes, defines_list,
				       symbols, static_area, stack_area);
      while ((entry = objlist_extract_head(symbol_list)) != NULL) {
	symbol = objentry_get_obj(entry);
	if (symbol->name == NULL) {
	  switch (syntax->type) {
	  case C_TYPE_STRUCT:
	  case C_TYPE_UNION:
	  case C_TYPE_ENUM:
	  case C_TYPE_TYPEDEF:
	    continue;
	  default:
	    ERREXIT(1);
	  }
	}
	if (symbol->value != NULL)
	  symbol->flags |= SYMBOL_FLAG_INITFIRST;
	if (!is_typedef) {
	  if ((symbol->flags & SYMBOL_FLAG_FUNCTION) &&
	      !(symbol->flags & SYMBOL_FLAG_FUNCBLOCK) &&
	      (symbol->scope == C_TYPE_NONE)) {
	    symbol->scope = C_TYPE_EXTERN;
	  }
	  if (defines_list != NULL)
	    insert_symbol(defines_list, symbol);
	  if (symbol->flags & SYMBOL_FLAG_FUNCBLOCK) {
	    s = symbol->value->obj.block.syntax;
	    words = objlist_create(NULL);
	    if (words == NULL)
	      ERREXIT(1);
	    objlist_insert_head(defines_list, &s->obj.block.defines->entry);
	    s->obj.block.syntaxes =
	      syntax_read_structure(s->obj.block.syntaxes, symbol, words,
				    defines_list, s->obj.block.symbols,
				    symbol->obj.function.labels,
				    static_area, symbol->obj.function.stack);
	    objlist_extract(defines_list, &s->obj.block.defines->entry);
	    change_static_symbol_name(&s->obj.block.defines->symbol_list);
	  }
	  if (symbol->scope != C_TYPE_EXTERN)
	    symbol_locate(symbol, symbols, static_area, stack_area);
	} else {
	  words = defines_list_get_typedef_list(defines_list);
	  _typedef = symbol_list_search(words, symbol->name);
	  if (_typedef != NULL) {
	    if (model_cmp(symbol->model, _typedef->model))
	      ERREXIT(1);
	  } else {
	    _typedef = symbol;
	    defines_list_insert_typedef(defines_list, _typedef);
	  }
	}
      }
      is_typedef = 0;
      break;

    case C_TYPE_MBRACKET_IN:
      if (syntaxes == NULL)
	ERREXIT(1);
      words = syntax->obj.words;
      syntax->obj.words = NULL;
      syntax->type = C_TYPE_BLOCK;
      syntax->obj.block.defines = defines_create();
      objlist_insert_head(defines_list, &syntax->obj.block.defines->entry);
      syntax->obj.block.symbols = objlist_create(NULL);
      syntax->obj.block.syntaxes = objlist_create(NULL);
      syntax->obj.block.syntaxes =
	read_structure(words, function, syntax->obj.block.syntaxes,
		       defines_list, syntax->obj.block.symbols,
		       labels, static_area, stack_area);
      objlist_extract(defines_list, &syntax->obj.block.defines->entry);
      change_static_symbol_name(&syntax->obj.block.defines->symbol_list);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_COMMENT_IN:
    case C_TYPE_COMMENT_OUT:
      syntax_destroy(syntax);
      break;

    case C_TYPE_CASE:
      if (syntaxes == NULL)
	ERREXIT(1);
      symbol = value_read(syntax_list, defines_list, symbols,
			  static_area, stack_area,
			  NULL, C_TYPE_COLON, NULL, 0, 0);
      if (symbol == NULL)
	ERREXIT(1);
      symbol = symbol_sign_extension(symbol, symbols, static_area, stack_area);
      symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      syntax_create_label(syntax, NULL, 0, syntax->type,
			  symbol->value->obj.number, NULL);
      syntax->obj.label.obj._case.symbol = symbol;

      /* dummy value (replaced to switch value later) */
      symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_STATIC,
			     SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
      symbol->value->obj.number = 0;
#if 0 /* No set READONLY for avoid optimize */
      symbol->flags |= SYMBOL_FLAG_READONLY;
#endif
      symbol->model = model_copy(syntax->obj.label.obj._case.symbol->model);

      symbol = value_make_op2(C_TYPE_EQEQ, symbol, syntax->obj.label.obj._case.symbol,
			      defines_list, symbols, static_area, stack_area);
      if (symbol == NULL)
	ERREXIT(1);
      symbol = symbol_sign_extension(symbol, symbols, static_area, stack_area);
      symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      syntax->obj.label.obj._case.symbol = symbol;

      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_DEFAULT:
      if (syntaxes == NULL)
	ERREXIT(1);
      syntax_create_label(syntax, NULL, 0, syntax->type, 0, NULL);
      objlist_insert_tail(syntaxes, &syntax->entry);

      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_COLON))
	ERREXIT(1);
      syntax_destroy(s);
      break;

    case C_TYPE_IF:
      if (syntaxes == NULL)
	ERREXIT(1);
      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_BRACKET_IN))
	ERREXIT(1);
      symbol = value_read(s->obj.words, defines_list, symbols,
			  static_area, stack_area,
			  NULL, 0, NULL, 0, 0);
      if (symbol == NULL)
	ERREXIT(1);
      symbol = symbol_sign_extension(symbol, symbols, static_area, stack_area);
      symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      syntax->obj._if.cond = symbol;
      syntax->obj._if.label.true =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._if.label.true == NULL)
	ERREXIT(1);
      syntax->obj._if.label.false =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._if.label.false == NULL)
	ERREXIT(1);
      syntax->obj._if.label.end =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._if.label.end == NULL)
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_ELSE:
      if (syntaxes == NULL)
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_FOR:
      if (syntaxes == NULL)
	ERREXIT(1);
      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_BRACKET_IN))
	ERREXIT(1);

      symbol = value_read(s->obj.words, defines_list, symbols,
			  static_area, stack_area,
			  NULL, C_TYPE_SEMICOLON, NULL, 0, 0);
      if (symbol != NULL) {
	symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      }
      syntax->obj._for.init = symbol;

      symbol = value_read(s->obj.words, defines_list, symbols,
			  static_area, stack_area,
			  NULL, C_TYPE_SEMICOLON, NULL, 0, 0);
      if (symbol != NULL) {
	symbol = symbol_sign_extension(symbol, symbols, static_area, stack_area);
	symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      }
      syntax->obj._for.cond = symbol;

      symbol = value_read(s->obj.words, defines_list, symbols,
			  static_area, stack_area,
			  NULL, 0, NULL, 0, 0);
      if (symbol != NULL) {
	symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      }
      syntax->obj._for.next = symbol;

      syntax->obj._for.label.start =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._for.label.start == NULL)
	ERREXIT(1);
      syntax->obj._for.label.loop =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._for.label.loop == NULL)
	ERREXIT(1);
      syntax->obj._for.label.next =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._for.label.next == NULL)
	ERREXIT(1);
      syntax->obj._for.label.end =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._for.label.end == NULL)
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_WHILE:
      if (syntaxes == NULL)
	ERREXIT(1);
      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_BRACKET_IN))
	ERREXIT(1);
      symbol = value_read(s->obj.words, defines_list, symbols,
			  static_area, stack_area,
			  NULL, 0, NULL, 0, 0);
      if (symbol == NULL)
	ERREXIT(1);
      symbol = symbol_sign_extension(symbol, symbols, static_area, stack_area);
      symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      syntax->obj._while.cond = symbol;
      syntax->obj._while.label.start =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._while.label.start == NULL)
	ERREXIT(1);
      syntax->obj._while.label.loop =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._while.label.loop == NULL)
	ERREXIT(1);
      syntax->obj._while.label.end =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._while.label.end == NULL)
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_DO:
      if (syntaxes == NULL)
	ERREXIT(1);
      syntax->obj._do.label.start =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._do.label.start == NULL)
	ERREXIT(1);
      syntax->obj._do.label.next =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._do.label.next == NULL)
	ERREXIT(1);
      syntax->obj._do.label.end =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._do.label.end == NULL)
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_SWITCH:
      if (syntaxes == NULL)
	ERREXIT(1);
      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_BRACKET_IN))
	ERREXIT(1);
      symbol = value_read(s->obj.words, defines_list, symbols,
			  static_area, stack_area,
			  NULL, 0, NULL, 0, 0);
      if (symbol == NULL)
	ERREXIT(1);
      symbol = symbol_sign_extension(symbol, symbols, static_area, stack_area);
      symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      syntax->obj._switch.value = symbol;

      symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_NONE,
			     SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NONE);
      if (symbols)
	objlist_insert_obj_tail(symbols, symbol, symbol_free);
      symbol->model = model_create(C_TYPE_LONG);
      symbol = symbol_locate(symbol, symbols, static_area, stack_area);

      s = syntax_create(C_TYPE_SYMBOL, loadline(), 0, NULL);
      s->obj.symbol = value_make_op2(C_TYPE_EQ, symbol, syntax->obj._switch.value,
				     defines_list, symbols, static_area, stack_area);
      if (s->obj.symbol->value->type != C_TYPE_EQ)
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &s->entry);

      syntax->obj._switch.value = s->obj.symbol->value->obj.op.arg[0];
      syntax->obj._switch.conds = objlist_create(NULL);
      syntax->obj._switch.label.end =
	syntax_create_label(NULL, NULL, syntax->line, C_TYPE_LABEL, 0, NULL);
      if (syntax->obj._switch.label.end == NULL)
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_GOTO:
      if (syntaxes == NULL)
	ERREXIT(1);
      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_WORD))
	ERREXIT(1);
      syntax->obj._goto.string = s->obj.word;
      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_SEMICOLON))
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_BREAK:
      if (syntaxes == NULL)
	ERREXIT(1);
      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_SEMICOLON))
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_CONTINUE:
      if (syntaxes == NULL)
	ERREXIT(1);
      s = objlist_extract_syntax_head(syntax_list);
      if ((s == NULL) || (s->type != C_TYPE_SEMICOLON))
	ERREXIT(1);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_RETURN:
      if (syntaxes == NULL)
	ERREXIT(1);
      symbol = value_read(syntax_list, defines_list, symbols,
			  static_area, stack_area,
			  NULL, C_TYPE_SEMICOLON, NULL, 0, 0);
      if (symbol != NULL) {
	if (function == NULL)
	  ERREXIT(1);
	symbol = symbol_change_model(symbol, function->model->function.retval,
				     symbols, static_area, stack_area);
	symbol = symbol_locate(symbol, symbols, static_area, stack_area);
      }
      syntax->obj._return.value = symbol;
      syntax->obj._return.function = function;
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;

    case C_TYPE_SEMICOLON:
      if (syntaxes != NULL) {
	syntax->type = C_TYPE_NOP;
	objlist_insert_tail(syntaxes, &syntax->entry);
      }
      break;

    case C_TYPE_CONST:
    case C_TYPE_RESTRICT:
    case C_TYPE_REGISTER:
    case C_TYPE_INLINE:
    case C_TYPE_VOLATILE:
    case C_TYPE_EXT_EXTENSION:
    case C_TYPE_EXT_ATTRIBUTE:
    case C_TYPE_EXT_CONST:
    case C_TYPE_EXT_RESTRICT:
    case C_TYPE_EXT_INLINE:
    case C_TYPE_EXT_ASM:
    case C_TYPE_EXT_DEAD2:
    case C_TYPE_EXT_PURE2:
    case C_TYPE_EXT_UNUSED:
    case C_TYPE_EXT_USED:
    case C_TYPE_EXT_PACKED:
    case C_TYPE_EXT_ALIGNED:
    case C_TYPE_EXT_ALIGNOF:
    case C_TYPE_EXT_SECTION:
    case C_TYPE_EXT_WEAK_SYMBOL:
    case C_TYPE_C11_ALIGNAS:
    case C_TYPE_C11_ALIGNOF:
    case C_TYPE_C11_ATOMIC:
    case C_TYPE_C11_NORETURN:
    case C_TYPE_C11_STATIC_ASSERT:
    case C_TYPE_C11_THREAD_LOCAL:
    case C_TYPE_CLANG_NONNULL:
    case C_TYPE_CLANG_NULLABLE:
    case C_TYPE_BUILTIN_EXPECT:
    case C_TYPE_BUILTIN_PREFETCH:
    case C_TYPE_BUILTIN_CONSTANT_P:
    case C_TYPE_BUILTIN_FRAME_ADDRESS:
    case C_TYPE_BUILTIN_RETURN_ADDRESS:
    case C_TYPE_SHARP:
      ERREXIT(1);
      break;

    default: /* label or value */
      if (syntaxes == NULL)
	ERREXIT(1);
      s = objlist_extract_syntax_head(syntax_list);
      if (s == NULL)
	ERREXIT(1);
      if (s->type == C_TYPE_COLON) { /* label */
	word = syntax->obj.word;
	syntax->obj.word = NULL;
	syntax_create_label(syntax, NULL, 0, C_TYPE_LABEL, 0, word);
	objlist_insert_tail(syntaxes, &syntax->entry);
	if (labels != NULL)
	  objlist_insert_obj_tail(labels, syntax, syntax_free);
	syntax_destroy(s);
	break;
      }
      objlist_insert_head(syntax_list, &s->entry);

      /* value */
      objlist_insert_head(syntax_list, &syntax->entry);
      syntax = syntax_create(C_TYPE_SYMBOL, syntax->line, 0, NULL);
      if (syntax == NULL)
	ERREXIT(1);
      syntax->obj.symbol = value_read(syntax_list, defines_list, symbols,
				      static_area, stack_area,
				      NULL, C_TYPE_SEMICOLON, NULL, 0, 0);
      objlist_insert_tail(syntaxes, &syntax->entry);
      break;
    }

    if (is_typedef)
      ERREXIT(1);
  }

  objlist_destroy(syntax_list);

  return syntaxes;
}

static objlist_t read_control(objlist_t syntax_list, objlist_t labels,
			      syntax_t label_break, syntax_t label_continue,
			      syntax_switch_t _switch);

static syntax_t read_one_sentense(objlist_t syntax_list, objlist_t labels,
				  syntax_t label_break, syntax_t label_continue,
				  syntax_switch_t _switch)
{
  syntax_t syntax, s;
  symbol_t symbol;

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

  saveline(syntax->line);

  switch (syntax->type) {
  case C_TYPE_BLOCK:
    syntax->obj.block.syntaxes = read_control(syntax->obj.block.syntaxes, labels,
					      label_break, label_continue, _switch);
    break;

  case C_TYPE_NOP:
    break;

  case C_TYPE_IF:
    s = read_one_sentense(syntax_list, labels,
			  label_break, label_continue, _switch);
    syntax->obj._if.true = s;
    s = objlist_extract_syntax_head(syntax_list);
    if (s != NULL) {
      if (s->type == C_TYPE_ELSE) {
	s = read_one_sentense(syntax_list, labels,
			      label_break, label_continue, _switch);
      } else {
	objlist_insert_head(syntax_list, &s->entry);
	s = NULL;
      }
    }
#if 0
    if (s == NULL) {
      s = syntax_create(C_TYPE_NOP, syntax->line, 0, NULL);
      if (s == NULL)
	ERREXIT(1);
    }
#endif
    syntax->obj._if.false = s;
    break;

  case C_TYPE_ELSE:
    ERREXIT(1);
    break;

  case C_TYPE_FOR:
    s = read_one_sentense(syntax_list, labels,
			  syntax->obj._for.label.end,
			  syntax->obj._for.label.next,
			  _switch);
    syntax->obj._for.loop = s;
    break;

  case C_TYPE_WHILE:
    s = read_one_sentense(syntax_list, labels,
			  syntax->obj._while.label.end,
			  syntax->obj._while.label.start,
			  _switch);
    syntax->obj._while.loop = s;
    break;

  case C_TYPE_DO:
    s = read_one_sentense(syntax_list, labels,
			  syntax->obj._do.label.end,
			  syntax->obj._do.label.next,
			  _switch);
    if (s->type != C_TYPE_BLOCK)
      ERREXIT(1);
    syntax->obj._do.loop = s;
    s = objlist_extract_syntax_head(syntax_list);
    if ((s == NULL) || (s->type != C_TYPE_WHILE))
      ERREXIT(1);
    syntax->obj._do.cond = s->obj._while.cond;
    s = objlist_extract_syntax_head(syntax_list);
    if ((s == NULL) || (s->type != C_TYPE_NOP))
      ERREXIT(1);
    break;

  case C_TYPE_SWITCH:
    s = read_one_sentense(syntax_list, labels,
			  syntax->obj._switch.label.end,
			  label_continue,
			  &syntax->obj._switch);
    if (s->type != C_TYPE_BLOCK)
      ERREXIT(1);
    syntax->obj._switch.block = s;
    break;

  case C_TYPE_GOTO:
    s = search_label(labels, syntax->obj._goto.string);
    if (s == NULL)
      ERREXIT(1);
    syntax->obj._goto.label = s;
    break;

  case C_TYPE_LABEL:
    if ((syntax->obj.label.type == C_TYPE_CASE) ||
	(syntax->obj.label.type == C_TYPE_DEFAULT)) {
      if (_switch == NULL)
	ERREXIT(1);
      objlist_insert_obj_tail(_switch->conds, syntax, syntax_free);

      if (syntax->obj.label.type == C_TYPE_CASE) {
	symbol = syntax->obj.label.obj._case.symbol;
	if ((symbol->value == NULL) || (symbol->value->type != C_TYPE_EQEQ))
	  ERREXIT(1);
	symbol->value->obj.op.arg[0] = _switch->value;
      }
    }
    break;

  case C_TYPE_BREAK:
    if (label_break == NULL)
      ERREXIT(1);
    syntax->obj._break.label = label_break;
    break;

  case C_TYPE_CONTINUE:
    if (label_continue == NULL)
      ERREXIT(1);
    syntax->obj._continue.label = label_continue;
    break;

  default:
    break;
  }
  return syntax;
}

static objlist_t read_control(objlist_t syntax_list, objlist_t labels,
			      syntax_t label_break, syntax_t label_continue,
			      syntax_switch_t _switch)
{
  objlist_t list;
  syntax_t syntax;

  if (syntax_list == NULL)
    return NULL;

  list = objlist_create(NULL);

  while ((syntax = read_one_sentense(syntax_list, labels,
				     label_break, label_continue, _switch)) != NULL) {
    objlist_insert_tail(list, &syntax->entry);
  }

  return list;
}

static objlist_t read_structure(objlist_t syntax_list, symbol_t function,
				objlist_t syntaxes, objlist_t defines_list,
				objlist_t symbols, objlist_t labels,
				area_t static_area, area_t stack_area)
{
  syntax_list = read_sentence(syntax_list, function, syntaxes, defines_list,
			      symbols, labels, static_area, stack_area);
  return syntax_list;
}

objlist_t syntax_read_structure(objlist_t syntax_list, symbol_t function,
				objlist_t syntaxes, objlist_t defines_list,
				objlist_t symbols, objlist_t labels,
				area_t static_area, area_t stack_area)
{
  syntax_list = read_structure(syntax_list, function, syntaxes, defines_list,
			       symbols, labels, static_area, stack_area);
  syntax_list = read_control(syntax_list, labels, NULL, NULL, NULL);
  return syntax_list;
}

objlist_t syntax_list_read_structure(objlist_t syntax_list, symbol_t function,
				     objlist_t syntaxes, objlist_t defines_list,
				     objlist_t symbols, objlist_t labels,
				     area_t static_area, area_t stack_area)
{
  syntax_list = read_comment(syntax_list);
  syntax_list = read_bracket(syntax_list, C_TYPE_NONE);
  syntax_list = syntax_read_structure(syntax_list, function, syntaxes, defines_list,
				      symbols, labels, static_area, stack_area);
  return syntax_list;
}

static syntax_t make_goto(syntax_t syntax, syntax_t label, int line)
{
  char *s, *string = NULL;

  if (label->type != C_TYPE_LABEL)
    goto err;

  if (syntax == NULL) {
    syntax = syntax_create(C_TYPE_GOTO, line, 0, NULL);
    if (syntax == NULL)
      goto err;
  } else {
    syntax->type = C_TYPE_GOTO;
  }

  switch (label->obj.label.type) {
  case C_TYPE_LABEL:   string = label->obj.label.obj.label.string; break;
  case C_TYPE_CASE:
  case C_TYPE_DEFAULT: string = label->obj.label.name; break;
  default:
    break;
  }

  if ((string == NULL) || ((s = strdup(string)) == NULL))
    goto err;

  syntax->obj._goto.string = s;
  syntax->obj._goto.label = label;

  return syntax;

err:
  if (syntax != NULL)
    syntax_destroy(syntax);
  return NULL;
}

static objlist_t change_control_one_sentence(syntax_t syntax);

static objlist_t change_if(syntax_t syntax)
{
  objlist_t list;
  syntax_t ifngoto, s;

  list = objlist_create(NULL);

  ifngoto = syntax_create(C_TYPE_IFNGOTO, syntax->line, 0, NULL);
  if (ifngoto == NULL)
    ERREXIT(1);
  ifngoto->obj.ifngoto.cond = syntax->obj._if.cond;
  ifngoto->obj.ifngoto.label = syntax->obj._if.label.false;
  objlist_insert_tail(list, &ifngoto->entry);

  objlist_insert_tail(list, &syntax->obj._if.label.true->entry);

  if (syntax->obj._if.true != NULL)
    objlist_insert_list_tail(list, change_control_one_sentence(syntax->obj._if.true));

  if (syntax->obj._if.false != NULL) {
    s = make_goto(NULL, syntax->obj._if.label.end, syntax->line);
    if (s == NULL)
      ERREXIT(1);
    objlist_insert_tail(list, &s->entry);
  }

  objlist_insert_tail(list, &syntax->obj._if.label.false->entry);

  if (syntax->obj._if.false != NULL)
    objlist_insert_list_tail(list, change_control_one_sentence(syntax->obj._if.false));

  objlist_insert_tail(list, &syntax->obj._if.label.end->entry);

  return list;
}

static objlist_t change_for(syntax_t syntax)
{
  objlist_t list;
  syntax_t ifngoto, s;

  list = objlist_create(NULL);

  if (syntax->obj._for.init != NULL) {
    s = syntax_create(C_TYPE_SYMBOL, syntax->line, 0, NULL);
    if (s == NULL)
      ERREXIT(1);
    s->obj.symbol = syntax->obj._for.init;
    objlist_insert_tail(list, &s->entry);
  }

  objlist_insert_tail(list, &syntax->obj._for.label.start->entry);

  if (syntax->obj._for.cond != NULL) {
    ifngoto = syntax_create(C_TYPE_IFNGOTO, syntax->line, 0, NULL);
    if (ifngoto == NULL)
      ERREXIT(1);
    ifngoto->obj.ifngoto.cond = syntax->obj._for.cond;
    ifngoto->obj.ifngoto.label = syntax->obj._for.label.end;
    objlist_insert_tail(list, &ifngoto->entry);
  }

  objlist_insert_tail(list, &syntax->obj._for.label.loop->entry);

  objlist_insert_list_tail(list, change_control_one_sentence(syntax->obj._for.loop));

  objlist_insert_tail(list, &syntax->obj._for.label.next->entry);

  if (syntax->obj._for.next != NULL) {
    s = syntax_create(C_TYPE_SYMBOL, syntax->line, 0, NULL);
    if (s == NULL)
      ERREXIT(1);
    s->obj.symbol = syntax->obj._for.next;
    objlist_insert_tail(list, &s->entry);
  }

  s = make_goto(NULL, syntax->obj._for.label.start, syntax->line);
  if (s == NULL)
    ERREXIT(1);
  objlist_insert_tail(list, &s->entry);

  objlist_insert_tail(list, &syntax->obj._for.label.end->entry);

  return list;
}

static objlist_t change_while(syntax_t syntax)
{
  objlist_t list;
  syntax_t ifngoto, s;

  list = objlist_create(NULL);

  objlist_insert_tail(list, &syntax->obj._while.label.start->entry);

  ifngoto = syntax_create(C_TYPE_IFNGOTO, syntax->line, 0, NULL);
  if (ifngoto == NULL)
    ERREXIT(1);
  ifngoto->obj.ifngoto.cond = syntax->obj._while.cond;
  ifngoto->obj.ifngoto.label = syntax->obj._while.label.end;
  objlist_insert_tail(list, &ifngoto->entry);

  objlist_insert_tail(list, &syntax->obj._while.label.loop->entry);

  objlist_insert_list_tail(list, change_control_one_sentence(syntax->obj._while.loop));

  s = make_goto(NULL, syntax->obj._while.label.start, syntax->line);
  if (s == NULL)
    ERREXIT(1);
  objlist_insert_tail(list, &s->entry);

  objlist_insert_tail(list, &syntax->obj._while.label.end->entry);

  return list;
}

static objlist_t change_do(syntax_t syntax)
{
  objlist_t list;
  syntax_t ifgoto;

  list = objlist_create(NULL);

  objlist_insert_tail(list, &syntax->obj._do.label.start->entry);

  objlist_insert_list_tail(list, change_control_one_sentence(syntax->obj._do.loop));

  objlist_insert_tail(list, &syntax->obj._do.label.next->entry);

  ifgoto = syntax_create(C_TYPE_IFGOTO, syntax->line, 0, NULL);
  if (ifgoto == NULL)
    ERREXIT(1);
  ifgoto->obj.ifgoto.cond = syntax->obj._do.cond;
  ifgoto->obj.ifgoto.label = syntax->obj._do.label.start;
  objlist_insert_tail(list, &ifgoto->entry);

  objlist_insert_tail(list, &syntax->obj._do.label.end->entry);

  return list;
}

static objlist_t change_switch(syntax_t syntax)
{
  objlist_t list;
  objentry_t entry;
  syntax_t ifgoto, s, _default = NULL;

  list = objlist_create(NULL);

  for (entry = objlist_get_head(syntax->obj._switch.conds);
       !objlist_is_end(syntax->obj._switch.conds, entry);
       entry = objentry_get_next(entry)) {
    s = objentry_get_syntax(entry);

    if (s->obj.label.type == C_TYPE_DEFAULT) {
      _default = s;
      continue;
    }

    ifgoto = syntax_create(C_TYPE_IFGOTO, syntax->line, 0, NULL);
    if (ifgoto == NULL)
      ERREXIT(1);
    ifgoto->obj.ifgoto.cond = s->obj.label.obj._case.symbol;
    ifgoto->obj.ifgoto.label = s;
    objlist_insert_tail(list, &ifgoto->entry);
  }

  if (_default == NULL)
    _default = syntax->obj._switch.label.end;

  s = make_goto(NULL, _default, syntax->line);
  if (s == NULL)
    ERREXIT(1);
  objlist_insert_tail(list, &s->entry);

  objlist_insert_list_tail(list, change_control_one_sentence(syntax->obj._switch.block));

  objlist_insert_tail(list, &syntax->obj._switch.label.end->entry);

  return list;
}

static objlist_t change_control_one_sentence(syntax_t syntax)
{
  objlist_t list;

  list = objlist_create(NULL);

  saveline(syntax->line);

  switch (syntax->type) {
  case C_TYPE_BLOCK:
    syntax_list_read_control(&syntax->obj.block);
    objlist_insert_tail(list, &syntax->entry);
    break;

  case C_TYPE_IF:
    objlist_insert_list_tail(list, change_if(syntax));
    break;

  case C_TYPE_FOR:
    objlist_insert_list_tail(list, change_for(syntax));
    break;

  case C_TYPE_WHILE:
    objlist_insert_list_tail(list, change_while(syntax));
    break;

  case C_TYPE_DO:
    objlist_insert_list_tail(list, change_do(syntax));
    break;

  case C_TYPE_SWITCH:
    objlist_insert_list_tail(list, change_switch(syntax));
    break;

  case C_TYPE_GOTO:
    objlist_insert_tail(list, &syntax->entry);
    break;

  case C_TYPE_BREAK:
    syntax = make_goto(syntax, syntax->obj._break.label, syntax->line);
    objlist_insert_tail(list, &syntax->entry);
    break;

  case C_TYPE_CONTINUE:
    syntax = make_goto(syntax, syntax->obj._continue.label, syntax->line);
    objlist_insert_tail(list, &syntax->entry);
    break;

  case C_TYPE_LABEL:
    if ((syntax->obj.label.type == C_TYPE_CASE) ||
	(syntax->obj.label.type == C_TYPE_DEFAULT))
      syntax = syntax_create_label(syntax, syntax->obj.label.name, 0,
				   C_TYPE_LABEL, 0, NULL);
    objlist_insert_tail(list, &syntax->entry);
    break;

  default:
    objlist_insert_tail(list, &syntax->entry);
    break;
  }

  return list;
}

static objlist_t change_control(objlist_t syntax_list)
{
  objlist_t list;
  syntax_t syntax;

  list = objlist_create(NULL);

  while ((syntax = objlist_extract_syntax_head(syntax_list)) != NULL) {
    objlist_insert_list_tail(list, change_control_one_sentence(syntax));
  }

  objlist_destroy(syntax_list);

  return list;
}

int syntax_list_read_control(syntax_block_t block)
{
  objentry_t entry;
  syntax_t syntax;
  symbol_t symbol;

  if (block->defines != NULL) {
    for (entry = objlist_get_head(&block->defines->symbol_list);
	 !objlist_is_end(&block->defines->symbol_list, entry);
	 entry = objentry_get_next(entry)) {
      symbol = objentry_get_obj(entry);
      if (symbol->flags & SYMBOL_FLAG_FUNCBLOCK) {
	syntax = symbol->value->obj.block.syntax;
	if (syntax->type == C_TYPE_BLOCK)
	  syntax_list_read_control(&syntax->obj.block);
      }
    }
  }

  block->syntaxes = change_control(block->syntaxes);

  return 0;
}

int syntax_custom_normalize(syntax_t syntax, objlist_t syntax_list, objlist_t defines_list)
{
  saveline(syntax->line);
  switch (syntax->type) {
  case C_TYPE_STRUCT:
    if (syntax->obj.custom._struct == NULL)
      struct_normalize(syntax, syntax_list, defines_list);
    break;
  case C_TYPE_UNION:
    if (syntax->obj.custom._union == NULL)
      union_normalize(syntax, syntax_list, defines_list);
    break;
  case C_TYPE_ENUM:
    if (syntax->obj.custom._enum == NULL)
      enum_normalize(syntax, syntax_list, defines_list);
    break;
  default:
    return 0;
  }
  if (syntax->obj.custom.p == NULL)
    ERREXIT(1);
  return 0;
}
