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

/*****************************************************************
 * value_t
 */

value_t value_destroy(value_t value)
{
  int i;

  if (value != NULL) {
    switch (value->type) {
    case C_TYPE_NUMBER:
      break;
    case C_TYPE_STRING:
      if (value->obj.string.s != NULL)
	free(value->obj.string.s);
      break;
    case C_TYPE_ARRAY:
      if (value->obj.array.values)
	objlist_destroy(value->obj.array.values);
      break;
    case C_TYPE_VARIABLE:
     break;
    case C_TYPE_FUNCTION:
      for (i = 0; i < value->obj.function.args.number; i++) {
	if (value->obj.function.args.arg[i])
	  symbol_destroy(value->obj.function.args.arg[i]);
      }
      break;
    case C_TYPE_BLOCK:
      syntax_destroy(value->obj.block.syntax);
      break;
    default:
      for (i = 0; i < VALUE_ARG_NUM; i++) {
	if (value->obj.op.arg[i])
	  symbol_destroy(value->obj.op.arg[i]);
      }
      break;
    }

    memset(value, 0, sizeof(*value));
    free(value);
  }

  return NULL;
}

value_t value_create(c_type_t type)
{
  value_t value;

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

  value->type = type;

  return value;

err:
  if (value != NULL)
    value_destroy(value);
  return NULL;
}

