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 curried <code>let</code> expression. * * @author Benedikt Meurer * @version $Id$ */ @SyntacticSugar public class CurriedLet extends Expression { // // Attributes // /** * The list of identifiers. * * @see #getIdentifiers() */ protected String[] identifiers; /** * The first expression. * * @see #getE1() */ protected Expression e1; /** * The second expression. * * @see #getE2() */ protected Expression e2; // // Constructor // /** * Allocates a new <code>CurriedLet</code> instance. * * @param identifiers an array with atleast two identifiers, * where the first identifier is the name * to use for the function and the remaining * identifiers specify the parameters for * the function. * @param e1 the function body. * @param e2 the second expression. * * @throws IllegalArgumentException if the <code>identifiers</code> array * contains less than two identifiers. */ public CurriedLet(String[] identifiers, Expression e1, Expression e2) { // validate the identifiers if (identifiers == null || identifiers.length < 2) { throw new IllegalArgumentException("identifiers must contain atleast two items"); } // initialize the attributes this.identifiers = identifiers; this.e1 = e1; this.e2 = e2; } // // Primitives // /** * Performs the substitution for <b>CurriedLet</b> expressions. * * @param id the identifier for the substitution. * @param e the expression to substitute. * * @return the new 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; // check if we can substitute below e1 if (!Arrays.asList(identifiers).subList(1, identifiers.length).contains(id)) { // bound rename for substituting e in e1 identifiers = identifiers.clone(); Set<String> freeE = e.free(); for (int n = 1; n < identifiers.length; ++n) { // generate a new unique identifier while (freeE.contains(identifiers[n])) identifiers[n] = identifiers[n] + "'"; // perform the bound renaming e1 = e1.substitute(this.identifiers[n], new Identifier(identifiers[n])); } // substitute in e1 if e1 = e1.substitute(id, e); } // substitute e2 if id is not bound in e2 if (!this.identifiers[0].equals(id)) e2 = e2.substitute(id, e); // generate the new expression return new CurriedLet(identifiers, e1, e2); } /** * Returns the free identifiers of * the subexpressions. * * @return the free identifiers. * * @see expressions.Expression#free() */ @Override public Set<String> free() { Set<String> freeE2 = new TreeSet<String>(); freeE2.addAll(this.e2.free()); freeE2.remove(this.identifiers[0]); Set<String> freeE1 = new TreeSet<String>(); freeE1.addAll(this.e1.free()); for (int n = 1; n < this.identifiers.length; ++n) freeE1.remove(this.identifiers[n]); Set<String> free = new TreeSet<String>(); free.addAll(freeE1); free.addAll(freeE2); return free; } /** * {@inheritDoc} * * @see expressions.Expression#translateSyntacticSugar() */ @Override public Expression translateSyntacticSugar() { // translate to: let id1 = lambda id2...lambda idn.e1 in e2 Expression e1 = this.e1; for (int n = this.identifiers.length - 1; n > 0; --n) { e1 = new Lambda(this.identifiers[n], e1); } return new Let(this.identifiers[0], e1, this.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.appendBreak(); builder.appendText(" "); builder.appendKeyword("in"); builder.appendText(" "); builder.appendBuilder(this.e2.toPrettyStringBuilder(), 0); return builder; } // // Accessors // /** * Returns the identifiers, where the first identifier is * the name of the function and the remaining identifiers * name the parameters of the functions, in a curried * fashion. * * @return the identifiers. */ public String[] getIdentifiers() { return this.identifiers; } /** * Returns the function body. * * @return the function body. */ public Expression getE1() { return this.e1; } /** * Returns the second expression. * * @return the second expression. */ public Expression getE2() { return this.e2; } }