package org.zwobble.couscous.ast.structure; import org.zwobble.couscous.ast.*; import org.zwobble.couscous.ast.visitors.NodeMapper; import java.util.Collection; import java.util.function.Predicate; import java.util.stream.Stream; import static org.zwobble.couscous.util.ExtraStreams.concatStreams; public class NodeStructure { public static Stream descendantNodesAndSelf(Node node, Predicate predicate) { return Stream.concat(Stream.of(node), descendantNodes(node, predicate)); } public static Stream descendantNodes(Node node, Predicate predicate) { return childNodes(node) .filter(predicate) .flatMap(child -> descendantNodesAndSelf(child, predicate)); } public static Stream descendantNodesAndSelf(Node node) { return Stream.concat(Stream.of(node), descendantNodes(node)); } public static Stream descendantNodes(Node node) { return childNodes(node).flatMap(NodeStructure::descendantNodesAndSelf); } public static Stream childNodes(Node node) { // TODO: is this just duplicating the logic already in node.transform? return node.accept(new NodeMapper>() { @Override public Stream visit(LiteralNode literal) { return Stream.empty(); } @Override public Stream visit(VariableReferenceNode variableReference) { return Stream.empty(); } @Override public Stream visit(ThisReferenceNode reference) { return Stream.empty(); } @Override public Stream visit(ArrayNode array) { return array.getElements().stream(); } @Override public Stream visit(AssignmentNode assignment) { return Stream.of(assignment.getValue(), assignment.getTarget()); } @Override public Stream visit(TernaryConditionalNode ternaryConditional) { return Stream.of( ternaryConditional.getCondition(), ternaryConditional.getIfTrue(), ternaryConditional.getIfFalse()); } @Override public Stream visit(MethodCallNode methodCall) { return Stream.concat( Stream.of(methodCall.getReceiver()), methodCall.getArguments().stream()); } @Override public Stream visit(ConstructorCallNode call) { return call.getArguments().stream(); } @Override public Stream visit(OperationNode operation) { return operation.getArguments().stream(); } @Override public Stream visit(InstanceOfNode instanceOf) { return Stream.of(instanceOf.getLeft()); } @Override public Stream visit(FieldAccessNode fieldAccess) { return Stream.of(fieldAccess.getLeft()); } @Override public Stream visit(TypeCoercionNode typeCoercion) { return Stream.of(typeCoercion.getExpression()); } @Override public Stream visit(CastNode cast) { return Stream.of(cast.getExpression()); } @Override public Stream visit(InstanceReceiver receiver) { return Stream.of(receiver.getExpression()); } @Override public Stream visit(StaticReceiver receiver) { return Stream.empty(); } @Override public Stream visit(ReturnNode returnNode) { return Stream.of(returnNode.getValue()); } @Override public Stream visit(ThrowNode throwNode) { return Stream.of(throwNode.getValue()); } @Override public Stream visit(ExpressionStatementNode expressionStatement) { return Stream.of(expressionStatement.getExpression()); } @Override public Stream visit(LocalVariableDeclarationNode localVariableDeclaration) { return Stream.of(localVariableDeclaration.getInitialValue()); } @Override public Stream visit(IfStatementNode ifStatement) { return concatStreams( Stream.of(ifStatement.getCondition()), ifStatement.getTrueBranch().stream(), ifStatement.getFalseBranch().stream()); } @Override public Stream visit(WhileNode whileLoop) { return Stream.concat( Stream.of(whileLoop.getCondition()), whileLoop.getBody().stream()); } @Override public Stream visit(TryNode tryStatement) { return Stream.concat( tryStatement.getBody().stream(), tryStatement.getExceptionHandlers().stream().flatMap(handler -> handler.getBody().stream())); } @Override public Stream visit(FormalTypeParameterNode parameter) { return Stream.empty(); } @Override public Stream visit(FormalArgumentNode formalArgumentNode) { return Stream.empty(); } @Override public Stream visit(AnnotationNode annotation) { return Stream.empty(); } @Override public Stream visit(ClassNode classNode) { return concatStreams( Stream.of(classNode.getConstructor()), classNode.getFields().stream(), classNode.getMethods().stream()); } @Override public Stream visit(InterfaceNode interfaceNode) { return interfaceNode.getMethods().stream(); } @Override public Stream visit(EnumNode enumNode) { return Stream.empty(); } @Override public Stream visit(MethodNode methodNode) { return concatStreams( methodNode.getAnnotations().stream(), methodNode.getArguments().stream(), methodNode.getBody().map(Collection::stream).orElse(Stream.empty())); } @Override public Stream visit(ConstructorNode constructorNode) { return Stream.concat(constructorNode.getArguments().stream(), constructorNode.getBody().stream()); } @Override public Stream visit(FieldDeclarationNode declaration) { return Stream.empty(); } }); } }