package nl.utwente.viskell.haskell.expr;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import nl.utwente.viskell.haskell.type.HaskellTypeError;
import nl.utwente.viskell.haskell.type.Type;
public class LetExpression extends Expression {
/** An ordered map of let bindings consisting of binder and expression pairs. */
private final LinkedHashMap<Binder, Expression> binders;
/** The expression forming the body of this let expression*/
private final Expression body;
protected boolean isGuard;
/**
* Constructs an empty let expression (with no local bindings).
* @param body the main expression of this let.
* @param isGuard whether this expression must be compiled as a pattern guard
*/
public LetExpression(Expression body, boolean isGuard) {
super();
this.body = body;
this.binders = new LinkedHashMap<>();
this.isGuard = isGuard;
}
/** @return The body of this let expression */
public Expression getBody() {
return this.body;
}
/**
* Extends a let expression with an extra binding.
* Preserves ordering of let bindings and avoids duplicate binders.
* @param binder the binder variable for the extra let binding.
* @param expr the bound subexpression for the extra let binding.
* @return whether a extra let binding has added to this let expression.
*/
public boolean addLetBinding(Binder binder, Expression expr) {
if (this.binders.containsKey(binder)) {
// remove the old entry to preserve least recent insertion ordering
this.binders.remove(binder);
this.binders.put(binder, expr);
return false;
}
this.binders.put(binder, expr);
return true;
}
@Override
public Type inferType() throws HaskellTypeError {
// TODO the binders should be typechecked first
return this.body.inferType();
}
@Override
public String toHaskell() {
StringBuilder builder = new StringBuilder();
if (isGuard) {
binders.forEach((binder, expr) -> builder.insert(0, binder.getUniqueName() + " <- " + expr.toHaskell() + ", "));
builder.append("True -> ");
builder.append(this.body.toHaskell());
return builder.toString();
}
else {
binders.forEach((binder, expr) -> builder.insert(0, binder.getUniqueName() + " = " + expr.toHaskell() + "; "));
builder.insert(0, "let {");
builder.append("} in ");
builder.append(this.body.toHaskell());
return "(" + builder.toString() + ")";
}
}
@Override
public String toString() {
return toHaskell();
}
@Override
public final List<Expression> getChildren() {
List<Expression> exprs = new ArrayList<>();
exprs.add(this.body);
exprs.addAll((this.binders.values()));
Collections.reverse(exprs);
return exprs;
}
}