package nl.utwente.viskell.haskell.typeparser;
import nl.utwente.viskell.haskell.type.Type;
import nl.utwente.viskell.haskell.type.TypeClass;
import nl.utwente.viskell.haskell.type.TypeScope;
import java.util.*;
/**
* ANTLR listener that builds Type instances.
*/
class TypeBuilderListener extends TypeBaseListener {
/** Temporary storage area for compound types. */
private final Stack<List<Type>> stack = new Stack<>();
/** Temporary store for the last type class. */
private Optional<TypeClass> typeClass = Optional.empty();
/** The available type classes. */
private final Map<String, TypeClass> typeClasses;
/** The type scope in which type variables are looked up and built. */
TypeScope scope = new TypeScope();
/**
* Build a TypeBuilderListener.
* @param typeClasses The available type classes.
*/
protected TypeBuilderListener(Map<String, TypeClass> typeClasses) {
this.typeClasses = typeClasses;
this.enter();
}
/** Build a TypeBuilderListener without type class support. */
protected TypeBuilderListener() {
this(new HashMap<>());
}
@Override
public void enterTypeWithClass(TypeParser.TypeWithClassContext ctx) {
this.typeClass = Optional.empty();
}
@Override
public void exitClassedType(TypeParser.ClassedTypeContext ctx) {
if (this.typeClass.isPresent()) {
this.scope.introduceConstraint(ctx.getText(), this.typeClass.get());
}
}
@Override
public void exitTypeClass(TypeParser.TypeClassContext ctx) {
this.typeClass = Optional.ofNullable(this.typeClasses.get(ctx.getText()));
}
@Override
public final void exitVariableType(TypeParser.VariableTypeContext ctx) {
this.addParam(this.scope.getVar(ctx.getText()));
}
@Override
public final void enterFunctionType(TypeParser.FunctionTypeContext ctx) {
this.enter();
}
@Override
public final void exitFunctionType(TypeParser.FunctionTypeContext ctx) {
Type[] params = this.popParams();
this.addParam(Type.fun(params[0], params[1])); // We can do this because the grammer makes sure that a function
// always has two arguments.
}
@Override
public final void enterTupleType(TypeParser.TupleTypeContext ctx) {
this.enter();
}
@Override
public final void exitTupleType(TypeParser.TupleTypeContext ctx) {
this.addParam(Type.tupleOf(this.popParams()));
}
@Override
public final void enterListType(TypeParser.ListTypeContext ctx) {
this.enter();
}
@Override
public final void exitListType(TypeParser.ListTypeContext ctx) {
this.addParam(Type.listOf((this.popParams()[0])));
}
@Override
public final void exitTypeConstructor(TypeParser.TypeConstructorContext ctx) {
this.addParam(Type.con(ctx.getText()));
}
@Override
public final void enterConstantType(TypeParser.ConstantTypeContext ctx) {
this.enter();
}
@Override
public final void exitConstantType(TypeParser.ConstantTypeContext ctx) {
Type[] types = this.popParams();
this.addParam(Type.app(types));
}
@Override
public void enterAppliedType(TypeParser.AppliedTypeContext ctx) {
this.enter();
}
@Override
public void exitAppliedType(TypeParser.AppliedTypeContext ctx) {
Type[] types = this.popParams();
this.addParam(Type.app(types));
}
/** Call this when entering a compound (function, list, tuple) type. */
private void enter() {
this.stack.push(new ArrayList<>());
}
/**
* Call this when adding a part to a compound type.
*
* @param t The type to addParam.
*/
private void addParam(Type t) {
this.stack.peek().add(t);
}
/**
* Utility method that pops and converts to an array.
*
* @return The topmost stack element as an array of Types.
*/
private Type[] popParams() {
List<Type> p = this.stack.pop();
return p.toArray(new Type[p.size()]);
}
/**
* Checks and returns the parse result.
*
* @return The result of the parse.
*/
public Type result() {
this.assertTrue(this.stack.size() == 1);
return this.stack.pop().get(0);
}
/**
* Version of assert that also works when Java assertions are off.
* @param condition This should be true.
*/
private void assertTrue(boolean condition) {
if (!condition) {
throw new RuntimeException("assertTrue failed while treebuilding");
}
}
}