static void value_print_array(value_t value, int indent)
{
  objentry_t entry;
  symbol_t symbol;

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

void value_print_simple(value_t value, int indent)
{
  int i;

  indent_print(indent);

  switch (value->type) {
  case C_TYPE_NUMBER:
    printf("%ld (%s)\n", value->obj.number, type_get_word(value->type));
    break;
  case C_TYPE_STRING:
    printf("%s (%s)\n", value->obj.string.s, type_get_word(value->type));
    break;
  case C_TYPE_ARRAY:
    printf("%d (%s)\n", value->obj.array.number, type_get_word(value->type));
    break;
  case C_TYPE_LABEL:
    symbol_print_name(value->obj.label.symbol);
    printf(" (%s:0x%x)\n", type_get_word(value->type), value->obj.label.symbol->id);
    break;
  case C_TYPE_VARIABLE:
    symbol_print_name(value->obj.variable.symbol);
    printf(" (%s:0x%x)\n", type_get_word(value->type), value->obj.variable.symbol->id);
    break;
  case C_TYPE_FUNCTION:
    symbol_print_name(value->obj.function.symbol);
    printf(" (%s:0x%x) (", type_get_word(value->type), value->obj.function.symbol->id);
    for (i = 0; i < value->obj.function.args.number; i++) {
      printf(" ");
      symbol_print_name(value->obj.function.args.arg[i]);
    }
    printf(" )\n");
    break;
  case C_TYPE_ARG:
    printf("%d (%s)\n", value->obj.arg.number, type_get_word(value->type));
    break;
  case C_TYPE_BLOCK:
    printf("%s\n", type_get_word(value->type));
    break;
  default:
    printf("%s (", type_get_word(value->type));
    for (i = 0; i < VALUE_ARG_NUM; i++) {
      if (value->obj.op.arg[i] != NULL) {
	printf(" ");
	symbol_print_name(value->obj.op.arg[i]);
      }
    }
    printf(" )\n");
    break;
  }
}

void value_print(value_t value, int indent)
{
  int i;

  value_print_simple(value, indent);

  switch (value->type) {
  case C_TYPE_NUMBER:
  case C_TYPE_STRING:
    break;
  case C_TYPE_ARRAY:
    if (value->obj.array.values != NULL)
      value_print_array(value, indent + 1);
    break;
  case C_TYPE_LABEL:
  case C_TYPE_VARIABLE:
    break;
  case C_TYPE_FUNCTION:
    indent++;
    symbol_print(value->obj.function.symbol, indent);
    indent_print(indent);
    printf("ARGS\n");
    for (i = 0; i < value->obj.function.args.number; i++) {
      symbol_print_simple(value->obj.function.args.arg[i], indent + 1);
    }
    break;
  case C_TYPE_ARG:
    break;
  case C_TYPE_BLOCK:
    if (value->obj.block.syntax != NULL)
      syntax_print(value->obj.block.syntax, indent + 1);
    break;
  default:
    break;
  }
}

void value_print_args(value_t value, int indent)
{
  int i;

  value_print_simple(value, indent);

  switch (value->type) {
  case C_TYPE_NUMBER:
  case C_TYPE_STRING:
  case C_TYPE_ARRAY:
  case C_TYPE_LABEL:
  case C_TYPE_VARIABLE:
    break;
  case C_TYPE_FUNCTION:
    for (i = 0; i < value->obj.function.args.number; i++) {
      symbol_print_value_simple(value->obj.function.args.arg[i], indent + 1);
    }
    break;
  case C_TYPE_ARG:
  case C_TYPE_BLOCK:
    break;
  default:
    for (i = 0; i < VALUE_ARG_NUM; i++) {
      if (value->obj.op.arg[i] != NULL)
	symbol_print_value_simple(value->obj.op.arg[i], indent + 1);
    }
    break;
  }
}

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

static int matching_model(syntax_t syntax0, syntax_t syntax1,
			  objlist_t symbols,
			  area_t static_area, area_t stack_area)
{
  int level0, level1;

  if ((syntax0->obj.symbol->model == NULL) ||
      (syntax1->obj.symbol->model == NULL))
    return -1;

  level0 = model_get_level(syntax0->obj.symbol->model);
  level1 = model_get_level(syntax1->obj.symbol->model);

  if (level0 != level1) {
    if (level0 > level1)
      syntax1->obj.symbol = symbol_change_model(syntax1->obj.symbol,
						syntax0->obj.symbol->model,
						symbols, static_area, stack_area);
    else
      syntax0->obj.symbol = symbol_change_model(syntax0->obj.symbol,
						syntax1->obj.symbol->model,
						symbols, static_area, stack_area);
  }

  return 0;
}

static symbol_t _value_read(objlist_t words,
			    objlist_t defines_list, objlist_t symbols,
			    area_t static_area, area_t stack_area);

static int op1_const(c_type_t type, symbol_t symbol, symbol_t nsymbol)
{
  long n, nn;
  unsigned long un, unn;
  model_t model;

  if (!symbol_is_const_number(nsymbol))
    return 0;

  nn = nsymbol->value->obj.number;

  model = model_create(C_TYPE_LONG);
  nn = model_change_number(model, nsymbol->model, nn);

  if (nsymbol->model->flags & MODEL_FLAG_UNSIGNED) {
    unn = nn;
    switch (type) {
    case C_TYPE_PLUS:  un =  unn; break;
    case C_TYPE_MINUS: un = -unn; break;
    default:
      return 0;
    }
    n = un;
  } else {
    switch (type) {
    case C_TYPE_PLUS:  n =  nn; break;
    case C_TYPE_MINUS: n = -nn; break;
    default:
      return 0;
    }
  }

  symbol->value->type = C_TYPE_NUMBER;
  symbol->value->obj.number = model_change_number(symbol->model, model, n);
  symbol->flags |= SYMBOL_FLAG_READONLY;
  symbol->scope = C_TYPE_STATIC;

  model_destroy(model);

  return 1;
}

static int op1(c_type_t type, syntax_t syntax,
	       objlist_t words, objlist_t defines_list, objlist_t symbols,
	       area_t static_area, area_t stack_area)
{
  objentry_t pentry, nentry;
  syntax_t psyntax = NULL, nsyntax = NULL;
  symbol_t symbol, s = NULL;

  saveline(syntax->line);

  pentry = objentry_get_prev(&syntax->entry);
  nentry = objentry_get_next(&syntax->entry);

  if (!objlist_is_end(words, pentry))
    psyntax = objentry_get_syntax(pentry);
  if (!objlist_is_end(words, nentry))
    nsyntax = objentry_get_syntax(nentry);

  switch (type) {
  case C_TYPE_AND: /* &a */
  case C_TYPE_MUL: /* *p */
  case C_TYPE_ADD: /* +i */
  case C_TYPE_SUB: /* -i */
    if ((psyntax != NULL) &&
	((psyntax->type == C_TYPE_SYMBOL) ||
	 (psyntax->type == C_TYPE_INC) || (psyntax->type == C_TYPE_DEC))) {
      return 0;
    }
    break;
  default:
    break;
  }

  switch (type) {
  case C_TYPE_AND: type = C_TYPE_ADDRESS; break;
  case C_TYPE_MUL: type = C_TYPE_POINTER; break;
  case C_TYPE_ADD: type = C_TYPE_PLUS;    break;
  case C_TYPE_SUB: type = C_TYPE_MINUS;   break;
  default: break;
  }

  switch (type) {
  case C_TYPE_INC: /* ++ */
  case C_TYPE_DEC: /* -- */
    if ((nsyntax != NULL) && (nsyntax->type == C_TYPE_SYMBOL)) { /* ++x/--x */
      if (!symbol_is_writable(nsyntax->obj.symbol))
	ERREXIT(1);
      /* fall through */
    } else if ((psyntax != NULL) && (psyntax->type == C_TYPE_SYMBOL)) { /* x++/x-- */
      if (!symbol_is_writable(psyntax->obj.symbol))
	ERREXIT(1);
      objlist_extract(words, pentry);
      s = psyntax->obj.symbol;
      switch (type) {
      case C_TYPE_INC: type = C_TYPE_INCA; break;
      case C_TYPE_DEC: type = C_TYPE_DECA; break;
      default: ERREXIT(1);
      }
      break;
    } else {
      ERREXIT(1);
    }
    /* fall through */
  default:
    objlist_extract(words, nentry);
    s = nsyntax->obj.symbol;
    break;
  }

  switch (type) {
  case C_TYPE_ADDRESS:
    if (s->type == C_TYPE_LABEL) {
      symbol = s;
      goto ret;
    }
    break;
  case C_TYPE_POINTER:
    if ((s->model != NULL) && (s->model->type == C_TYPE_FUNCTION) &&
	(s->model->modifiers.number == 1)) {
      symbol = s;
      goto ret;
    }
    break;
  default:
    break;
  }

  symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_NONE,
			 SYMBOL_NAME_AUTO, NULL, 0, type);
  if (symbol == NULL)
    ERREXIT(1);
  if (symbols)
    objlist_insert_obj_tail(symbols, symbol, symbol_free);

  switch (type) {
  case C_TYPE_PLUS:  /* +i */
  case C_TYPE_MINUS: /* -i */
  case C_TYPE_INV:   /* ~i */
    s = symbol_sign_extension(s, symbols, static_area, stack_area);
    /* fall through */
  case C_TYPE_INC:
  case C_TYPE_DEC:
  case C_TYPE_INCA:
  case C_TYPE_DECA:
    symbol->model = model_copy(s->model);
    break;

  case C_TYPE_NOT:   /* !i */
    s = symbol_sign_extension(s, symbols, static_area, stack_area);
    symbol->model = model_create(C_TYPE_INT);
    break;

  case C_TYPE_ADDRESS: /* &a */
    symbol->model = model_copy(s->model);
    if (model_add_modifier_pointer(symbol->model) < 0)
      ERREXIT(1);
    break;

  case C_TYPE_POINTER: /* *p */
    symbol->model = model_copy(s->model);
    if (model_del_modifier(symbol->model) < 0)
      ERREXIT(1);
#if 0
    model_array_to_pointer(symbol->model);
#else
    if (model_is_array(symbol->model))
      symbol->type = C_TYPE_LABEL;
#endif
    symbol->location.type = C_TYPE_POINTER;
    symbol->location.base.symbol = s;
    symbol->location.offset = 0;
    symbol->flags |= SYMBOL_FLAG_LOCATED;
    break;

  default:
    ERREXIT(1);
    return -1;
  }

  model_array_to_pointer(symbol->model);

  switch (type) {
  case C_TYPE_PLUS:
  case C_TYPE_MINUS:
    if (op1_const(type, symbol, s))
      goto ret;
    break;
  default:
    break;
  }

  symbol->flags |= SYMBOL_FLAG_TEMPORARY;

  switch (type) {
  case C_TYPE_POINTER:
  case C_TYPE_PLUS:
  case C_TYPE_MINUS:
  case C_TYPE_INV:
  case C_TYPE_NOT:
  case C_TYPE_INC:
  case C_TYPE_DEC:
  case C_TYPE_INCA:
  case C_TYPE_DECA:
    s = symbol_locate(s, symbols, static_area, stack_area);
    break;
  default:
    break;
  }

  symbol->value->obj.op.arg[0] = s;

ret:
  syntax->type = C_TYPE_SYMBOL;
  syntax->obj.symbol = symbol;

  return 0;
}

