/*
* 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.
*/
/*
* TypeCheckerImpl.java
* Created: Feb 19, 2003 at 12:15:01 PM
* By: Raymond Cypher
*/
package org.openquark.cal.compiler;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openquark.cal.machine.CodeGenerator;
import org.openquark.cal.machine.Program;
import org.openquark.util.Pair;
/**
* Warning- this class should only be used by the CAL compiler implementation. It is not part of the
* external API of the CAL platform.
* <p>
* This is the TypeCheckerImpl class.
*
* This class is used to type-check CAL functions and composition node graphs.
* <p>
* Created: Feb 19, 2003 at 12:15:01 PM
* @author Raymond Cypher
*/
public abstract class TypeCheckerImpl implements TypeChecker {
private final CALCompiler compiler;
private final Program program;
/**
* Constructor for TypeCheckerImpl.
* @param program
*/
protected TypeCheckerImpl(Program program) {
compiler = new CALCompiler();
if (program == null) {
throw new NullPointerException("Argument 'program' cannot be null.");
}
this.program = program;
// makes sure that the CALCompiler has a copy of the packager with the program
// from the very beginning
compiler.setPackager(makePackager(program));
}
abstract protected Packager makePackager(Program program);
abstract protected CodeGenerator makeCodeGenerator();
/**
* Parse the code text and return the list of identifiers that occur within it.
* This method is used for the initial syntax checking and qualification of
* text within a code gem.
*
* The identifiers returned include free symbols i.e. the symbols in the expression that
* are not defined within the expression itself (e.g. by a let definition). The free symbols can
* be either qualified (Prelude.x, Prelude.Boolean) or unqualified (True, Eq, sin). Those that are
* variables which do not successfully resolve to a top-level symbol must be arguments of the code
* expression.
*
* The list is ordered by increasing source positions of identifiers.
*
* Example:
* " let x :: Boolean; x = and Prelude.True False;
* in and x y ;"
* Returns identifiers : Boolean, and, Prelude.True, False, and, y
*
* Note: Any compiler messages held by the logger will be lost.
*
* @param codeGemBodyText the text of the body of the code gem i.e. what the user actually typed
* @param moduleName the name of the module in which the code gem is considered to exist.
* @param logger logger of compiler messages
* @return List (SourceIdentifier) the name, type and position of identifiers encountered in
* the expression. Returns null if the expression does not parse.
*/
public List<SourceIdentifier> findIdentifiersInExpression(String codeGemBodyText, ModuleName moduleName, ModuleNameResolver moduleNameResolver, CompilerMessageLogger logger) {
compiler.setCompilerMessageLogger(logger);
List<SourceIdentifier> identifiers = compiler.findIdentifiersInExpression(codeGemBodyText, moduleName, moduleNameResolver);
compiler.setCompilerMessageLogger(null);
return identifiers;
}
/**
* Parse the specified module and return all identifier references which need to be affected
* in order to rename the specified top-level identifier without conflicts.
*
* The list of objects returned (RenameData) indicates the changes which must occur in order
* to properly rename the specified symbol (ie: the results will include renamings for references
* to the requested symbol, and may include renamings for local variables if any conflict with the
* new name).
*
* Example:
* Renaming "s" to "r" in the expression
*
* "let r = 1.0;
* r2 = if (s r) then 2.0 else 0.0;
* in
* case x of
* (r, r3) -> and (s r) (r2 < r3);
* ;"
*
* Will return renamings to "r" for references to "s",
* renamings to "r4" for case variable "r" ("r2" is bound in let, "r3" bound in case)
* renamings to "r3" for let variable "r" ("r2" is bound in let)
*
* Notes: The list returned is ordered by increasing source position of identifier names.
* Any compiler messages held by this compiler will be lost
*
* @param sourceDef source to parse
* @param logger message logger to hold any errors encountered
* @param oldName old name of the identifier
* @param newName new name of the identifier
* @param category category of the identifier
* @return the name and position of identifiers encountered in
* the expression. Returns null if the expression does not parse.
*/
List<SourceModification> findRenamingsInModule(ModuleSourceDefinition sourceDef, CompilerMessageLogger logger, QualifiedName oldName, QualifiedName newName, SourceIdentifier.Category category) {
compiler.setCompilerMessageLogger(logger);
List<SourceModification> identifiers = compiler.findRenamingsInModule(sourceDef, oldName, newName, category);
compiler.setCompilerMessageLogger(null);
return identifiers;
}
/**
* Parses the specified expression and returns a copy of the expression with any references to the renamed entity updated.
* The changes may include renamings for references to the renamed symbol, and renamings for local variables if any
* conflict with the new name.
*
* Example:
* Renaming "s" to "r" in the expression
*
* "let r = 1.0;
* r2 = if (s r) then 2.0 else 0.0;
* in
* case x of
* (r, r3) -> and (s r) (r2 < r3);
* ;"
*
* Will return the following string:
*
* "let r3 = 1.0;
* r2 = if (r r3) then 2.0 else 0.0;
* in
* case x of
* (r4, r3) -> and (r r4) (r2 < r3);
* ;"
*
* @param codeExpression The text of the expression to update
* @param moduleName The name of the module that this expression belongs to
* @param qualificationMap The qualificationMap associated with this expression, or null if no such thing exists.
* @param oldName old name of the identifier
* @param newName new name of the identifier
* @param category category of the identifier
* @param logger message logger to hold any errors encountered
* @return True if changes were made to the expression
*/
public String calculateUpdatedCodeExpression (String codeExpression, ModuleName moduleName, ModuleNameResolver moduleNameResolver, CodeQualificationMap qualificationMap, QualifiedName oldName, QualifiedName newName, SourceIdentifier.Category category, CompilerMessageLogger logger) {
compiler.setCompilerMessageLogger(logger);
if (logger == null) {
logger = compiler.getMessageLogger();
}
int nOldErrors = logger.getNErrors();
List<SourceModification> identifierPositions =
compiler.findRenamingsInExpression(codeExpression, moduleName, moduleNameResolver, qualificationMap, oldName, newName, category);
compiler.setCompilerMessageLogger(null);
if (logger.getNErrors() > nOldErrors) {
return codeExpression;
} else if (identifierPositions == null) {
logger.logMessage(new CompilerMessage(new MessageKind.Error.CouldNotParseExpressionInModule(moduleName)));
return codeExpression;
}
if (identifierPositions.size() == 0) {
// Nothing to rename in this expression
return codeExpression;
}
SourceModifier renamer = new SourceModifier();
for (final SourceModification sourceModification : identifierPositions) {
renamer.addSourceModification(sourceModification);
}
return renamer.apply(codeExpression);
}
/**
* {@inheritDoc}
*/
public Pair<Map<CompositionNode.CompositionArgument, TypeExpr>, Map<CompositionNode, List<TypeExpr>>> checkGraph(Set<? extends CompositionNode> rootNodes, ModuleName moduleName, CompilerMessageLogger logger) throws TypeException {
compiler.setCompilerMessageLogger(logger);
Pair<Map<CompositionNode.CompositionArgument, TypeExpr>, Map<CompositionNode, List<TypeExpr>>> retVal = compiler.getTypeChecker().checkGraph(rootNodes, moduleName);
compiler.setCompilerMessageLogger(null);
return retVal;
}
/**
* {@inheritDoc}
*/
public TypeExpr checkFunction(AdjunctSource scText, ModuleName scModule, CompilerMessageLogger logger) {
compiler.setCompilerMessageLogger(logger);
TypeExpr retVal = compiler.getTypeChecker().checkFunction(scText, scModule);
compiler.setCompilerMessageLogger(null);
return retVal;
}
/**
* {@inheritDoc}
*/
public TypeExpr getTypeFromString(String typeString, ModuleName workingModule, CompilerMessageLogger logger) {
compiler.setCompilerMessageLogger(logger);
TypeExpr retVal = compiler.getTypeChecker().getTypeFromString(typeString, workingModule);
compiler.setCompilerMessageLogger(null);
return retVal;
}
/**
* {@inheritDoc}
*/
public TypeCheckInfo getTypeCheckInfo(ModuleName moduleName) {
return new TypeCheckInfo(moduleName, this, program);
}
}