package de.unisiegen.tpml.core.expressions ;
import java.util.ArrayList ;
import de.unisiegen.tpml.core.exceptions.LanguageParserMultiException ;
import de.unisiegen.tpml.core.exceptions.NotOnlyFreeVariableException ;
import de.unisiegen.tpml.core.interfaces.BoundIdentifiers ;
import de.unisiegen.tpml.core.interfaces.DefaultExpressions ;
import de.unisiegen.tpml.core.interfaces.DefaultTypes ;
import de.unisiegen.tpml.core.latex.DefaultLatexCommand ;
import de.unisiegen.tpml.core.latex.LatexCommandList ;
import de.unisiegen.tpml.core.latex.LatexPackage ;
import de.unisiegen.tpml.core.latex.LatexPackageList ;
import de.unisiegen.tpml.core.latex.LatexStringBuilder ;
import de.unisiegen.tpml.core.latex.LatexStringBuilderFactory ;
import de.unisiegen.tpml.core.prettyprinter.PrettyStringBuilder ;
import de.unisiegen.tpml.core.prettyprinter.PrettyStringBuilderFactory ;
import de.unisiegen.tpml.core.typechecker.TypeSubstitution ;
import de.unisiegen.tpml.core.types.MonoType ;
import de.unisiegen.tpml.core.types.Type ;
import de.unisiegen.tpml.core.util.BoundRenaming ;
/**
* Represents the simple binding mechanism <code>let</code>. The string
* representation is <code>let id = e1 in e2</code>.
*
* @author Benedikt Meurer
* @author Christian Fehler
* @version $Rev:1092 $
* @see Expression
* @see Lambda
*/
public class Let extends Expression implements BoundIdentifiers , DefaultTypes ,
DefaultExpressions
{
/**
* Indeces of the child {@link Expression}s.
*/
private static final int [ ] INDICES_E = new int [ ]
{ 1 , 2 } ;
/**
* Indeces of the child {@link Identifier}s.
*/
private static final int [ ] INDICES_ID = new int [ ]
{ - 1 } ;
/**
* Indeces of the child {@link Type}s.
*/
private static final int [ ] INDICES_TYPE = new int [ ]
{ - 1 } ;
/**
* String for the case that the identifier is null.
*/
private static final String IDENTIFIER_NULL = "identifier is null" ; //$NON-NLS-1$
/**
* The identifier has the wrong set.
*/
private static final String WRONG_SET = "the set of the identifier has to be 'variable'" ; //$NON-NLS-1$
/**
* String for the case that e1 is null.
*/
private static final String E1_NULL = "e1 is null" ; //$NON-NLS-1$
/**
* String for the case that e2 is null.
*/
private static final String E2_NULL = "e2 is null" ; //$NON-NLS-1$
/**
* The caption of this {@link Expression}.
*/
private static final String CAPTION = Expression.getCaption ( Let.class ) ;
/**
* Returns a set of needed latex commands for this latex printable object.
*
* @return A set of needed latex commands for this latex printable object.
*/
public static LatexCommandList getLatexCommandsStatic ( )
{
LatexCommandList commands = new LatexCommandList ( ) ;
commands.add ( new DefaultLatexCommand ( LATEX_KEY_LET , 0 ,
"\\textbf{\\color{" + LATEX_COLOR_KEYWORD + "}{let}}" ) ) ; //$NON-NLS-1$ //$NON-NLS-2$
commands.add ( new DefaultLatexCommand ( LATEX_KEY_IN , 0 ,
"\\textbf{\\color{" + LATEX_COLOR_KEYWORD + "}{in}}" ) ) ; //$NON-NLS-1$ //$NON-NLS-2$
commands.add ( new DefaultLatexCommand ( LATEX_LET , 4 ,
"\\ifthenelse{\\equal{#2}{}}" + LATEX_LINE_BREAK_NEW_COMMAND //$NON-NLS-1$
+ "{\\color{" + LATEX_COLOR_EXPRESSION + "}\\" + LATEX_KEY_LET //$NON-NLS-1$ //$NON-NLS-2$
+ "\\ #1\\ =\\ #3\\ \\" + LATEX_KEY_IN + "\\ #4}"//$NON-NLS-1$ //$NON-NLS-2$
+ LATEX_LINE_BREAK_NEW_COMMAND + "{\\color{"//$NON-NLS-1$
+ LATEX_COLOR_EXPRESSION + "}\\" + LATEX_KEY_LET//$NON-NLS-1$
+ "\\ #1\\colon\\ #2\\ =\\ #3\\ \\" + LATEX_KEY_IN + "\\ #4}" ,//$NON-NLS-1$ //$NON-NLS-2$
"id" , "tau" , "e1" , "e2" ) ) ;//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
return commands ;
}
/**
* Returns a set of needed latex packages for this latex printable object.
*
* @return A set of needed latex packages for this latex printable object.
*/
public static LatexPackageList getLatexPackagesStatic ( )
{
LatexPackageList packages = new LatexPackageList ( ) ;
packages.add ( LatexPackage.IFTHEN ) ;
return packages ;
}
/**
* The list of identifiers.
*
* @see #getIdentifiers()
*/
protected Identifier [ ] identifiers ;
/**
* The types for the identifiers, where the assignment is as follows:
*
* @see #getTypes()
*/
protected MonoType [ ] types ;
/**
* The first and second expression.
*/
protected Expression [ ] expressions ;
/**
* Allocates a new <code>Let</code> with the specified <code>id</code>,
* <code>e1</code> and <code>e2</code>.
*
* @param pIdentifier the name of the identifier.
* @param pTau the type for the <code>id</code> (and thereby for
* <code>e1</code>) or <code>null</code>.
* @param pExpression1 the first expression.
* @param pExpression2 the second expression.
* @throws NullPointerException if <code>id</code>, <code>e1</code> or
* <code>e2</code> is <code>null</code>.
*/
public Let ( Identifier pIdentifier , MonoType pTau ,
Expression pExpression1 , Expression pExpression2 )
{
if ( pIdentifier == null )
{
throw new NullPointerException ( IDENTIFIER_NULL ) ;
}
if ( ! Identifier.Set.VARIABLE.equals ( pIdentifier.getSet ( ) ) )
{
throw new IllegalArgumentException ( WRONG_SET ) ;
}
if ( pExpression1 == null )
{
throw new NullPointerException ( E1_NULL ) ;
}
if ( pExpression2 == null )
{
throw new NullPointerException ( E2_NULL ) ;
}
// Identifier
this.identifiers = new Identifier [ ]
{ pIdentifier } ;
this.identifiers [ 0 ].setParent ( this ) ;
// Type
this.types = new MonoType [ ]
{ pTau } ;
if ( this.types [ 0 ] != null )
{
this.types [ 0 ].setParent ( this ) ;
}
// Expression
this.expressions = new Expression [ ]
{ pExpression1 , pExpression2 } ;
this.expressions [ 0 ].setParent ( this ) ;
this.expressions [ 1 ].setParent ( this ) ;
checkDisjunction ( ) ;
}
/**
* Allocates a new <code>Let</code> with the specified <code>id</code>,
* <code>e1</code> and <code>e2</code>.
*
* @param pIdentifier the name of the identifier.
* @param pTau the type for the <code>id</code> (and thereby for
* <code>e1</code>) or <code>null</code>.
* @param pExpression1 the first expression.
* @param pExpression2 the second expression.
* @param pParserStartOffset The start offset of this {@link Expression} in
* the source code.
* @param pParserEndOffset The end offset of this {@link Expression} in the
* source code.
* @throws NullPointerException if <code>id</code>, <code>e1</code> or
* <code>e2</code> is <code>null</code>.
*/
public Let ( Identifier pIdentifier , MonoType pTau ,
Expression pExpression1 , Expression pExpression2 ,
int pParserStartOffset , int pParserEndOffset )
{
this ( pIdentifier , pTau , pExpression1 , pExpression2 ) ;
this.parserStartOffset = pParserStartOffset ;
this.parserEndOffset = pParserEndOffset ;
}
/**
* Checks the disjunction of the {@link Identifier} sets.
*/
protected void checkDisjunction ( )
{
ArrayList < Identifier > allIdentifiers = this.expressions [ 1 ]
.getIdentifiersAll ( ) ;
ArrayList < Identifier > negativeIdentifiers = new ArrayList < Identifier > ( ) ;
for ( Identifier allId : allIdentifiers )
{
if ( ( this.identifiers [ 0 ].equals ( allId ) )
&& ( ! ( ( Identifier.Set.VARIABLE.equals ( allId.getSet ( ) ) || ( Identifier.Set.METHOD
.equals ( allId.getSet ( ) ) ) ) ) ) )
{
negativeIdentifiers.add ( allId ) ;
}
}
/*
* Throw an exception, if the negative identifier list contains one or more
* identifiers. If this happens, all Identifiers are added.
*/
if ( negativeIdentifiers.size ( ) > 0 )
{
negativeIdentifiers.clear ( ) ;
for ( Identifier allId : allIdentifiers )
{
if ( this.identifiers [ 0 ].equals ( allId ) )
{
negativeIdentifiers.add ( allId ) ;
}
}
negativeIdentifiers.add ( this.identifiers [ 0 ] ) ;
LanguageParserMultiException
.throwExceptionDisjunction ( negativeIdentifiers ) ;
}
}
/**
* {@inheritDoc}
*
* @see Expression#clone()
*/
@ Override
public Let clone ( )
{
return new Let ( this.identifiers [ 0 ].clone ( ) ,
this.types [ 0 ] == null ? null : this.types [ 0 ].clone ( ) ,
this.expressions [ 0 ].clone ( ) , this.expressions [ 1 ].clone ( ) ) ;
}
/**
* {@inheritDoc}
*
* @see Expression#equals(Object)
*/
@ Override
public boolean equals ( Object pObject )
{
if ( ( pObject instanceof Let )
&& ( this.getClass ( ).equals ( pObject.getClass ( ) ) ) )
{
Let other = ( Let ) pObject ;
return ( ( this.identifiers [ 0 ].equals ( other.identifiers [ 0 ] ) )
&& ( this.expressions [ 0 ].equals ( other.expressions [ 0 ] ) )
&& ( this.expressions [ 1 ].equals ( other.expressions [ 1 ] ) ) && ( ( this.types [ 0 ] == null ) ? ( other.types [ 0 ] == null )
: this.types [ 0 ].equals ( other.types [ 0 ] ) ) ) ;
}
return false ;
}
/**
* {@inheritDoc}
*/
@ Override
public String getCaption ( )
{
return CAPTION ;
}
/**
* Returns the first expression.
*
* @return the first expression.
*/
public Expression getE1 ( )
{
return this.expressions [ 0 ] ;
}
/**
* Returns the second expression.
*
* @return the second expression.
*/
public Expression getE2 ( )
{
return this.expressions [ 1 ] ;
}
/**
* Returns the sub expressions.
*
* @return the sub expressions.
*/
public Expression [ ] getExpressions ( )
{
return this.expressions ;
}
/**
* Returns the indices of the child {@link Expression}s.
*
* @return The indices of the child {@link Expression}s.
*/
public int [ ] getExpressionsIndex ( )
{
return INDICES_E ;
}
/**
* Returns the identifier of the <code>Let</code> expression.
*
* @return the identifier of the <code>Let</code> expression.
*/
public Identifier getId ( )
{
return this.identifiers [ 0 ] ;
}
/**
* Returns the {@link Identifier}s of this {@link Expression}.
*
* @return The {@link Identifier}s of this {@link Expression}.
*/
public Identifier [ ] getIdentifiers ( )
{
return this.identifiers ;
}
/**
* Returns a list of in this {@link Expression} bound {@link Identifier}s.
*
* @return A list of in this {@link Expression} bound {@link Identifier}s.
*/
public ArrayList < ArrayList < Identifier >> getIdentifiersBound ( )
{
if ( this.boundIdentifiers == null )
{
this.boundIdentifiers = new ArrayList < ArrayList < Identifier >> ( 1 ) ;
ArrayList < Identifier > boundIdList = new ArrayList < Identifier > ( ) ;
ArrayList < Identifier > boundE2 = this.expressions [ 1 ]
.getIdentifiersFree ( ) ;
for ( Identifier freeId : boundE2 )
{
if ( this.identifiers [ 0 ].equals ( freeId ) )
{
freeId.setBoundTo ( this , this.identifiers [ 0 ] ) ;
boundIdList.add ( freeId ) ;
}
}
this.boundIdentifiers.add ( boundIdList ) ;
}
return this.boundIdentifiers ;
}
/**
* {@inheritDoc}
*
* @see Expression#getIdentifiersFree()
*/
@ Override
public ArrayList < Identifier > getIdentifiersFree ( )
{
if ( this.identifiersFree == null )
{
this.identifiersFree = new ArrayList < Identifier > ( ) ;
this.identifiersFree.addAll ( this.expressions [ 1 ]
.getIdentifiersFree ( ) ) ;
while ( this.identifiersFree.remove ( this.identifiers [ 0 ] ) )
{
// Remove all Identifiers with the same name
}
this.identifiersFree.addAll ( this.expressions [ 0 ]
.getIdentifiersFree ( ) ) ;
}
return this.identifiersFree ;
}
/**
* Returns the indices of the child {@link Identifier}s.
*
* @return The indices of the child {@link Identifier}s.
*/
public int [ ] getIdentifiersIndex ( )
{
return INDICES_ID ;
}
/**
* Returns a set of needed latex commands for this latex printable object.
*
* @return A set of needed latex commands for this latex printable object.
*/
@ Override
public LatexCommandList getLatexCommands ( )
{
LatexCommandList commands = super.getLatexCommands ( ) ;
commands.add ( getLatexCommandsStatic ( ) ) ;
return commands ;
}
/**
* Returns a set of needed latex packages for this latex printable object.
*
* @return A set of needed latex packages for this latex printable object.
*/
@ Override
public LatexPackageList getLatexPackages ( )
{
LatexPackageList packages = super.getLatexPackages ( ) ;
packages.add ( getLatexPackagesStatic ( ) ) ;
return packages ;
}
/**
* Returns the type for the identifier (and thereby the type for
* <code>e1</code>) or <code>null</code> if no type was specified by the
* user or the translation to core syntax.
*
* @return the type for <code>id</code> or <code>null</code>.
*/
public MonoType getTau ( )
{
return this.types [ 0 ] ;
}
/**
* Returns the types for the <code>identifiers</code>.
*
* @return the types.
*/
public MonoType [ ] getTypes ( )
{
return this.types ;
}
/**
* Returns the indices of the child {@link Type}s.
*
* @return The indices of the child {@link Type}s.
*/
public int [ ] getTypesIndex ( )
{
return INDICES_TYPE ;
}
/**
* {@inheritDoc}
*
* @see Expression#hashCode()
*/
@ Override
public int hashCode ( )
{
return this.identifiers [ 0 ].hashCode ( )
+ ( ( this.types [ 0 ] == null ) ? 0 : this.types [ 0 ].hashCode ( ) )
+ this.expressions [ 0 ].hashCode ( )
+ this.expressions [ 1 ].hashCode ( ) ;
}
/**
* {@inheritDoc}
*
* @see Expression#substitute(Identifier, Expression)
*/
@ Override
public Let substitute ( Identifier pId , Expression pExpression )
{
if ( pExpression.getIdentifierFreeNotOnlyVariable ( ) )
{
throw new NotOnlyFreeVariableException ( ) ;
}
/*
* Perform the substitution in e1.
*/
Expression newE1 = this.expressions [ 0 ].substitute ( pId , pExpression ) ;
Expression newE2 = this.expressions [ 1 ] ;
/*
* Do not substitute in e2 , if the Identifiers are equal.
*/
if ( this.identifiers [ 0 ].equals ( pId ) )
{
return new Let ( this.identifiers [ 0 ] , this.types [ 0 ] , newE1 ,
newE2 ) ;
}
/*
* Perform the bound renaming if required.
*/
ArrayList < Identifier > freeE2 = newE2.getIdentifiersFree ( ) ;
BoundRenaming < Identifier > boundRenaming = new BoundRenaming < Identifier > ( ) ;
boundRenaming.add ( freeE2 ) ;
boundRenaming.remove ( this.identifiers [ 0 ] ) ;
boundRenaming.add ( pExpression.getIdentifiersFree ( ) ) ;
boundRenaming.add ( pId ) ;
Identifier newId = boundRenaming.newIdentifier ( this.identifiers [ 0 ] ) ;
/*
* Substitute the old Identifier only with the new Identifier, if they are
* different.
*/
if ( ! this.identifiers [ 0 ].equals ( newId ) )
{
newE2 = newE2.substitute ( this.identifiers [ 0 ] , newId ) ;
}
/*
* Perform the substitution in e2.
*/
newE2 = newE2.substitute ( pId , pExpression ) ;
return new Let ( newId , this.types [ 0 ] , newE1 , newE2 ) ;
}
/**
* {@inheritDoc}
*
* @see Expression#substitute(TypeSubstitution)
*/
@ Override
public Let substitute ( TypeSubstitution pTypeSubstitution )
{
MonoType newTau = ( this.types [ 0 ] == null ) ? null : this.types [ 0 ]
.substitute ( pTypeSubstitution ) ;
Expression newE1 = this.expressions [ 0 ].substitute ( pTypeSubstitution ) ;
Expression newE2 = this.expressions [ 1 ].substitute ( pTypeSubstitution ) ;
return new Let ( this.identifiers [ 0 ] , newTau , newE1 , newE2 ) ;
}
/**
* {@inheritDoc}
*
* @see Expression#toLatexStringBuilder(LatexStringBuilderFactory,int)
*/
@ Override
public LatexStringBuilder toLatexStringBuilder (
LatexStringBuilderFactory pLatexStringBuilderFactory , int pIndent )
{
LatexStringBuilder builder = pLatexStringBuilderFactory.newBuilder (
PRIO_LET , LATEX_LET , pIndent , this.toPrettyString ( ).toString ( ) ,
this.identifiers [ 0 ].toPrettyString ( ).toString ( ) ,
this.types [ 0 ] == null ? LATEX_NO_TYPE : this.types [ 0 ]
.toPrettyString ( ).toString ( ) , this.expressions [ 0 ]
.toPrettyString ( ).toString ( ) , this.expressions [ 1 ]
.toPrettyString ( ).toString ( ) ) ;
builder.addBuilder ( this.identifiers [ 0 ].toLatexStringBuilder (
pLatexStringBuilderFactory , pIndent + LATEX_INDENT ) , PRIO_ID ) ;
if ( this.types [ 0 ] == null )
{
builder.addEmptyBuilder ( ) ;
}
else
{
builder.addBuilder ( this.types [ 0 ].toLatexStringBuilder (
pLatexStringBuilderFactory , pIndent + LATEX_INDENT ) , PRIO_LET_TAU ) ;
}
builder.addBuilder ( this.expressions [ 0 ].toLatexStringBuilder (
pLatexStringBuilderFactory , pIndent + LATEX_INDENT ) , PRIO_LET_E1 ) ;
builder.addBreak ( ) ;
builder.addBuilder ( this.expressions [ 1 ].toLatexStringBuilder (
pLatexStringBuilderFactory , pIndent + LATEX_INDENT ) , PRIO_LET_E2 ) ;
return builder ;
}
/**
* {@inheritDoc}
*
* @see Expression#toPrettyStringBuilder(PrettyStringBuilderFactory)
*/
@ Override
public PrettyStringBuilder toPrettyStringBuilder (
PrettyStringBuilderFactory pPrettyStringBuilderFactory )
{
if ( this.prettyStringBuilder == null )
{
this.prettyStringBuilder = pPrettyStringBuilderFactory.newBuilder ( this ,
PRIO_LET ) ;
this.prettyStringBuilder.addKeyword ( PRETTY_LET ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addBuilder ( this.identifiers [ 0 ]
.toPrettyStringBuilder ( pPrettyStringBuilderFactory ) , PRIO_ID ) ;
if ( this.types [ 0 ] != null )
{
this.prettyStringBuilder.addText ( PRETTY_COLON ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addBuilder ( this.types [ 0 ]
.toPrettyStringBuilder ( pPrettyStringBuilderFactory ) ,
PRIO_LET_TAU ) ;
}
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addText ( PRETTY_EQUAL ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addBuilder ( this.expressions [ 0 ]
.toPrettyStringBuilder ( pPrettyStringBuilderFactory ) , PRIO_LET_E1 ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addBreak ( ) ;
this.prettyStringBuilder.addKeyword ( PRETTY_IN ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addBuilder ( this.expressions [ 1 ]
.toPrettyStringBuilder ( pPrettyStringBuilderFactory ) , PRIO_LET_E2 ) ;
}
return this.prettyStringBuilder ;
}
}