static int op2_const(c_type_t type, symbol_t symbol,
		     symbol_t psymbol, symbol_t nsymbol)
{
  long n, pn, nn;
  unsigned long un, upn, unn;
  model_t model;

  if (!symbol_is_const_number(psymbol) || !symbol_is_const_number(nsymbol))
    return 0;

  n = 0;
  pn = psymbol->value->obj.number;
  nn = nsymbol->value->obj.number;

  model = model_create(C_TYPE_LONG);
  pn = model_change_number(model, psymbol->model, pn);
  nn = model_change_number(model, nsymbol->model, nn);

  if ((type == C_TYPE_DIV) || (type == C_TYPE_MOD)) {
    if (nn == 0)
      ERREXIT(1);
  }

  if (model_is_address(psymbol->model) || model_is_address(nsymbol->model)) {
    if (!model_is_address(nsymbol->model)) {
      if (type == C_TYPE_ADD)
	n = pn + (nn * model_get_pointer_size(psymbol->model));
      else if (type == C_TYPE_SUB)
	n = pn - (nn * model_get_pointer_size(psymbol->model));
      else
	ERREXIT(1);
    } else if (!model_is_address(psymbol->model)) {
      if (type == C_TYPE_ADD)
	n = (pn * model_get_pointer_size(nsymbol->model)) + nn;
      else
	ERREXIT(1);
    } else {
      if (type == C_TYPE_SUB) {
	n = (pn - nn) / model_get_pointer_size(psymbol->model);
      } else
	ERREXIT(1);
    }
  } else {
    if (psymbol->model->flags & MODEL_FLAG_UNSIGNED) {
      upn = pn;
      unn = nn;
      switch (type) {
      case C_TYPE_MUL:    un = upn *  unn; break;
      case C_TYPE_DIV:    un = upn /  unn; break;
      case C_TYPE_MOD:    un = upn %  unn; break;
      case C_TYPE_ADD:    un = upn +  unn; break;
      case C_TYPE_SUB:    un = upn -  unn; break;
      case C_TYPE_RSHIFT: un = upn >> unn; break;
      case C_TYPE_LSHIFT: un = upn << unn; break;
      case C_TYPE_GTEQ:   un = upn >= unn; break;
      case C_TYPE_LTEQ:   un = upn <= unn; break;
      case C_TYPE_GT:     un = upn >  unn; break;
      case C_TYPE_LT:     un = upn <  unn; break;
      case C_TYPE_EQEQ:   un = upn == unn; break;
      case C_TYPE_NEQ:    un = upn != unn; break;
      case C_TYPE_AND:    un = upn &  unn; break;
      case C_TYPE_XOR:    un = upn ^  unn; break;
      case C_TYPE_OR:     un = upn |  unn; break;
      case C_TYPE_ANDAND: un = upn && unn; break;
      case C_TYPE_OROR:   un = upn || unn; break;
      default:
	return 0;
      }
      n = un;
    } else {
      switch (type) {
      case C_TYPE_MUL:    n = pn *  nn; break;
      case C_TYPE_DIV:    n = pn /  nn; break;
      case C_TYPE_MOD:    n = pn %  nn; break;
      case C_TYPE_ADD:    n = pn +  nn; break;
      case C_TYPE_SUB:    n = pn -  nn; break;
      case C_TYPE_RSHIFT: n = pn >> nn; break;
      case C_TYPE_LSHIFT: n = pn << nn; break;
      case C_TYPE_GTEQ:   n = pn >= nn; break;
      case C_TYPE_LTEQ:   n = pn <= nn; break;
      case C_TYPE_GT:     n = pn >  nn; break;
      case C_TYPE_LT:     n = pn <  nn; break;
      case C_TYPE_EQEQ:   n = pn == nn; break;
      case C_TYPE_NEQ:    n = pn != nn; break;
      case C_TYPE_AND:    n = pn &  nn; break;
      case C_TYPE_XOR:    n = pn ^  nn; break;
      case C_TYPE_OR:     n = pn |  nn; break;
      case C_TYPE_ANDAND: n = pn && nn; break;
      case C_TYPE_OROR:   n = pn || nn; break;
      default:
	return 0;
      }
    }
  }

  symbol->value->type = C_TYPE_NUMBER;
  symbol->value->obj.number = model_change_number(symbol->model, model, n);
  symbol->flags |= SYMBOL_FLAG_READONLY;
  symbol->scope = C_TYPE_STATIC;

  model_destroy(model);

  return 1;
}

