package org.zwobble.couscous.ast.visitors; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.zwobble.couscous.ast.*; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Type; import org.zwobble.couscous.types.TypeParameter; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import static org.zwobble.couscous.util.ExtraIterables.lazyMap; import static org.zwobble.couscous.util.ExtraLists.*; public class NodeTransformer { public static List applyAll(List transformers, List types) { for (NodeTransformer transformer : transformers) { types = apply(transformer, types); } return types; } public static List apply(NodeTransformer transformer, List types) { return eagerFlatMap(types, transformer::transformTypeDeclaration); } public static NodeTransformer replaceExpressions(Map replacements) { return builder(). transformExpression(expression -> Optional.ofNullable(replacements.get(expression))) .build(); } public static Builder builder() { return new Builder(); } public static class Builder { private Function>> transformStatement = statement -> Optional.empty(); private Function> transformExpression = expression -> Optional.empty(); private Function transformType = type -> type; private Function transformMethodName = MethodSignature::getName; private Function transformFieldName = name -> name; private Function transformReceiver = receiver -> receiver; public Builder transformExpression( Function> transformExpression) { this.transformExpression = transformExpression; return this; } public Builder transformStatement( Function>> transformStatement) { this.transformStatement = transformStatement; return this; } public Builder transformType( Function transformType) { this.transformType = transformType; return this; } public Builder transformMethodName(Function transformMethodName) { this.transformMethodName = transformMethodName; return this; } public Builder transformFieldName(Function transformFieldName) { this.transformFieldName = transformFieldName; return this; } public Builder transformReceiver(Function receiver) { this.transformReceiver = receiver; return this; } public NodeTransformer build() { return new NodeTransformer( transformExpression, transformStatement, transformType, transformMethodName, transformFieldName, transformReceiver ); } } private final Function> transformExpression; private final Function>> transformStatement; private final Function transformType; private final Function transformMethodName; private final Function transformFieldName; private final Function transformReceiver; private NodeTransformer( Function> transformExpression, Function>> transformStatement, Function transformType, Function transformMethodName, Function transformFieldName, Function transformReceiver) { this.transformExpression = transformExpression; this.transformStatement = transformStatement; this.transformType = transformType; this.transformMethodName = transformMethodName; this.transformFieldName = transformFieldName; this.transformReceiver = transformReceiver; } public MethodSignature transform(MethodSignature signature) { return new MethodSignature( transformMethodName(signature), eagerMap(signature.getTypeParameters(), this::transform), eagerMap(signature.getArguments(), this::transform), transform(signature.getReturnType())); } public Type transform(Type type) { return transformType.apply(type); } public ScalarType transform(ScalarType type) { return (ScalarType)transformType.apply(type); } public TypeParameter transform(TypeParameter type) { return (TypeParameter)transformType.apply(type); } public String transformMethodName(MethodSignature signature) { return transformMethodName.apply(signature); } public String transformFieldName(String name) { return transformFieldName.apply(name); } public ExpressionNode transformExpression(ExpressionNode expression) { ExpressionNode transformed = expression.transformSubtree(this); return transformExpression.apply(transformed).orElse(transformed); } public Receiver transformReceiver(Receiver receiver) { return transformReceiver.apply(receiver.transformSubtree(this)); } public VariableDeclaration transform(VariableDeclaration declaration) { return VariableDeclaration.var(declaration.getId(), declaration.getName(), transform(declaration.getType())); } public final Iterable transformStatement(StatementNode statement) { StatementNode transformed = statement.transformSubtree(this); return transformStatement.apply(transformed).orElseGet(() -> list(transformed)); } public FormalTypeParameterNode transformFormalTypeParameter(FormalTypeParameterNode typeParameter) { return typeParameter.transformSubtree(this); } public FormalArgumentNode transformFormalArgument(FormalArgumentNode formalArgumentNode) { return formalArgumentNode.transformSubtree(this); } public AnnotationNode transformAnnotation(AnnotationNode annotation) { return annotation.transformSubtree(this); } public MethodNode transformMethod(MethodNode method) { return method.transformSubtree(this); } public ConstructorNode transformConstructor(ConstructorNode constructor) { return constructor.transformSubtree(this); } public FieldDeclarationNode transformField(FieldDeclarationNode field) { return field.transformSubtree(this); } public List transformTypeDeclaration(TypeNode typeNode) { return list(typeNode.transformSubtree(this)); } public ClassNode transformClass(ClassNode classNode) { return classNode.transformSubtree(this); } private Node transformInterface(InterfaceNode interfaceNode) { return interfaceNode.transformSubtree(this); } private Node transformEnum(EnumNode enumNode) { return enumNode.transformSubtree(this); } public List transformAnnotations(List annotations) { return transformList(annotations, this::transformAnnotation); } public Set transformTypes(Set superTypes) { return transformSet(superTypes, this::transform); } public List transformFields(List fields) { return transformList(fields, this::transformField); } public List transformMethods(List methods) { return transformList(methods, this::transformMethod); } public List transformStatements(List body) { return eagerFlatMap(body, this::transformStatement); } public List transformExpressions(List expressions) { return transformList(expressions, this::transformExpression); } public List transformFormalTypeParameters(List typeParameters) { return transformList(typeParameters, this::transformFormalTypeParameter); } public List transformFormalArguments(List arguments) { return transformList(arguments, this::transformFormalArgument); } private Set transformSet(Set values, Function function) { return ImmutableSet.copyOf(lazyMap(values, function)); } private List transformList(List values, Function function) { return ImmutableList.copyOf(lazyMap(values, function)); } }