
/*
 * Copyright (c) 1998 David Stes.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Library General Public License as published 
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: assoc.m,v 1.17 1998/05/26 13:13:58 stes Exp $
 */

#include "config.h"
#include <stdlib.h>
#include <assert.h>
#include <Object.h>
#include <ocstring.h>
#include <ordcltn.h>
#include "node.h"
#include "def.h"
#include "datadef.h"
#include "expr.h"
#include "decl.h"
#include "initdecl.h"
#include "symbol.h"
#include "stclass.h"
#include "trlunit.h"
#include "type.h"
#include "options.h"
#include "stmt.h"
#include "compstmt.h"
#include "util.h"

@implementation DataDef 

- specs
{
  return specs;
}

- decllist
{
  return decllist;
}

- specs:aSpec
{
  specs = aSpec;
  return self;
}

- add:aDecl 
{
  if (!decllist) {
    decllist = [OrdCltn new];
  }
  [decllist add:aDecl];
  return self;
}

- synthattrs
{
  int i, n;

  for (i = 0, n = [specs size]; i < n; i++) {
    id s = [specs at:i];

    if ([s isstorageclass]) {
      if ([s istypedef])
	istypedef++;
      else if ([s isstatic])
	isstatic++;
      else if ([s isinline])
	isinline++;
      else if ([s isextern])
	isextern++;
    }
  }
  return self;
}

- (BOOL)istypedef
{
  return istypedef;
}

- (BOOL)isstatic
{
  return isstatic;
}

- (BOOL)isinline
{
  return isinline;
}

- (BOOL)definesocu
{
  /* Linux Debian headers contain 'inline' fun impls */
  return !isstatic && !isextern && !isinline;
}

- synth
{
  int i, n;

  if (specs)
    [specs elementsPerform:_cmd];
  [self synthattrs];

  for (i = 0, n = [decllist size]; i < n; i++) {
    id var = [[decllist at:i] identifier];

    if (var) {
      id t = [Type new];
      id d = [decllist at:i];

      if (specs) {
	[t specs:specs];	/* type filters out storage class */
	[t decl:d];		/* type makes a -abstrdecl of it */
      } else {
	[t addspec:s_int];	/* C default */
	[t decl:d];
      }
      [t isstatic:isstatic];
      [t isextern:isextern];
      if ([d islistinit])
	[t haslistinit:YES];
      if (istypedef) {
	if (curdef) {
	  warn("typedef not allowed inside function or method definition");
	} else {
	  [trlunit def:var astype:t];
	}
      } else {
	if (curdef) {
	  if (curcompound) {
	    [curcompound deflocal:var astype:t];
	  } else {
	    [curdef defparm:var astype:t];
	  }
	} else {
	  [trlunit defdata:var astype:t];
	  if (!o_postlink && [self definesocu] && ![d isfunproto]) {
	    [trlunit definesentry:var];
	  }
	}

	/* after defining the variable, process initializer if any */
	[d synthinits];

	/* for assignments to work in the refcnt case, locals must be nil'ed */
	if (o_refcnt && [t isid]) {
	  if ([d isinit]) {
	    [d incref];
	  } else {
	    if (!isextern) {
	      d = [[[InitDecl new] decl:d] initnil];
	      [decllist at:i put:d];	/* make an init : id foo = nil */
	    }
	  }
	}
      }
    } else {
      fatal("missing name in data definition");
    }
  }

  return self;
}

- removevars:set initializers:cltn
{
  int n = [decllist size];

  while (n--) {
    id d, v;
    BOOL isheapvar;

    d = [decllist at:n];
    v = [d identifier];
    isheapvar = [set contains:v];
    if ([d isinit]) {
      if ([d islistinit]) {
	if (isheapvar || ![[d initializer] isconstexpr]) {
	  char *m = "list initializers not yet allowed in function with Blocks";

	  fatalat(v, m);
	}			/* if it is const and not for a heapvar, then doesnt need remove */
      } else {
	id x = [mkidentexpr(v) synth];

	[cltn add:mkexprstmt(mkassignexpr(x, "=", [d initializer]))];
	[decllist at:n put:[d decl]];	/* hence removing the initializer */
      }
    }
    if (isheapvar) {
      [decllist removeAt:n];	/* removing var alltogether */
    }
  }
  if (![decllist size])
    decllist = nil;
  return self;
}

- gen
{
  if (specs)
    [specs elementsPerform:@selector(gen)];
  if (decllist)
    gcommalist(decllist);
  gc(';');
  return self;
}

- st80
{
  if (decllist)
    [decllist elementsPerform:_cmd];
  return self;
}

- st80inits
{
  if (decllist)
    [decllist elementsPerform:_cmd];
  return self;
}

@end
 