static int op2(c_type_t type, syntax_t syntax,
	       objlist_t words, objlist_t defines_list, objlist_t symbols,
	       area_t static_area, area_t stack_area)
{
  syntax_t psyntax, nsyntax;
  symbol_t symbol, s;

  saveline(syntax->line);

  psyntax = objlist_extract_syntax_prev(words, syntax);
  nsyntax = objlist_extract_syntax_next(words, syntax);

  if ((psyntax == NULL) || (nsyntax == NULL) ||
      (psyntax->type != C_TYPE_SYMBOL) || (nsyntax->type != C_TYPE_SYMBOL))
    ERREXIT(1);

  symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_NONE,
			 SYMBOL_NAME_AUTO, NULL, 0, type);
  if (symbol == NULL)
    ERREXIT(1);
  if (symbols)
    objlist_insert_obj_tail(symbols, symbol, symbol_free);

  switch (type) {
  case C_TYPE_EQ:
  case C_TYPE_ADDEQ:
  case C_TYPE_SUBEQ:
  case C_TYPE_MULEQ:
  case C_TYPE_DIVEQ:
  case C_TYPE_MODEQ:
  case C_TYPE_ANDEQ:
  case C_TYPE_XOREQ:
  case C_TYPE_OREQ:
  case C_TYPE_RSHIFTEQ:
  case C_TYPE_LSHIFTEQ:
    if (!symbol_is_writable(psyntax->obj.symbol))
      ERREXIT(1);
    break;

  default:
    break;
  }

  switch (type) {
  case C_TYPE_ADD:
  case C_TYPE_SUB:
    if (model_is_address(psyntax->obj.symbol->model) ||
	model_is_address(nsyntax->obj.symbol->model)) {
      s = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_STATIC,
			SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
      if (s == NULL)
	ERREXIT(1);
      if (symbols)
	objlist_insert_obj_tail(symbols, s, symbol_free);
      s->flags |= SYMBOL_FLAG_READONLY;
      s->model = model_create(C_TYPE_INT);

      /*
       * Shift calculation is not used.
       * To avoid a recursive calling of the left shift at the pointer
       * calculation in the buildin left shift function.
       * (__builtin_lshlsi3()/__builtin_mask32[]),
       */
#define NOUSE_SHIFT_CALC_POINTER

      if (!model_is_address(nsyntax->obj.symbol->model)) {
	s->value->obj.number = model_get_pointer_size(psyntax->obj.symbol->model);
	switch (s->value->obj.number) {
	case  1: break;
#ifndef NOUSE_SHIFT_CALC_POINTER
	case  2: s->value->obj.number = 1; goto nshift;
	case  4: s->value->obj.number = 2; goto nshift;
	case  8: s->value->obj.number = 3; goto nshift;
	case 16: s->value->obj.number = 4; goto nshift;
	case 32: s->value->obj.number = 5; goto nshift;
	case 64: s->value->obj.number = 6; goto nshift;
	nshift:
	  nsyntax->obj.symbol = 
	    value_make_op2(C_TYPE_LSHIFT, nsyntax->obj.symbol, s,
			   defines_list, symbols, static_area, stack_area);
	  break;
#endif
	default:
	  nsyntax->obj.symbol = 
	    value_make_op2(C_TYPE_MUL, nsyntax->obj.symbol, s,
			   defines_list, symbols, static_area, stack_area);
	  break;
	}
	symbol->model = model_copy(psyntax->obj.symbol->model);
	break;
      } else if (!model_is_address(psyntax->obj.symbol->model)) {
	if (type == C_TYPE_SUB)
	  ERREXIT(1);
	s->value->obj.number = model_get_pointer_size(nsyntax->obj.symbol->model);
	switch (s->value->obj.number) {
	case  1: break;
#ifndef NOUSE_SHIFT_CALC_POINTER
	case  2: s->value->obj.number = 1; goto pshift;
	case  4: s->value->obj.number = 2; goto pshift;
	case  8: s->value->obj.number = 3; goto pshift;
	case 16: s->value->obj.number = 4; goto pshift;
	case 32: s->value->obj.number = 5; goto pshift;
	case 64: s->value->obj.number = 6; goto pshift;
	pshift:
	  psyntax->obj.symbol = 
	    value_make_op2(C_TYPE_LSHIFT, psyntax->obj.symbol, s,
			   defines_list, symbols, static_area, stack_area);
	  break;
#endif
	default:
	  psyntax->obj.symbol = 
	    value_make_op2(C_TYPE_MUL, psyntax->obj.symbol, s,
			   defines_list, symbols, static_area, stack_area);
	  break;
	}
	symbol->model = model_copy(nsyntax->obj.symbol->model);
	break;
      } else {
	if (type == C_TYPE_SUB) {
	  if (model_get_pointer_size(psyntax->obj.symbol->model) != model_get_pointer_size(nsyntax->obj.symbol->model))
	    ERREXIT(1);
	  s->value->obj.number = model_get_pointer_size(psyntax->obj.symbol->model);
	  psyntax->obj.symbol =
	    symbol_change_model_type(psyntax->obj.symbol, C_TYPE_LONG,
				     symbols, static_area, stack_area);
	  nsyntax->obj.symbol =
	    symbol_change_model_type(nsyntax->obj.symbol, C_TYPE_LONG,
				     symbols, static_area, stack_area);
	  psyntax->obj.symbol = 
	    value_make_op2(type, psyntax->obj.symbol, nsyntax->obj.symbol,
			   defines_list, symbols, static_area, stack_area);
	  nsyntax->obj.symbol = s;
	  type = symbol->value->type = C_TYPE_DIV;
	  symbol->model = model_create(C_TYPE_LONG);
	  break;
	} else
	  ERREXIT(1);
      }
    }
    /* fall through */

  case C_TYPE_MUL:
  case C_TYPE_DIV:
  case C_TYPE_MOD:
  case C_TYPE_AND:
  case C_TYPE_XOR:
  case C_TYPE_OR:
  case C_TYPE_RSHIFT:
  case C_TYPE_LSHIFT:
    if (model_is_address(psyntax->obj.symbol->model) ||
	model_is_address(nsyntax->obj.symbol->model))
      ERREXIT(1);
    if ((type == C_TYPE_RSHIFT) || (type == C_TYPE_LSHIFT)) {
      psyntax->obj.symbol = symbol_sign_extension(psyntax->obj.symbol, symbols, static_area, stack_area);
      nsyntax->obj.symbol = symbol_sign_extension(nsyntax->obj.symbol, symbols, static_area, stack_area);
      symbol->model = model_copy(psyntax->obj.symbol->model);
      break;
    } else if ((type == C_TYPE_DIV) || (type == C_TYPE_MOD)) {
      if ((psyntax->obj.symbol->model->flags & MODEL_FLAG_UNSIGNED) &&
	  (nsyntax->obj.symbol->model->flags & MODEL_FLAG_UNSIGNED)) {
	if (matching_model(psyntax, nsyntax, symbols, static_area, stack_area) < 0)
	  ERREXIT(1);
	symbol->model = model_copy(psyntax->obj.symbol->model);
	break;
      }
    }
    psyntax->obj.symbol = symbol_sign_extension(psyntax->obj.symbol, symbols, static_area, stack_area);
    nsyntax->obj.symbol = symbol_sign_extension(nsyntax->obj.symbol, symbols, static_area, stack_area);
    if (matching_model(psyntax, nsyntax, symbols, static_area, stack_area) < 0)
      ERREXIT(1);
    symbol->model = model_copy(psyntax->obj.symbol->model);
    break;

  case C_TYPE_GTEQ:
  case C_TYPE_LTEQ:
  case C_TYPE_GT:
  case C_TYPE_LT:
  case C_TYPE_EQEQ:
  case C_TYPE_NEQ:
    psyntax->obj.symbol = symbol_sign_extension(psyntax->obj.symbol, symbols, static_area, stack_area);
    nsyntax->obj.symbol = symbol_sign_extension(nsyntax->obj.symbol, symbols, static_area, stack_area);
    if (matching_model(psyntax, nsyntax, symbols, static_area, stack_area) < 0)
      ERREXIT(1);
    symbol->model = model_create(C_TYPE_INT);
    break;

  case C_TYPE_ANDAND:
  case C_TYPE_OROR:
    psyntax->obj.symbol = symbol_sign_extension(psyntax->obj.symbol, symbols, static_area, stack_area);
    nsyntax->obj.symbol = symbol_sign_extension(nsyntax->obj.symbol, symbols, static_area, stack_area);
    symbol->model = model_create(C_TYPE_INT);
    break;

  case C_TYPE_ADDEQ:
  case C_TYPE_SUBEQ:
    if (model_is_address(psyntax->obj.symbol->model)) {
      if (model_is_address(nsyntax->obj.symbol->model))
	ERREXIT(1);
      s = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_STATIC,
			SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
      if (s == NULL)
	ERREXIT(1);
      if (symbols)
	objlist_insert_obj_tail(symbols, s, symbol_free);
      s->flags |= SYMBOL_FLAG_READONLY;
      s->model = model_create(C_TYPE_INT);

      s->value->obj.number = model_get_pointer_size(psyntax->obj.symbol->model);
      nsyntax->obj.symbol =
	value_make_op2(C_TYPE_MUL, nsyntax->obj.symbol, s,
		       defines_list, symbols, static_area, stack_area);
    }
    /* fall through */

  case C_TYPE_EQ:
  case C_TYPE_MULEQ:
  case C_TYPE_DIVEQ:
  case C_TYPE_MODEQ:
  case C_TYPE_ANDEQ:
  case C_TYPE_XOREQ:
  case C_TYPE_OREQ:
  case C_TYPE_RSHIFTEQ:
  case C_TYPE_LSHIFTEQ:
    if ((type == C_TYPE_RSHIFTEQ) || (type == C_TYPE_LSHIFTEQ)) {
      nsyntax->obj.symbol = symbol_sign_extension(nsyntax->obj.symbol, symbols, static_area, stack_area);
    } else {
      nsyntax->obj.symbol = symbol_change_model(nsyntax->obj.symbol, psyntax->obj.symbol->model, symbols, static_area, stack_area);
    }
    symbol->model = model_copy(psyntax->obj.symbol->model);
    break;

  case C_TYPE_COMMA:
    symbol->model = model_copy(nsyntax->obj.symbol->model);
    break;

  default:
    ERREXIT(1);
    break;
  }

  model_array_to_pointer(symbol->model);

  if (op2_const(type, symbol, psyntax->obj.symbol, nsyntax->obj.symbol))
    goto ret;

  symbol->flags |= SYMBOL_FLAG_TEMPORARY;

  nsyntax->obj.symbol = symbol_locate(nsyntax->obj.symbol, symbols,
				      static_area, stack_area);

  switch (type) {
  case C_TYPE_ADD:
  case C_TYPE_SUB:
  case C_TYPE_MUL:
  case C_TYPE_DIV:
  case C_TYPE_MOD:
  case C_TYPE_AND:
  case C_TYPE_XOR:
  case C_TYPE_OR:
  case C_TYPE_RSHIFT:
  case C_TYPE_LSHIFT:
  case C_TYPE_GTEQ:
  case C_TYPE_LTEQ:
  case C_TYPE_GT:
  case C_TYPE_LT:
  case C_TYPE_EQEQ:
  case C_TYPE_NEQ:
  case C_TYPE_ANDAND:
  case C_TYPE_OROR:
  case C_TYPE_COMMA:
    psyntax->obj.symbol = symbol_locate(psyntax->obj.symbol, symbols,
					static_area, stack_area);
    break;

  default:
    break;
  }

  symbol->value->obj.op.arg[0] = psyntax->obj.symbol;
  symbol->value->obj.op.arg[1] = nsyntax->obj.symbol;

