package expressions; import java.util.Arrays; import java.util.Set; import java.util.TreeSet; import common.prettyprinter.PrettyStringBuilder; import expressions.annotation.SyntacticSugar; import util.StringUtilities; /** * Represents a multiple let expression. * * @author Benedikt Meurer * @version $Id$ */ @SyntacticSugar public final class MultiLet extends Expression { // // Attributes // /** * The bound identifiers. * * @see #getIdentifiers() */ private String[] identifiers; /** * The first expression. * * @see #getE1() */ private Expression e1; /** * The second expression. * * @see #getE2() */ private Expression e2; // // Constructor // /** * Allocates a new <code>MultiLet</code> expression with * the specified <code>identifiers</code> and the given * expressions <code>e1</code> and <code>e2</code>. * * @param identifiers non-empty set of identifiers. * @param e1 the first expression. * @param e2 the second expression. * * @throws IllegalArgumentException if the <code>identifiers</code> * list is empty. */ public MultiLet(String[] identifiers, Expression e1, Expression e2) { // validate the identifiers if (identifiers.length == 0) { throw new IllegalArgumentException("identifiers is empty"); } // initialize the attributes this.identifiers = identifiers; this.e1 = e1; this.e2 = e2; } // // Primitives // /** * {@inheritDoc} * * @see expressions.Expression#substitute(java.lang.String, expressions.Expression) */ @Override public Expression substitute(String id, Expression e) { // determine the expressions and the identifiers String[] identifiers = this.identifiers; Expression e1 = this.e1; Expression e2 = this.e2; // substitute in e1 e1 = e1.substitute(id, e); // check if we can substitute below e2 if (!Arrays.asList(identifiers).contains(id)) { // bound rename for substituting e in e2 identifiers = identifiers.clone(); Set<String> freeE = e.free(); for (int n = 0; n < identifiers.length; ++n) { // generate a new unique identifier while (freeE.contains(identifiers[n])) identifiers[n] = identifiers[n] + "'"; // perform the bound renaming e2 = e2.substitute(this.identifiers[n], new Identifier(identifiers[n])); } // substitute in e2 e2 = e2.substitute(id, e); } // generate the new expression return new MultiLet(identifiers, e1, e2); } /** * {@inheritDoc} * * @see expressions.Expression#free() */ @Override public Set<String> free() { TreeSet<String> free = new TreeSet<String>(); free.addAll(this.e2.free()); free.removeAll(Arrays.asList(this.identifiers)); free.addAll(this.e1.free()); 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.e1.free().contains(id) || this.e2.free().contains(id) || Arrays.asList(this.identifiers).contains(id)) { id = id + "'"; } // generate the required let's Expression e2 = this.e2; for (int n = this.identifiers.length - 1; n >= 0; --n) { e2 = new Let(this.identifiers[n], new Application(new Projection(this.identifiers.length, n + 1), new Identifier(id)), e2); } // and return the new let expression return new Let(id, this.e1, e2); } /** * {@inheritDoc} * * @see expressions.Expression#toPrettyStringBuilder() */ @Override protected PrettyStringBuilder toPrettyStringBuilder() { PrettyStringBuilder builder = new PrettyStringBuilder(this, 0); builder.appendKeyword("let"); builder.appendText(" (" + StringUtilities.join(", ", this.identifiers) + ") = "); builder.appendBuilder(this.e1.toPrettyStringBuilder(), 0); builder.appendText(" "); builder.appendKeyword("in"); builder.appendText(" "); builder.appendBuilder(this.e2.toPrettyStringBuilder(), 0); return builder; } // // Accessors // /** * Returns the identifiers for the tuple items. * * @return the identifiers for the tuple items. */ public String[] getIdentifiers() { return this.identifiers; } /** * Returns the first expression. * * @return the first expression. */ public Expression getE1() { return this.e1; } /** * Returns the second expression. * * @return the second expression. */ public Expression getE2() { return this.e2; } }