package expressions;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import common.prettyprinter.PrettyStringBuilder;
import expressions.annotation.SyntacticSugar;
import types.MonoType;
import util.StringUtilities;
/**
* Represents a multi lambda abstraction, which takes
* a single tuple argument as parameter.
*
* @author Benedikt Meurer
* @version $Id$
*/
@SyntacticSugar
public final class MultiLambda extends Value {
//
// Attributes
//
/**
* The tuple parameter identifiers.
*
* @see #getIdentifiers()
*/
private String[] identifiers;
/**
* The type of the <code>identifiers</code> or <code>null</code>.
*
* @see #getTau()
*/
private MonoType tau;
/**
* The function body expression.
*
* @see #getE()
*/
private Expression e;
//
// Constructors
//
/**
* Convenience wrapper for {@link #MultiLambda(String[], MonoType, Expression)}
* passing <code>null</code> for <code>tau</code>.
*
* @param identifiers non-empty set of identifiers.
* @param e the function body.
*
* @throws IllegalArgumentException if the <code>identifiers</code>
* list is empty.
* @throws NullPointerException if <code>e</code> is <code>null</code>.
*/
public MultiLambda(String[] identifiers, Expression e) {
this(identifiers, null, e);
}
/**
* Allocates a new <code>MultiLambda</code> expression with
* the specified <code>identifiers</code> and the function
* body <code>e</code>.
*
* @param identifiers non-empty set of identifiers.
* @param tau the type of the identifiers or <code>null</code>.
* @param e the function body.
*
* @throws IllegalArgumentException if the <code>identifiers</code>
* list is empty.
* @throws NullPointerException if <code>e</code> is <code>null</code>.
*/
public MultiLambda(String[] identifiers, MonoType tau, Expression e) {
if (identifiers.length == 0) {
throw new IllegalArgumentException("identifiers is empty");
}
if (e == null) {
throw new NullPointerException("e is null");
}
this.identifiers = identifiers;
this.tau = tau;
this.e = e;
}
//
// Primitives
//
/**
* {@inheritDoc}
*
* @see expressions.Expression#substitute(java.lang.String, expressions.Expression)
*/
@Override
public Expression substitute(String id, Expression e) {
// check if we can substitute below the lambda abstraction
if (Arrays.asList(this.identifiers).contains(id)) {
return this;
}
else {
// bound rename for substituting e in this.e
Expression newE = this.e;
Set<String> freeE = e.free();
String[] newIdentifiers = this.identifiers.clone();
for (int n = 0; n < newIdentifiers.length; ++n) {
// generate a new unique identifier
while (freeE.contains(newIdentifiers[n]))
newIdentifiers[n] = newIdentifiers[n] + "'";
// perform the bound renaming
newE = newE.substitute(this.identifiers[n], new Identifier(newIdentifiers[n]));
}
// perform the substitution
newE = newE.substitute(id, e);
// allocate the new multi lambda
return new MultiLambda(newIdentifiers, this.tau, newE);
}
}
/**
* {@inheritDoc}
*
* @see expressions.Expression#free()
*/
@Override
public Set<String> free() {
TreeSet<String> free = new TreeSet<String>();
free.addAll(this.e.free());
free.removeAll(Arrays.asList(this.identifiers));
return free;
}
/**
* {@inheritDoc}
*
* @see expressions.Expression#translateSyntacticSugar()
*/
@Override
public Expression translateSyntacticSugar() {
// determine a new unique identifier
// to be used for the tuple parameter
String id = "id";
while (this.e.free().contains(id) || Arrays.asList(this.identifiers).contains(id)) {
id = id + "'";
}
// generate the required let's
Expression e = this.e;
for (int n = this.identifiers.length - 1; n >= 0; --n) {
e = new Let(this.identifiers[n],
new Application(new Projection(this.identifiers.length, n + 1),
new Identifier(id)),
e);
}
// and return the new lambda expression
return new Lambda(id, this.tau, e);
}
/**
* {@inheritDoc}
*
* @see expressions.Expression#toPrettyStringBuilder()
*/
@Override
protected PrettyStringBuilder toPrettyStringBuilder() {
PrettyStringBuilder builder = new PrettyStringBuilder(this, 0);
builder.appendKeyword("\u03bb");
builder.appendText("(" + StringUtilities.join(", ", this.identifiers) + ")");
if (this.tau != null) {
builder.appendText(":");
builder.appendText(this.tau.toString());
}
builder.appendText(".");
builder.appendBuilder(this.e.toPrettyStringBuilder(), 0);
return builder;
}
//
// Accessors
//
/**
* Returns the arity of the tuple parameter.
*
* @return the arity of the tuple parameter.
*/
public int getArity() {
return this.identifiers.length;
}
/**
* Returns the identifiers for the tuple parameter.
*
* @return the identifiers for the tuple parameter.
*/
public String[] getIdentifiers() {
return this.identifiers;
}
/**
* Returns the type of the <code>identifiers</code> or
* <code>null</code> if no type was specified.
*
* @return the type of the identifiers;
*/
public MonoType getTau() {
return this.tau;
}
/**
* Returns the function body expression.
*
* @return the function body expression.
*/
public Expression getE() {
return this.e;
}
}