ret:
  switch (type) {
  case C_TYPE_DIV:
  case C_TYPE_MOD:
    symbol = symbol_sign_extension(symbol, symbols, static_area, stack_area);
    break;
  default:
    break;
  }

  syntax->type = C_TYPE_SYMBOL;
  syntax->obj.symbol = symbol;

  return 0;
}

static int op3(c_type_t type, syntax_t syntax,
	       objlist_t words, objlist_t defines_list, objlist_t symbols,
	       area_t static_area, area_t stack_area)
{
  syntax_t syntax0, syntax1, syntax2;
  symbol_t symbol;

  saveline(syntax->line);

  syntax0 = objlist_extract_syntax_prev(words, syntax);
  syntax1 = objlist_extract_syntax_next(words, syntax);
  syntax2 = objlist_extract_syntax_next(words, syntax);
  if ((syntax0 == NULL) || (syntax1 == NULL) || (syntax2 == NULL) ||
      (syntax0->type != C_TYPE_SYMBOL) ||
      (syntax1->type != C_TYPE_SYMBOL) ||
      (syntax2->type != C_TYPE_SYMBOL))
    return -1;

  if (type == C_TYPE_QUESTION) {
    syntax0->obj.symbol = symbol_sign_extension(syntax0->obj.symbol, symbols, static_area, stack_area);
    syntax1->obj.symbol = symbol_sign_extension(syntax1->obj.symbol, symbols, static_area, stack_area);
    syntax2->obj.symbol = symbol_sign_extension(syntax2->obj.symbol, symbols, static_area, stack_area);
    if (matching_model(syntax1, syntax2, symbols, static_area, stack_area) < 0)
      ERREXIT(1);
  }

  if (type == C_TYPE_QUESTION) {
    if (symbol_is_const_number(syntax0->obj.symbol)) {
      if (syntax0->obj.symbol->value->obj.number != 0) {
	symbol = syntax1->obj.symbol;
      } else {
	symbol = syntax2->obj.symbol;
      }
      goto ret;
    }
  }

  syntax0->obj.symbol = symbol_locate(syntax0->obj.symbol, symbols,
				      static_area, stack_area);
  syntax1->obj.symbol = symbol_locate(syntax1->obj.symbol, symbols,
				      static_area, stack_area);
  syntax2->obj.symbol = symbol_locate(syntax2->obj.symbol, symbols,
				      static_area, stack_area);

  symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_NONE,
			 SYMBOL_NAME_AUTO, NULL, 0, type);
  if (symbol == NULL)
    ERREXIT(1);
  if (symbols)
    objlist_insert_obj_tail(symbols, symbol, symbol_free);

  symbol->flags |= SYMBOL_FLAG_TEMPORARY;

  symbol->model = model_copy(syntax1->obj.symbol->model);
  symbol->value->obj.op.arg[0] = syntax0->obj.symbol;
  symbol->value->obj.op.arg[1] = syntax1->obj.symbol;
  symbol->value->obj.op.arg[2] = syntax2->obj.symbol;

ret:
  model_array_to_pointer(symbol->model);
  syntax->type = C_TYPE_SYMBOL;
  syntax->obj.symbol = symbol;

  return 0;
}

symbol_t value_make_op1(c_type_t type, symbol_t arg0,
			objlist_t defines_list, objlist_t symbols,
			area_t static_area, area_t stack_area)
{
  objlist_t words;
  syntax_t syntax, nsyntax;

  saveline(arg0->line);

  words = objlist_create(NULL);
  if (words == NULL)
    return NULL;

  syntax = syntax_create(C_TYPE_SYMBOL, arg0->line, 0, NULL);
  nsyntax = syntax_create(C_TYPE_SYMBOL, arg0->line, 0, NULL);
  if ((syntax == NULL) || (nsyntax == NULL))
    return NULL;

  syntax->type = type;
  nsyntax->obj.symbol = arg0;
  objlist_insert_tail(words, &syntax->entry);
  objlist_insert_tail(words, &nsyntax->entry);

  if (op1(type, syntax, words, defines_list, symbols, static_area, stack_area) < 0)
    return NULL;

  syntax = objlist_extract_syntax_head(words);
  objlist_destroy(words);

  return syntax->obj.symbol;
}

symbol_t value_make_op2(c_type_t type, symbol_t arg0, symbol_t arg1,
			objlist_t defines_list, objlist_t symbols,
			area_t static_area, area_t stack_area)
{
  objlist_t words;
  syntax_t syntax, psyntax, nsyntax;

  saveline(arg0->line);

  words = objlist_create(NULL);
  if (words == NULL)
    return NULL;

  syntax = syntax_create(C_TYPE_SYMBOL, arg0->line, 0, NULL);
  psyntax = syntax_create(C_TYPE_SYMBOL, arg0->line, 0, NULL);
  nsyntax = syntax_create(C_TYPE_SYMBOL, arg0->line, 0, NULL);
  if ((syntax == NULL) || (psyntax == NULL) || (nsyntax == NULL))
    return NULL;

  syntax->type = type;
  psyntax->obj.symbol = arg0;
  nsyntax->obj.symbol = arg1;
  objlist_insert_tail(words, &psyntax->entry);
  objlist_insert_tail(words, &syntax->entry);
  objlist_insert_tail(words, &nsyntax->entry);

  if (op2(type, syntax, words, defines_list, symbols, static_area, stack_area) < 0)
    return NULL;

  syntax = objlist_extract_syntax_head(words);
  objlist_destroy(words);

  return syntax->obj.symbol;
}

