de.codesourcery.jasm16.ast.ASTUtils.java Source code

Java tutorial

Introduction

Here is the source code for de.codesourcery.jasm16.ast.ASTUtils.java

Source

/**
 * Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.codesourcery.jasm16.ast;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;

import org.apache.commons.lang.StringUtils;

import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.WordAddress;
import de.codesourcery.jasm16.compiler.ICompilationUnit;
import de.codesourcery.jasm16.compiler.SourceLocation;
import de.codesourcery.jasm16.utils.ITextRegion;

/**
 * Provides various utility methods related to ASTs and AST nodes.
 * 
 * @author tobias.gierke@code-sourcery.de
 */
public class ASTUtils {

    public static void visitInOrder(ASTNode node, ISimpleASTNodeVisitor<ASTNode> visitor) {
        final Iterator<ASTNode> iterator = createInOrderIterator(node);
        while (iterator.hasNext() && visitor.visit(iterator.next()))
            ;
    }

    protected enum VisitorResult {
        STOP, DONT_GO_DEEPER, CONTINUE_TRAVERSAL;
    }

    protected static final class IterationContext implements IIterationContext {
        private VisitorResult result;

        public void reset() {
            result = null;
        }

        private void setResult(VisitorResult result) {
            if (this.result != null) {
                throw new IllegalStateException("Result already set?");
            }
            this.result = result;
        }

        public boolean isDontGoDeeper() {
            return result == VisitorResult.DONT_GO_DEEPER;
        }

        public boolean isContinueTraveral() {
            return result == VisitorResult.CONTINUE_TRAVERSAL;
        }

        public boolean isStop() {
            return result == VisitorResult.STOP;
        }

        public VisitorResult getResult() {
            return result;
        }

        @Override
        public void stop() {
            setResult(VisitorResult.STOP);
        }

        @Override
        public void dontGoDeeper() {
            setResult(VisitorResult.DONT_GO_DEEPER);
        }

        @Override
        public void continueTraversal() {
            setResult(VisitorResult.CONTINUE_TRAVERSAL);
        }
    }

    public static void visitInOrder(ASTNode node, IASTNodeVisitor<ASTNode> visitor) {
        final IterationContext context = new IterationContext();
        visitInOrder(node, context, 0, visitor);
    }

    public static void visitInOrder(ASTNode node, IterationContext context, int depth,
            IASTNodeVisitor<ASTNode> visitor) {
        context.reset();
        visitor.visit(node, context);
        if (!context.isStop() && !context.isDontGoDeeper()) {
            for (ASTNode child : node.getChildren()) {
                visitInOrder(child, context, depth + 1, visitor);
            }
        }
    }

    public static void visitPostOrder(ASTNode node, IASTNodeVisitor<ASTNode> visitor) {
        final IterationContext context = new IterationContext();
        visitPostOrder(node, context, 0, visitor);
    }

    public static void visitPostOrder(ASTNode node, IterationContext context, int depth,
            IASTNodeVisitor<ASTNode> visitor) {
        int currentDepth = depth;
        for (ASTNode child : node.getChildren()) {
            visitPostOrder(child, context, currentDepth + 1, visitor);
        }

        if (!context.isStop()) {
            context.reset();
            visitor.visit(node, context);
        }
    }

    /*             3
     *            / \
     * (child 1) 2   1 (child 0)
     */
    public static Iterator<ASTNode> createDepthFirst(ASTNode node) {
        final Stack<ASTNode> stack = new Stack<ASTNode>();
        if (node != null) {
            stack.push(node);
        }
        return new Iterator<ASTNode>() {

            @Override
            public boolean hasNext() {
                return !stack.isEmpty();
            }

            @Override
            public ASTNode next() {
                ASTNode n = stack.peek();
                for (ASTNode child : n.getChildren()) {
                    stack.push(child);
                }
                return stack.pop();
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("Not implemented");
            }
        };
    }

