/*******************************************************************************
*
* Copyright (c) 2008 Fujitsu Services Ltd.
*
* Author: Nick Battle
*
* This file is part of VDMJ.
*
* VDMJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VDMJ 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VDMJ. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************************/
package org.overture.typechecker;
import java.util.List;
import java.util.Set;
import org.overture.ast.definitions.AStateDefinition;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.definitions.SClassDefinition;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.ast.lex.LexNameList;
import org.overture.ast.typechecker.NameScope;
import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory;
/**
* The parent class of all type checking environments.
*/
abstract public class Environment
{
public final ITypeCheckerAssistantFactory af;
/** The environment chain. */
protected final Environment outer;
/** The enclosing func/op definition at this point, or null. */
private PDefinition enclosingDefinition = null;
/** Whether we are in a functional (true) or operational (false) context, or null. */
private Boolean isFunctional = null;
/**
* Create an environment linking to the given outer chain.
*
* @param af
* @param outer
*/
public Environment(ITypeCheckerAssistantFactory af, Environment outer)
{
this.af = af;
this.outer = outer;
}
/**
* The the definitions that this environment can find
*
* @return
*/
protected abstract List<PDefinition> getDefinitions();
/**
* Check whether the list of definitions passed contains any duplicates, or whether any names in the list hide the
* same name further down the environment chain.
*
* @param list
* The list of definitions to check.
*/
protected void dupHideCheck(List<PDefinition> list, NameScope scope)
{
LexNameList allnames = af.createPDefinitionListAssistant().getVariableNames(list);
for (ILexNameToken n1 : allnames)
{
LexNameList done = new LexNameList();
for (ILexNameToken n2 : allnames)
{
if (n1 != n2 && n1.equals(n2) && !done.contains(n1))
{
TypeChecker.warning(5007, "Duplicate definition: " + n1, n1.getLocation());
done.add(n1);
}
}
if (outer != null)
{
// We search for any scoped name (ie. the first), but then check
// the scope matches what we can see. If we pass scope to findName
// it throws errors if the name does not match the scope.
PDefinition def = outer.findName(n1, NameScope.NAMESANDSTATE);
// TODO: RWL: This is not sound, however the behaviour below is not sound
// in case def.getNameScope is null.
if (def != null && def.getNameScope() == null)
{
def.setNameScope(NameScope.GLOBAL);
}
if (def != null && !def.getLocation().equals(n1.getLocation())
&& def.getNameScope().matches(scope))
{
// Reduce clutter for names in the same module/class
String message = null;
if (def.getLocation().getFile().equals(n1.getLocation().getFile()))
{
message = def.getName() + " "
+ def.getLocation().toShortString()
+ " hidden by " + n1.getFullName();
} else
{
message = def.getName() + " " + def.getLocation()
+ " hidden by " + n1.getFullName();
}
TypeChecker.warning(5008, message, n1.getLocation());
}
}
}
}
public PDefinition getEnclosingDefinition()
{
if (enclosingDefinition != null)
{
return enclosingDefinition;
}
return outer == null ? null : outer.getEnclosingDefinition();
}
public void setEnclosingDefinition(PDefinition def)
{
enclosingDefinition = def;
}
public boolean isFunctional()
{
if (isFunctional != null)
{
return isFunctional;
}
return outer == null ? false : outer.isFunctional();
}
public void setFunctional(boolean functional)
{
isFunctional = Boolean.valueOf(functional);
}
/**
* Find a name in the environment of the given scope.
*
* @param name
* @param scope
* @return
*/
abstract public PDefinition findName(ILexNameToken name, NameScope scope);
/**
* Find a type in the environment.
*
* @param name
* @param fromModule
* @return
*/
abstract public PDefinition findType(ILexNameToken name, String fromModule);
/**
* Find the state defined in the environment, if any.
*
* @return
*/
abstract public AStateDefinition findStateDefinition();
/**
* Find the enclosing class definition, if any.
*
* @return
*/
abstract public SClassDefinition findClassDefinition();
/**
* True if the calling context is a static function or operation.
*
* @return
*/
abstract public boolean isStatic();
/** Check whether any definitions in the environment were unused. */
abstract public void unusedCheck();
/**
* True if this is a VDM++ environment.
*
* @return
*/
abstract public boolean isVDMPP();
/**
* True if this is a VDM-RT "system" environment.
*
* @return
*/
abstract public boolean isSystem();
/**
* Find functions and operations of the given basic name.
*
* @param name
* @return
*/
abstract public Set<PDefinition> findMatches(ILexNameToken name);
/** Mark all definitions, at this level, used. */
public void markUsed()
{
// Nothing, by default. Implemented in flat environments.
}
/**
* Add details to a TC error with alternative fn/op name possibilities.
*
* @param name
*/
public void listAlternatives(ILexNameToken name)
{
for (PDefinition possible : findMatches(name))
{
if (af.createPDefinitionAssistant().isFunctionOrOperation(possible))
{
TypeChecker.detail("Possible", possible.getName());
}
}
}
/**
* Unravelling unused check.
*
* @param downTo
*/
public void unusedCheck(Environment downTo)
{
Environment p = this;
while (p != null && p != downTo)
{
p.unusedCheck();
p = p.outer;
}
}
public String toString()
{
StringBuilder sb = new StringBuilder();
for (PDefinition d : getDefinitions())
{
sb.append("\n---\n");
sb.append(d.toString());
}
return sb.toString();
}
}