static symbol_t read_array(objlist_t words,
			   objlist_t defines_list, objlist_t symbols,
			   area_t static_area, area_t stack_area)
{
  objentry_t entry;
  symbol_t symbol, s;

  symbol = symbol_create(C_TYPE_LABEL, loadline(), C_TYPE_NONE,
			 SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_ARRAY);
  if (symbols)
    objlist_insert_obj_tail(symbols, symbol, symbol_free);

  symbol->value->obj.array.values = objlist_create(NULL);

  while (!objlist_is_empty(words)) {
    s = value_read(words, defines_list, symbols, static_area, stack_area,
		   NULL, C_TYPE_COMMA, NULL, 1, 0);
    if (s == NULL)
      ERREXIT(1);
    entry = objlist_insert_obj_tail(symbol->value->obj.array.values,
				    s, symbol_free);
    if (entry == NULL)
      ERREXIT(1);
    s->flags &= ~SYMBOL_FLAG_TEMPORARY;
    symbol->value->obj.array.number++;
  }

  return symbol;
}

static int search_term_type(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 symbol_t type_read(objlist_t syntax_list,
			  objlist_t defines_list, objlist_t symbols)
{
  objlist_t words;
  objentry_t entry;
  syntax_t syntax;
  typedef_t _typedef;

  entry = objlist_get_head(syntax_list);
  if (objlist_is_end(syntax_list, entry))
    return NULL;
  syntax = objentry_get_syntax(entry);

  saveline(syntax->line);

  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:
  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_TYPEDEF:
    entry = objlist_search_from_head(syntax_list, search_term_type, 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);
    }
    return symbol_read(words, defines_list, symbols);
  default:
    break;
  }
  return NULL;
}

static int read_function_args(symbol_t symbol, symbol_t function, objlist_t words,
			      objlist_t defines_list, objlist_t symbols,
			      area_t static_area, area_t stack_area)
{
  symbol_t s;
  model_t arg;
  int argc = 0;

  while (!objlist_is_empty(words)) {
    if (argc == VALUE_FUNCTION_ARGS_NUM)
      ERREXIT(1);
    arg = NULL;
    if (argc < function->model->function.args.number)
      arg = function->model->function.args.arg[argc];
    if ((arg != NULL) && (arg->flags & MODEL_FLAG_TYPETYPE)) {
      s = type_read(words, defines_list, symbols);
      if (s == NULL)
	ERREXIT(1);
    } else if ((arg != NULL) && (arg->flags & MODEL_FLAG_TYPESYMBOL)) {
      s = value_read(words, defines_list, symbols, static_area, stack_area,
		     NULL, C_TYPE_COMMA, NULL, 1, 0);
      if (s == NULL)
	ERREXIT(1);
      s = symbol_locate(s, symbols, static_area, stack_area);
    } else {
      s = value_read(words, defines_list, symbols, static_area, stack_area,
		     NULL, C_TYPE_COMMA, NULL, 1, 0);
      if (s == NULL)
	ERREXIT(1);
      if (arg != NULL) {
	s = symbol_change_model(s, arg, symbols, static_area, stack_area);
      } else {
	s = symbol_change_model_type(s, C_TYPE_LONG,
				     symbols, static_area, stack_area);
      }
      s = symbol_locate(s, symbols, static_area, stack_area);
    }
    symbol->value->obj.function.args.arg[argc++] = s;
    symbol->value->obj.function.args.number = argc;
  }

  return 0;
}

static symbol_t builtin_function(symbol_t symbol)
{
  return symbol;
}

