/* $Id$ */

/* Symbol handling */

#include	<stdio.h>
#include	<alloc.h>
#include	<out.h>
#include	<stb.h>
#include	<assert.h>

#include	"position.h"
#include	"file.h"
#include	"idf.h"
#include	"type.h"
#include	"symbol.h"
#include	"scope.h"
#include	"tree.h"
#include	"operator.h"
#include	"misc.h"

p_symbol	currfile, listfile;

extern FILE	*db_out;

p_symbol
NewSymbol(s, scope, class, nam)
  char	*s;
  register p_scope scope;
  struct outname *nam;
{
  register p_symbol sym;
  
  sym = new_symbol();
  sym->sy_idf = str2idf(s, 0);
  sym->sy_scope = scope;
  sym->sy_prev_sc = scope->sc_symbs;
  scope->sc_symbs = sym;
  sym->sy_next = sym->sy_idf->id_def;
  sym->sy_idf->id_def = sym;
  sym->sy_class = class;
  switch(class) {
  case MODULE:
  case PROC:
  case FUNCTION:
  case VAR:
  case REGVAR:
  case LOCVAR:
  case VARPAR:
	sym->sy_name.nm_value = nam->on_valu;
	break;
  default:
	break;
  }
  return sym;
}

/* Lookup a definition for 'id' in scope 'scope' with class in the 'class'
   bitset.
*/
p_symbol
Lookup(id, scope, class)
  struct idf *id;
  p_scope scope;
  int	class;
{
  register p_symbol p = id ? id->id_def : 0;

  while (p) {
	if (p->sy_scope == scope && (p->sy_class & class)) {
		return p;
	}
	p = p->sy_next;
  }
  return (p_symbol) 0;
}

/* Lookup a definition for 'id' with class in the 'class' bitset,
   starting in scope 'sc' and also looking in enclosing scopes.
*/
p_symbol
Lookfromscope(id, class, sc)
  register struct idf *id;
  int	class;
  register p_scope	sc;
{
  if (! id) return (p_symbol) 0;

  while (sc) {
	register p_symbol sym = id->id_def;
	while (sym) {
		if (sym->sy_scope == sc && (sym->sy_class & class)) {
			return sym;
		}
		sym = sym->sy_next;
	}
	sc = sc->sc_static_encl;
  }
  return (p_symbol) 0;
}

extern char *strrchr();

p_symbol
add_file(s)
  char	*s;
{
  register p_symbol sym = NewSymbol(s,
				    PervasiveScope,
				    FILESYM,
				    (struct outname *) 0);
  register char *p;

  sym->sy_file = new_file();
  sym->sy_file->f_sym = sym;
  p = strrchr(s, '.');
  if (p) {
	char c = *p;
	p_symbol sym1;

	*p = 0;
	s = Salloc(s, (unsigned) strlen(s)+1);
	*p = c;
	sym1 = NewSymbol(s,
		  	 PervasiveScope,
		 	 FILELINK,
			 (struct outname *) 0);
	sym1->sy_filelink = sym;
	sym->sy_file->f_base = sym1;
  }
  return sym;
}

static p_scope
def_scope(s)
  p_symbol	s;
{
  switch(s->sy_class) {
  case FILELINK:
	s = s->sy_filelink;
	/* fall through */
  case FILESYM:
	return s->sy_file->f_scope;
  case PROC:
  case FUNCTION:
  case MODULE:
  case TYPE:
  case VAR:
  case REGVAR:
  case LOCVAR:
  case VARPAR:
	return s->sy_name.nm_scope;
  }
  return 0;
}

/* Determine if the OP_SELECT tree indicated by 'p' could lead to scope 'sc'.
*/
int
consistent(p, sc)
  p_tree	p;
  p_scope	sc;
{
  p_tree	arg;
  p_symbol	sym;
  p_scope	target_sc;

  assert(p->t_oper == OP_SELECT);

  p = p->t_args[0];

  switch(p->t_oper) {
  case OP_NAME:
#define CLASS	(FILELINK|FILESYM|PROC|FUNCTION|MODULE|TYPE|VAR|REGVAR|LOCVAR|VARPAR|LBOUND|UBOUND)
	sym = Lookfromscope(p->t_idf, CLASS, sc->sc_static_encl);
	if (sym) {
		int precise = 1;

		target_sc = def_scope(sym);
		while (sc && sc != target_sc) {
			precise = 0;
			sc = sc->sc_static_encl;
		}
		return sc == 0 ? 0 : precise + 1 ;
	}
	return 0;

  case OP_SELECT:
	arg = p->t_args[1];
	sym = Lookfromscope(arg->t_idf, CLASS, sc->sc_static_encl);
	if (sym) {
		int precise = 1;

		target_sc = def_scope(sym);
		while (sc && sc != target_sc) {
			precise = 0;
			sc = sc->sc_static_encl;
		}
		if (sc == 0) return 0;
		if (precise) return consistent(p, sym->sy_scope);
		return consistent(p, sym->sy_scope) != 0;
	}
	return 0;

  default:
	assert(0);
  }
  return 0;	/* notreached? */
}