    /*             1
     *            / \
     * (child 1) 5   2 (child 0)
     *               /\
     *              4  3
     */
    public static Iterator<ASTNode> createInOrderIterator(ASTNode node) {
        if (node == null) {
            throw new IllegalArgumentException("node must not be NULL");
        }
        final Stack<ASTNode> stack = new Stack<ASTNode>();
        stack.push(node);
        return new Iterator<ASTNode>() {

            @Override
            public boolean hasNext() {
                return !stack.isEmpty();
            }

            /*    A (1)
             *    |\
             *(5) E B (2)
             *    |\ 
             *(4) D C (3)
             */
            @Override
            public ASTNode next() {
                if (stack.isEmpty()) {
                    throw new NoSuchElementException();
                }
                ASTNode result = stack.pop();

                final List<ASTNode> children = result.getChildren();
                Collections.reverse(children);

                for (ASTNode child : children) {
                    stack.push(child);
                }
                return result;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    public static <T extends ASTNode> List<T> getNodesByType(final ASTNode root, final Class<T> clazz,
            final boolean excludeInputNode) {

        final List<T> result = new ArrayList<T>();
        final ISimpleASTNodeVisitor<ASTNode> visitor = new ISimpleASTNodeVisitor<ASTNode>() {

            @SuppressWarnings("unchecked")
            @Override
            public boolean visit(ASTNode node) {
                if (clazz.isAssignableFrom(node.getClass())) {
                    if (!excludeInputNode || node != root) {
                        result.add((T) node);
                    }
                }
                return true;
            }
        };
        // do NOT change the order here, AST#parseInternal() checks
        // .org directives for ascending values and relies
        // on the fact that this method returns the nodes in-order !
        visitInOrder(root, visitor);
        return result;
    }

    public static <T extends ASTNode> void visitNodesByType(ASTNode root, final ISimpleASTNodeVisitor<T> visitor,
            final Class<T> clazz) {

        final ISimpleASTNodeVisitor<ASTNode> visitor2 = new ISimpleASTNodeVisitor<ASTNode>() {

            @SuppressWarnings("unchecked")
            @Override
            public boolean visit(ASTNode node) {
                if (clazz.isAssignableFrom(node.getClass())) {
                    return visitor.visit((T) node);
                }
                return true;
            }
        };
        visitInOrder(root, visitor2);
    }

    public static <T extends ASTNode> boolean containsNodeWithType(ASTNode node, Class<T> clazz) {
        final boolean[] result = { false };
        final ISimpleASTNodeVisitor<T> visitor = new ISimpleASTNodeVisitor<T>() {

            @Override
            public boolean visit(T node) {
                result[0] = true;
                return false;
            }
        };
        visitNodesByType(node, visitor, clazz);
        return result[0];
    }

    public static final void printAST(ASTNode n, String source) {
        printAST(n, 1, source);
    }

    public static final void printAST(ASTNode n, int depth, String source) {
        final String indent = StringUtils.repeat(" ", depth * 2);
        System.out.println(indent + n.toString() + " >" + n.getTextRegion().apply(source) + "<");

        for (ASTNode child : n.getChildren()) {
            printAST(child, depth + 1, source);
        }
    }

    public static final void printAST(ASTNode n) {
        printAST(n, 1);
    }

    public static final void printAST(ASTNode n, int depth) {
        final String indent = StringUtils.repeat(" ", depth * 2);
        System.out.println(indent + n.toString());
        for (ASTNode child : n.getChildren()) {
            printAST(child, depth + 1);
        }
    }

    public static String printAST(ICompilationUnit unit, AST ast) throws IOException {

        if (ast == null) {
            return "<NULL AST>";
        }
        final StringBuilder result = new StringBuilder();
        printAST(unit, ast, 0, result);
        return result.toString();
    }

    private static void printAST(ICompilationUnit unit, ASTNode currentNode, int currentDepth, StringBuilder result)
            throws IOException {
        final String indent = StringUtils.repeat(" ", currentDepth * 2);

        final String contents = unit.getSource(currentNode.getTextRegion());
        final String src = ">" + contents + "<";
        result.append(indent + " " + currentNode.getClass().getSimpleName() + " (" + src + ")").append("\n");
        for (ASTNode child : currentNode.getChildren()) {
            printAST(unit, child, currentDepth + 1, result);
        }
    }

    public static boolean isEquals(ASTNode n1, ASTNode n2) {
        final Iterator<ASTNode> it1 = ASTUtils.createInOrderIterator(n1);
        final Iterator<ASTNode> it2 = ASTUtils.createInOrderIterator(n2);
        while (it1.hasNext() && it2.hasNext()) {
            if (!it1.next().equals(it2.next())) {
                return false;
            }
        }
        if (it1.hasNext() != it2.hasNext()) {
            return false;
        }
        return true;
    }

    public static int getRegisterReferenceCount(ASTNode node) {
        return ASTUtils.getNodesByType(node, RegisterReferenceNode.class, false).size();
    }

    /**
     * Returns the earliest memory location from a given subtree.
     * 
     * @param node
     * @return earliest memory location or <code>null</code> if the subtree either
     * did not contain any {@link ObjectCodeOutputNode}s or none of the nodes had
     * an address assigned to yet.
     */
    public static Address getEarliestMemoryLocation(ASTNode node) {
        return findMemoryLocation(node, true);
    }

    public static Address getLatestMemoryLocation(ASTNode node) {
        return findMemoryLocation(node, false);
    }

    private static Address findMemoryLocation(ASTNode node, final boolean findEarliest) {
        final Address[] result = { null };
        final ISimpleASTNodeVisitor<ASTNode> visitor = new ISimpleASTNodeVisitor<ASTNode>() {

            @Override
            public boolean visit(ASTNode node) {
                if (node instanceof ObjectCodeOutputNode) {
                    final Address adr = ((ObjectCodeOutputNode) node).getAddress();
                    if (adr != null) {
                        if (result[0] == null) {
                            result[0] = adr;
                            if (result[0].equals(WordAddress.ZERO)) {
                                System.out.println("Node with ZERO address: " + node);
                            }
                        } else if ((findEarliest && adr.isLessThan(result[0]))
                                || (!findEarliest && adr.isGreaterThan(result[0]))) {
                            result[0] = adr;
                            if (result[0].equals(WordAddress.ZERO)) {
                                System.out.println("Node with ZERO address: " + node);
                            }
                        }
                    }
                }
                return true;
            }
        };
        visitInOrder(node, visitor);
        return result[0];
    }

    public static void debugPrintTextRegions(ASTNode node, final String source) {
        debugPrintTextRegions(node, source, null);
    }

    public static void debugPrintTextRegions(ASTNode node, final String source, final ICompilationUnit unit) {
        ASTUtils.visitInOrder(node, new ISimpleASTNodeVisitor<ASTNode>() {
            @Override
            public boolean visit(ASTNode node) {
                ITextRegion region = node.getTextRegion();
                if (region != null) {
                    int len = region.getEndOffset() - region.getStartingOffset() - 1;
                    if (len < 0) {
                        len = 0;
                    }
                    String regionString = StringUtils.repeat(" ", region.getStartingOffset()) + "^"
                            + StringUtils.repeat(" ", len) + "^";
                    String bodyText = null;
                    try {
                        region.apply(source).replace("\n", "|").replace("\r", "|");
                        bodyText = source.replace("\n", "|").replace("\r", "|");
                        bodyText += "\n" + regionString;
                    } catch (Exception e) {
                        bodyText = "<Invalid region>";
                    }
                    String loc = "";
                    if (unit != null) {
                        SourceLocation sourceLocation = unit.getSourceLocation(region);
                        if (sourceLocation != null) {
                            loc = "[ " + sourceLocation.toString() + " ]";
                        }
                    }
                    System.out.print("\n-----\nAST Node " + node.getClass().getSimpleName() + " convers range "
                            + region + " " + loc + "\n---\n" + bodyText);
                } else {
                    System.err.print(
                            "\n-----\nAST Node " + node.getClass().getSimpleName() + " has no text region set");
                }
                return true;
            }
        });
    }
}