static symbol_t _value_read(objlist_t words,
			    objlist_t defines_list, objlist_t symbols,
			    area_t static_area, area_t stack_area)
{
  objentry_t entry, pentry, nentry;
  syntax_t syntax, s, psyntax, nsyntax;
  symbol_t symbol;
  model_modifier_t modifier;
  objlist_t w;
  int count, size;
  char *string;
  long number;

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    switch (syntax->type) {
    case C_TYPE_WORD:
      if (defines_list == NULL)
	ERREXIT(1);
      symbol = defines_list_search_symbol_name(defines_list, syntax->obj.word);
      if (symbol == NULL) {
	symbol = enum_list_search_symbol_defines(defines_list, syntax->obj.word);
	if (symbol != NULL) {
	  number = symbol->value->obj.number;
	  symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_STATIC,
				 SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
	  if (symbol == NULL)
	    ERREXIT(1);
	  if (symbols)
	    objlist_insert_obj_tail(symbols, symbol, symbol_free);
	  symbol->flags |= SYMBOL_FLAG_READONLY;
	  symbol->model = model_create(C_TYPE_INT);
	  symbol->value->obj.number = number;
	}
      }
      if (symbol == NULL)
	ERREXIT(1);
      syntax->type = C_TYPE_SYMBOL;
      syntax->obj.symbol = symbol;
      break;

    case C_TYPE_SYMBOL:
      symbol = syntax->obj.symbol;
      if (symbol_is_const_string(symbol)) {
	pentry = objentry_get_prev(entry);
	psyntax = !objlist_is_end(words, pentry) ? objentry_get_syntax(pentry) : NULL;
	if ((psyntax != NULL) && (psyntax->type == C_TYPE_SYMBOL) &&
	    symbol_is_const_string(psyntax->obj.symbol)) {
	  /* "abc" "def" -> "abcdef" */
	  objlist_extract(words, entry);
	  size =
	    (psyntax->obj.symbol->value->obj.string.size - 1) +
	    (syntax->obj.symbol->value->obj.string.size - 1);
	  string = psyntax->obj.symbol->value->obj.string.s;
	  if ((string = realloc(string, size + 1)) == NULL)
	    ERREXIT(1);
	  memcpy(string + psyntax->obj.symbol->value->obj.string.size - 1,
		 syntax->obj.symbol->value->obj.string.s,
		 syntax->obj.symbol->value->obj.string.size - 1);
	  string[size] = '\0';
	  psyntax->obj.symbol->value->obj.string.size = size + 1;
	  psyntax->obj.symbol->value->obj.string.s = string;
	  modifier = model_get_modifier_last(psyntax->obj.symbol->model);
	  modifier->param.array.number = size + 1;
	  syntax_destroy(syntax);
	  entry = pentry;
	  break;
	}
      }
      if (!symbol_is_const_number(symbol)) {
	if (symbols)
	  objlist_insert_obj_tail(symbols, symbol, symbol_free);
      }
      break;

    case C_TYPE_BRACKET_IN:
      pentry = objentry_get_prev(entry);
      psyntax = !objlist_is_end(words, pentry) ? objentry_get_syntax(pentry) : NULL;
      if ((psyntax != NULL) &&
	  (psyntax->type == C_TYPE_SYMBOL) &&
	  (psyntax->obj.symbol->model->type == C_TYPE_FUNCTION)
#if 0
	  && (psyntax->obj.symbol->model->modifiers.number <= 1)
#endif
	  ) { /* function call */
#if 0
	if (psyntax->obj.symbol->model->modifiers.number == 0)
	  ERREXIT(1);
#endif
	psyntax->obj.symbol = symbol_locate(psyntax->obj.symbol, symbols,
					    static_area, stack_area);
	objlist_extract(words, pentry);
	symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_NONE,
			       SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_FUNCTION);
	if (symbol == NULL)
	  ERREXIT(1);
	if (symbols)
	  objlist_insert_obj_tail(symbols, symbol, symbol_free);
	symbol->flags |= SYMBOL_FLAG_TEMPORARY;
	symbol->model = model_copy(psyntax->obj.symbol->model->function.retval);
	symbol->value->obj.function.symbol = psyntax->obj.symbol;
	read_function_args(symbol, psyntax->obj.symbol,
			   syntax->obj.words, defines_list, symbols,
			   static_area, stack_area);
	symbol = builtin_function(symbol);
	syntax->type = C_TYPE_SYMBOL;
	syntax->obj.symbol = symbol;
	break;
      }
      symbol = type_read(syntax->obj.words, defines_list, symbols);
      if (symbol != NULL) { /* (type) or sizeof(type) */
	if ((psyntax != NULL) &&
	    (psyntax->type == C_TYPE_SIZEOF)) { /* sizeof(type) */
	  /*
	   * "sizeof (int) - val" is "(sizeof(int)) - (val)",
	   * not "sizeof((int)(-val))".
	   * To avoid coming to be upper when interpreting as the order of
	   * priority of unary operators, It is processed here.
	   */
	  objlist_extract(words, pentry);
	  size = model_get_size(symbol->model);
	  if (size < 0)
	    ERREXIT(1);

	  syntax->type = C_TYPE_SYMBOL;
	  symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_STATIC,
				 SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
	  if (symbols)
	    objlist_insert_obj_tail(symbols, symbol, symbol_free);
	  symbol->value->obj.number = size;
	  symbol->flags |= SYMBOL_FLAG_READONLY;
	  symbol->model = model_create(C_TYPE_LONG);
	  syntax->obj.symbol = symbol;
	  break;
	}
	/* (type) */
	syntax->type = C_TYPE_MODEL;
	syntax->obj.model = model_copy(symbol->model);
	break;
      }
      symbol = _value_read(syntax->obj.words, defines_list, symbols,
			   static_area, stack_area);
      if (symbol == NULL)
	ERREXIT(1);
      syntax->type = C_TYPE_SYMBOL;
      syntax->obj.symbol = symbol;
      break;

    case C_TYPE_BBRACKET_IN:
      pentry = objentry_get_prev(entry);
      if (objlist_is_end(words, pentry))
	ERREXIT(1);
      psyntax = objentry_get_syntax(pentry);
      if (psyntax->type != C_TYPE_SYMBOL)
	ERREXIT(1);

      /* p[i] -> *(p + i) */

      w = syntax->obj.words;
      syntax->obj.words = NULL;
      symbol = _value_read(w, defines_list, symbols, static_area, stack_area);
      if (symbol == NULL)
	ERREXIT(1);

      objlist_extract(words, pentry);
      symbol = value_make_op2(C_TYPE_ADD, psyntax->obj.symbol, symbol,
			      defines_list, symbols, static_area, stack_area);
      if (symbol == NULL)
	ERREXIT(1);

      symbol = value_make_op1(C_TYPE_MUL, symbol,
			      defines_list, symbols, static_area, stack_area);
      if (symbol == NULL)
	ERREXIT(1);

      syntax->type = C_TYPE_SYMBOL;
      syntax->obj.symbol = symbol;

      break;

    case C_TYPE_MBRACKET_IN:
      symbol = read_array(syntax->obj.words, defines_list, symbols,
			  static_area, stack_area);
      if (symbol == NULL)
	ERREXIT(1);
      syntax->type = C_TYPE_SYMBOL;
      syntax->obj.symbol = symbol;
      break;

    case C_TYPE_ARROW:
      pentry = objentry_get_prev(entry);
      if (pentry == NULL)
	ERREXIT(1);
      psyntax = objentry_get_syntax(pentry);
      if (psyntax->type != C_TYPE_SYMBOL)
	ERREXIT(1);

      /* p->m -> (*p).m */
      psyntax->obj.symbol = value_make_op1(C_TYPE_MUL, psyntax->obj.symbol,
					   defines_list, symbols, static_area, stack_area);
      if (psyntax->obj.symbol == NULL)
	ERREXIT(1);
      syntax->type = C_TYPE_DOT;
      /* fall through */

    case C_TYPE_DOT:
      pentry = objlist_extract_prev(words, entry);
      nentry = objlist_extract_next(words, entry);
      if ((pentry == NULL) || (nentry == NULL))
	ERREXIT(1);
      psyntax = objentry_get_syntax(pentry);
      nsyntax = objentry_get_syntax(nentry);
      if (psyntax->type != C_TYPE_SYMBOL)
	ERREXIT(1);
      if (nsyntax->type != C_TYPE_WORD)
	ERREXIT(1);
      if (psyntax->obj.symbol->model->modifiers.number > 0)
	ERREXIT(1);

      symbol = NULL;
      switch (psyntax->obj.symbol->model->type) {
      case C_TYPE_STRUCT:
	symbol = area_search_symbol(psyntax->obj.symbol->model->custom._struct->members, nsyntax->obj.word);
	break;
      case C_TYPE_UNION:
	symbol = area_search_symbol(psyntax->obj.symbol->model->custom._union->members, nsyntax->obj.word);
	break;
      default:
	ERREXIT(1);
      }
      if (symbol == NULL)
	ERREXIT(1);

      syntax->obj.symbol = symbol_create(symbol->type, loadline(),
					 C_TYPE_NONE, SYMBOL_NAME_AUTO,
					 NULL, 0, syntax->type);
      if (symbols)
	objlist_insert_obj_tail(symbols, syntax->obj.symbol, symbol_free);
      syntax->obj.symbol->flags |= SYMBOL_FLAG_TEMPORARY;
      syntax->type = C_TYPE_SYMBOL;

      syntax->obj.symbol->model = model_copy(symbol->model);
      syntax->obj.symbol->value->obj.op.arg[0] = psyntax->obj.symbol;
      syntax->obj.symbol->value->obj.op.arg[1] = symbol;
      syntax->obj.symbol->location.type = C_TYPE_SYMBOL;
      syntax->obj.symbol->location.base.symbol = psyntax->obj.symbol;
      syntax->obj.symbol->location.offset = symbol->location.offset;
      if (symbol->flags & SYMBOL_FLAG_BITFIELD) {
	syntax->obj.symbol->flags |= SYMBOL_FLAG_BITFIELD;
	syntax->obj.symbol->param.variable.bitfield.bits = symbol->param.variable.bitfield.bits;
	syntax->obj.symbol->param.variable.bitfield.shift = symbol->param.variable.bitfield.shift;
      }
      syntax->obj.symbol->flags |= SYMBOL_FLAG_LOCATED;
      break;

    default:
      break;
    }
  }

  for (entry = objlist_get_tail(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_prev(entry)) { /* right to left */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    switch (syntax->type) {
    case C_TYPE_SIZEOF: /* sizeof(symbol) */
      nentry = objlist_extract_next(words, entry);
      if (nentry == NULL)
	ERREXIT(1);
      nsyntax = objentry_get_syntax(nentry);
      if (nsyntax->type != C_TYPE_SYMBOL)
	ERREXIT(1);
      size = model_get_size(nsyntax->obj.symbol->model);
      if (size < 0)
	ERREXIT(1);

      syntax->type = C_TYPE_SYMBOL;
      symbol = symbol_create(C_TYPE_VARIABLE, loadline(), C_TYPE_STATIC,
			     SYMBOL_NAME_AUTO, NULL, 0, C_TYPE_NUMBER);
      if (symbols)
	objlist_insert_obj_tail(symbols, symbol, symbol_free);
      symbol->value->obj.number = size;
      symbol->flags |= SYMBOL_FLAG_READONLY;
      symbol->model = model_create(C_TYPE_LONG);
      syntax->obj.symbol = symbol;
      break;
    case C_TYPE_AND:
    case C_TYPE_MUL:
    case C_TYPE_ADD:
    case C_TYPE_SUB:
    case C_TYPE_INC:
    case C_TYPE_DEC:
    case C_TYPE_INV:
    case C_TYPE_NOT:
      if (op1(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
      break;

    case C_TYPE_MODEL: /* (type) */
      nentry = objentry_get_next(entry);
      if (objlist_is_end(words, nentry))
	ERREXIT(1);
      nsyntax = objentry_get_syntax(nentry);
      if (nsyntax->type != C_TYPE_SYMBOL)
	ERREXIT(1);
      objlist_extract(words, nentry);

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

      syntax->type = C_TYPE_SYMBOL;
      syntax->obj.symbol = symbol_make_cast(nsyntax->obj.symbol, syntax->obj.model,
					    symbols, static_area, stack_area);
      break;

    default:
      break;
    }
  }
 
  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if ((syntax->type == C_TYPE_MUL) ||
	(syntax->type == C_TYPE_DIV) ||
	(syntax->type == C_TYPE_MOD)) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if ((syntax->type == C_TYPE_ADD) ||
	(syntax->type == C_TYPE_SUB)) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if ((syntax->type == C_TYPE_RSHIFT) ||
	(syntax->type == C_TYPE_LSHIFT)) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if ((syntax->type == C_TYPE_GTEQ) ||
	(syntax->type == C_TYPE_LTEQ) ||
	(syntax->type == C_TYPE_GT) ||
	(syntax->type == C_TYPE_LT)) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if ((syntax->type == C_TYPE_EQEQ) ||
	(syntax->type == C_TYPE_NEQ)) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if (syntax->type == C_TYPE_AND) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if (syntax->type == C_TYPE_XOR) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if (syntax->type == C_TYPE_OR) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if (syntax->type == C_TYPE_ANDAND) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if (syntax->type == C_TYPE_OROR) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  count = 0;
  s = NULL;
  for (entry = objlist_get_tail(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_prev(entry)) { /* right to left */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if (syntax->type == C_TYPE_COLON) {
      if (count == 0)
	s = syntax;
      count++;
    } else if (syntax->type == C_TYPE_QUESTION) {
      if (count == 0)
	ERREXIT(1);
      if (--count == 0) {
	nentry = objentry_get_next(&syntax->entry);
	pentry = objentry_get_prev(&s->entry);
	if (objlist_is_end(words, nentry) || objlist_is_end(words, pentry) ||
	    (nentry == &s->entry) || (pentry == &syntax->entry))
	  ERREXIT(1);
	w = objlist_extract_list(words, nentry, pentry);
	symbol = _value_read(w, defines_list, symbols, static_area, stack_area);
	if (symbol == NULL)
	  ERREXIT(1);
	s->type = C_TYPE_SYMBOL;
	s->obj.symbol = symbol;
	if (op3(syntax->type, syntax, words, defines_list, symbols,
		static_area, stack_area) < 0)
	  ERREXIT(1);
	s = NULL;
      }
    }
  }

  for (entry = objlist_get_tail(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_prev(entry)) { /* right to left */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if ((syntax->type == C_TYPE_EQ) ||
	(syntax->type == C_TYPE_ADDEQ) ||
	(syntax->type == C_TYPE_SUBEQ) ||
	(syntax->type == C_TYPE_MULEQ) ||
	(syntax->type == C_TYPE_DIVEQ) ||
	(syntax->type == C_TYPE_MODEQ) ||
	(syntax->type == C_TYPE_ANDEQ) ||
	(syntax->type == C_TYPE_XOREQ) ||
	(syntax->type == C_TYPE_OREQ) ||
	(syntax->type == C_TYPE_RSHIFTEQ) ||
	(syntax->type == C_TYPE_LSHIFTEQ)) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  for (entry = objlist_get_head(words);
       !objlist_is_end(words, entry);
       entry = objentry_get_next(entry)) { /* left to right */
    syntax = objentry_get_syntax(entry);
    saveline(syntax->line);
    if (syntax->type == C_TYPE_COMMA) {
      if (op2(syntax->type, syntax, words, defines_list, symbols,
	      static_area, stack_area) < 0)
	ERREXIT(1);
    }
  }

  syntax = objlist_extract_syntax_head(words);
  if (syntax == NULL) {
    symbol = NULL;
  } else {
    if (syntax->type != C_TYPE_SYMBOL)
      ERREXITAT(syntax->line, 1);
    symbol = syntax->obj.symbol;
    if ((s = objlist_extract_syntax_head(words)) != NULL)
      ERREXITAT(s->line, 1);
  }

  return symbol;
}

static int search_term_default(objentry_t entry, int arg, void *argp)
{
  syntax_t syntax;
  int i;

  syntax = objentry_get_syntax(entry);

  if (argp == NULL) {
    if (syntax->type == arg)
      return 1;
  } else {
    for (i = 0; i < arg; i++) {
      if (syntax->type == ((int *)argp)[i])
	return 1;
    }
  }

  return 0;
}

symbol_t value_read(objlist_t words,
		    objlist_t defines_list, objlist_t symbols,
		    area_t static_area, area_t stack_area,
		    objentry_search_func_t search_func, int arg, void *argp,
		    int permit_noterm, int keep_term)
{
  objlist_t w;
  objentry_t entry;

  if (search_func == NULL) {
    if ((arg == 0) && (argp == NULL)) {
      w = objlist_extract_list_all(words);
      goto ret;
    }
    search_func = search_term_default;
  }

  entry = objlist_search_from_head(words, search_func, arg, argp);
  if (entry == NULL) {
    if (!permit_noterm)
      ERREXIT(1);
    w = objlist_extract_list_all(words);
  } else {
    w = objlist_extract_list_prev(words, entry);
    if (!keep_term) {
      objlist_extract(words, entry);
      syntax_destroy(objentry_get_syntax(entry));
    }
  }

ret:
  return _value_read(w, defines_list, symbols, static_area, stack_area);
}