/* Try to find the name referred to in the node indicated by 'p', and
   try to be just a little bit intelligent about it.
*/
p_symbol
identify(p, class_set)
  p_tree	p;
  int		class_set;
{
  p_symbol	sym = 0, sym1 = 0;
  register p_symbol s;
  p_tree	arg;
  int precise = 0;

  switch(p->t_oper) {
  case OP_NAME:
	sym = Lookfromscope(p->t_idf, class_set, CurrentScope);
	if (sym) {
		/* Found it. */
		break;
	}

	/* We could not find it using the current scope; now we try to identify
	   it using class_set. If this results in only one definition, we
	   take this one.
	*/
	s = p->t_idf->id_def;
	while (s) {
		if (s->sy_class & class_set) {
			if (sym) {
				error("could not identify \"%s\"", p->t_str);
				sym = 0;
				break;
			}
			sym = s;
		}
		s = s->sy_next;
	}
	if (!sym && !s) {
		error("could not find \"%s\"", p->t_str);
	}
	break;

  case OP_SELECT:
	arg = p->t_args[1];
	assert(arg->t_oper == OP_NAME);
	s = arg->t_idf->id_def;
	while (s) {
		int temp;
		if ((s->sy_class & class_set) &&
		    (temp = consistent(p, s->sy_scope))) {
			if (temp > precise) {
				sym = s;
				precise = temp;
				sym1 = 0;
			}
			else if (sym && temp == precise) sym1 = s;
		}
		s = s->sy_next;
	}
	if (sym && sym1) {
		error("could not identify \"%s\"", arg->t_str);
		return 0;
	}
	if (!sym && !s) {
		error("could not find \"%s\"", arg->t_str);
		return 0;
	}
	break;

  default:
	assert(0);
  }
  return sym;
}

static
pr_scopes(sc)
  p_scope	sc;
{
  if (! sc) return;
  pr_scopes(sc->sc_static_encl);
  if (sc->sc_definedby) {
  	if (sc->sc_definedby->sy_class == FILESYM &&
	    sc->sc_definedby->sy_file->f_base) {
		fprintf(db_out, "%s`", sc->sc_definedby->sy_file->f_base->sy_idf->id_text);
  	}
  	else fprintf(db_out, "%s`", sc->sc_definedby->sy_idf->id_text);
  }
}

pr_sym(s)
  p_symbol	s;
{
  switch(s->sy_class) {
  case CONST:
	fprintf(db_out, "Constant:\t");
	break;
  case TYPE:
	fprintf(db_out, "Type:\t\t");
	break;
  case TAG:
	fprintf(db_out, "Tag:\t\t");
	break;
  case MODULE:
	fprintf(db_out, "Module:\t\t");
	break;
  case PROC:
  case FUNCTION:
	fprintf(db_out, "Routine:\t");
	break;
  case VAR:
  case REGVAR:
  case LOCVAR:
  case VARPAR:
  case LBOUND:
  case UBOUND:
	fprintf(db_out, "Variable:\t");
	break;
  case FIELD:
	fprintf(db_out, "Field:\t\t");
	break;
  case FILESYM:
  case FILELINK:
	fprintf(db_out, "File:\t\t");
	break;
  default:
	assert(0);
  }
  pr_scopes(s->sy_scope);
  fprintf(db_out, "%s\n", s->sy_idf->id_text);
}

resolve_cross(tp)
  p_type	tp;
{
  register p_symbol	sym = tp->ty_sym->sy_idf->id_def;

  while (sym) {
	if (sym->sy_class == TAG &&
	    sym->sy_type->ty_class == T_CROSS &&
	    sym->sy_type->ty_cross == (p_type) 0 &&
	    sym->sy_type->ty_size == tp->ty_class &&
	    scope_encloses(tp->ty_sym->sy_scope, sym->sy_scope)) {
		sym->sy_type->ty_cross = tp;
		sym->sy_type->ty_size = tp->ty_size;
	}
	sym = sym->sy_next;
  }
}
