/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Env.java
* Created: July 17th 2000
* By: Bo Ilic
*/
package org.openquark.cal.compiler;
import java.util.HashSet;
import java.util.Set;
/**
* A class for environments which associates identifiers to type expressions. Note that Env is
* implemented as a persistent linked list. For example, after a call to extend, we have full access to both
* the original environment and the extended one as well.
* All entities within the environment should be defined within the same module.
*
* Creation date: (7/17/00 4:15:14 PM)
* @author Bo Ilic
*/
final class Env {
/** the entity at the head of this Env. */
private FunctionalAgent headEntity;
/** the rest of the environment. */
private Env tail;
/**
* Construct an environment from an existing environment and a new head entity
* Creation date: (7/21/00 2:33:25 PM)
* @param headEntity the entity at the head of this Env
* @param tail the original environment (to be augmented)
*/
private Env(FunctionalAgent headEntity, Env tail) {
if (headEntity == null) {
throw new IllegalArgumentException("Env constructor: headEntity must not be null.");
}
if (tail != null && !headEntity.getName().getModuleName().equals(tail.headEntity.getName().getModuleName())) {
throw new IllegalArgumentException("Env constructor: all entities in the environment must be in the same module.");
}
this.headEntity = headEntity;
this.tail = tail;
}
/**
* Extend an environment with an identifier-type pair, specifying non-default type.
*
* Creation date: (7/17/00 4:21:26 PM)
* @return Env
* @param existingEnv the Env to extend. May be null.
* @param envEntity
*/
static Env extend(Env existingEnv, FunctionalAgent envEntity) {
if (existingEnv == null) {
return new Env(envEntity, null);
}
return existingEnv.extend(envEntity);
}
/**
* Extend an environment with an identifier-type pair, specifying non-default type.
*
* Creation date: (7/17/00 4:21:26 PM)
* @return Env
* @param envEntity
*/
Env extend(FunctionalAgent envEntity) {
return new Env(envEntity, this);
}
/**
* Indicates that all entities in the Env have had their defining expressions typechecked.
* Creation date: (4/9/01 4:18:48 PM)
*/
void finishedTypeChecking() {
headEntity.setTypeCheckingDone();
if (tail != null) {
tail.finishedTypeChecking();
}
}
/**
* Indicates that the topmost nEnties entities in the Env have had their defining expressions typechecked.
* Creation date: (4/9/01 4:18:48 PM)
* @param nEntities number of entities to mark as finished
*/
void finishedTypeChecking(int nEntities) {
if (nEntities <= 0) {
return;
}
headEntity.setTypeCheckingDone();
if (tail != null) {
tail.finishedTypeChecking(nEntities - 1);
}
}
/**
* Returns true if the name is in this environment.
*
* Creation date: (9/5/00 11:21:52 AM)
* @return boolean whether this identifier was found
* @param name the unqualified name to search for
*/
boolean isIdentifier(String name) {
for (Env env = this; env != null; env = env.tail) {
if (name.equals(env.headEntity.getName().getUnqualifiedName())) {
return true;
}
}
return false;
}
/**
* Returns a set consisting of the distinct identifiers in the environment.
* Creation date: (2/5/01 11:47:18 AM)
* @return Set
*/
Set<String> makeSet() {
Set<String> set = new HashSet<String>();
Env env = this;
do {
set.add(env.headEntity.getName().getUnqualifiedName());
env = env.tail;
} while (env != null);
return set;
}
/**
* Search for an entity in an environment. The entity must be defined in the same module
* as the module for the environment. Returns null if the entity is not found.
*
* Creation date: (6/4/01 3:47:15 PM)
* @return FunctionalAgent
* @param entityName unqualified name of the entity
*/
FunctionalAgent retrieveEntity(String entityName) {
for (Env env = this; env != null; env = env.tail) {
if (entityName.equals(env.headEntity.getName().getUnqualifiedName())) {
return env.headEntity;
}
}
//Entity not found
return null;
}
/**
* Writes out a string representation of the environment.
* Creation date: (1/23/01 12:10:57 PM)
* @return String
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (tail != null) {
sb.append(tail.toString());
}
sb.append(headEntity.toString());
sb.append("\n");
return sb.toString();
}
/**
* Forwards the elements of the environment to the packager. This should only be done after
* all elements of the environment have been fully type checked.
* Creation date: (6/12/01 11:48:51 AM)
* @param moduleTypeInfo
*/
void wrap(ModuleTypeInfo moduleTypeInfo) {
for (Env env = this; env != null; env = env.tail) {
//internal functions such as the derived instance function $equalsMaybe should
//not be added to the ModuleTypeInfo
FunctionalAgent headEntity = env.headEntity;
if (headEntity instanceof Function &&
headEntity.getName().getUnqualifiedName().charAt(0) != '$') {
moduleTypeInfo.addFunction((Function)headEntity);
}
}
}
}