de.se_rwth.langeditor.util.antlr.ParseTrees.java Source code

Java tutorial

Introduction

Here is the source code for de.se_rwth.langeditor.util.antlr.ParseTrees.java

Source

/*******************************************************************************
 * MontiCore Language Workbench
 * Copyright (c) 2015, 2016, MontiCore, All rights reserved.
 *  
 * This project is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this project. If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package de.se_rwth.langeditor.util.antlr;

import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.getLast;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Trees;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import de.se_rwth.langeditor.util.Misc;

public final class ParseTrees {

    private ParseTrees() {
        // noninstantiable
    }

    public static Interval tokenInterval(ParseTree parseTree) {
        Optional<TerminalNode> firstTerminal = getFirstTerminal(parseTree);
        Optional<TerminalNode> lastTerminal = getLastTerminal(parseTree);
        if (firstTerminal.isPresent() && lastTerminal.isPresent()) {
            int startIndex = firstTerminal.get().getSymbol().getStartIndex();
            int stopIndex = lastTerminal.get().getSymbol().getStopIndex() + 1;
            return new Interval(startIndex, stopIndex);
        } else {
            return Interval.INVALID;
        }
    }

    public static Stream<ParseTree> successors(ParseTree parseTree) {
        return Misc.preorder(parseTree, ParseTrees::getChildren);
    }

    public static List<ParseTree> getChildren(ParseTree parseTree) {
        List<ParseTree> children = new ArrayList<ParseTree>();
        for (int i = 0; i < parseTree.getChildCount(); i++) {
            children.add(parseTree.getChild(i));
        }
        return children;
    }

    public static ParseTree root(ParseTree parseTree) {
        ParseTree root = parseTree;
        while (root.getParent() != null) {
            root = root.getParent();
        }
        return root;
    }

    public static Optional<TerminalNode> getTerminalBySourceCharIndex(ParseTree parseTree, int documentOffset) {
        List<TerminalNode> terminals = Trees.getDescendants(parseTree).stream()
                .filter(TerminalNode.class::isInstance).map(TerminalNode.class::cast).collect(Collectors.toList());
        for (TerminalNode terminal : terminals) {
            int startIndex = terminal.getSymbol().getStartIndex();
            int stopIndex = terminal.getSymbol().getStopIndex() + 1;
            if (startIndex <= documentOffset && documentOffset < stopIndex) {
                return Optional.of(terminal);
            }
        }
        return Optional.empty();
    }

    /** Find smallest subtree of t enclosing range start..stop
     *  inclusively using postorder traversal.  Recursive depth-first-search.
     *
     *  @since 4.5.1
     */
    public static ParserRuleContext getRootOfSubtreeEnclosingRegion(ParseTree t, int start, // inclusive
            int stop) // inclusive
    {
        int n = t.getChildCount();
        for (int i = 0; i < n; i++) {
            ParseTree child = t.getChild(i);
            ParserRuleContext r = getRootOfSubtreeEnclosingRegion(child, start, stop);
            if (r != null)
                return r;
        }
        if (t instanceof ParserRuleContext) {
            ParserRuleContext r = (ParserRuleContext) t;
            if (start >= r.getStart().getStartIndex() && // is range fully contained in t?
                    (r.getStop() == null || stop <= r.getStop().getStopIndex())) {
                // note: r.getStop()==null likely implies that we bailed out of parser and there's nothing to the right
                return r;
            }
        }
        return null;
    }

    public static Optional<TerminalNode> getTerminalByLineAndColumn(ParseTree parseTree, int line, int column) {
        List<TerminalNode> terminals = Trees.getDescendants(parseTree).stream()
                .filter(TerminalNode.class::isInstance).map(TerminalNode.class::cast).collect(Collectors.toList());
        for (TerminalNode terminal : terminals) {
            boolean sameLine = terminal.getSymbol().getLine() == line;
            boolean sameColumn = terminal.getSymbol().getCharPositionInLine() == column;
            if (sameLine && sameColumn) {
                return Optional.of(terminal);
            }
        }
        return Optional.empty();
    }

    public static Optional<TerminalNode> getFirstTerminal(ParseTree parseTree) {
        return Trees.getDescendants(parseTree).stream().filter(TerminalNode.class::isInstance)
                .map(TerminalNode.class::cast).findFirst();
    }

    public static Optional<TerminalNode> getLastTerminal(ParseTree parseTree) {
        TerminalNode lastTerminal = getLast(filter(Trees.getDescendants(parseTree), TerminalNode.class), null);
        return Optional.ofNullable(lastTerminal);
    }

    public static Optional<Token> getFirstToken(ParseTree parseTree) {
        return getFirstTerminal(parseTree).map(TerminalNode::getSymbol);
    }

    public static Optional<Token> getLastToken(ParseTree parseTree) {
        return getLastTerminal(parseTree).map(TerminalNode::getSymbol);
    }

    public static int getTokenLength(Token token) {
        return (token.getStopIndex() + 1) - token.getStartIndex();
    }

    public static ImmutableSet<ParseTree> filterContexts(ParseTree parseTree,
            ImmutableSet<Class<? extends ParseTree>> types) {
        Set<ParserRuleContext> matchingRules = Trees.getDescendants(parseTree).stream()
                .filter(descendant -> types.contains(descendant.getClass())).map(ParserRuleContext.class::cast)
                .collect(Collectors.toSet());
        return ImmutableSet.copyOf(matchingRules);
    }

    public static ImmutableList<ParseTree> bottomUpAncestors(ParseTree parseTree) {
        return ImmutableList
                .copyOf(Iterables.filter(Lists.reverse(Trees.getAncestors(parseTree)), ParseTree.class));
    }
}