All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.mihosoft.vmf.vmftext.ParseTreeUtil Maven / Gradle / Ivy

/*
 * Copyright 2017-2018 Michael Hoffer . All rights reserved.
 * Copyright 2017-2018 Goethe Center for Scientific Computing, University Frankfurt. All rights reserved.
 *
 * 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.
 *
 * If you use this software for scientific research then please cite the following publication(s):
 *
 * M. Hoffer, C. Poliwoda, & G. Wittum. (2013). Visual reflection library:
 * a framework for declarative GUI programming on the Java platform.
 * Computing and Visualization in Science, 2013, 16(4),
 * 181–192. http://doi.org/10.1007/s00791-014-0230-y
 */
package eu.mihosoft.vmf.vmftext;

import eu.mihosoft.vmf.vmftext.grammar.CodeLocation;
import eu.mihosoft.vmf.vmftext.grammar.CodeRange;
import eu.mihosoft.vmf.vmftext.grammar.antlr4.ANTLRv4Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;

/**
 * Utility class for ANTLR4 parse trees. The utility methods provided by this class are
 * mostly useful for determining the type of specified element contexts.
 *
 * @author Michael Hoffer 
 */
public class ParseTreeUtil {

    private ParseTreeUtil() {
        throw new AssertionError("Don't instantiate me!");
    }

    /**
     * Indicates whether the specified element context is a rule block.
     * @param e the element context to check
     * @return {@code true} if the specified context is a rule block; {@code false} otherwise
     */
    public static boolean isRuleBlock(ANTLRv4Parser.ElementContext e) {
        return e.labeledElement().atom()== null && e.labeledElement().block()!=null;
    }

    /**
     * Indicates whether the specified element context is a parser rule.
     * @param e the element context to check
     * @return {@code true} if the specified celement ontext is a parser rule; {@code false} otherwise
     */
    public static boolean isParserRule(ANTLRv4Parser.ElementContext e) {

        if(e.labeledElement() == null || e.labeledElement().atom()==null) return false;

        return e.labeledElement().atom().ruleref() != null;
    }

    /**
     * Indicates whether the specified element context is a lexer rule.
     * @param e the element context to check
     * @return {@code true} if the specified element context is a lexer rule; {@code false} otherwise
     */
    public static boolean isLexerRule(ANTLRv4Parser.ElementContext e) {

        if(e.labeledElement()!=null && e.labeledElement().atom()!=null) {
            String atomText = e.labeledElement().atom().getText();

            // detect lexer rules with not operator
            if(atomText.length()> 1
                    && atomText.startsWith("~")
                    && Character.isUpperCase(atomText.codePointAt(1)))
                return true;
        }



        if(e.labeledElement() == null || e.labeledElement().atom()==null) return false;

        return e.labeledElement().atom().terminal() != null &&
                e.labeledElement().atom().terminal().TOKEN_REF() != null;
    }

    /**
     * Indicates whether the specified element context is a string literal.
     * @param e the element context to check
     * @return {@code true} if the specified element context is a string literal; {@code false} otherwise
     */
    public static boolean isStringLiteral(ANTLRv4Parser.ElementContext e) {

        if(e.labeledElement()!=null && e.labeledElement().atom()!=null) {
            String atomText = e.labeledElement().atom().getText();

            // detect literals with not operator
            if(atomText.startsWith("~'")) return true;
        }


        if(e.labeledElement() == null || e.labeledElement().atom()==null) return false;

        return e.labeledElement().atom().terminal() != null &&
                e.labeledElement().atom().terminal().TOKEN_REF() == null;
    }

    /**
     * Indicates whether the specified element context is negated, i.e. {@code ~[ABC]}
     * @param e the element context to check
     * @return {@code true} if the specified element context is negated; {@code false} otherwise
     */
    public static boolean isNegated(ANTLRv4Parser.ElementContext e) {

        if(isParserRule(e)) {
            // parser rules can't be negated
            return false;
        } else if(e.atom()!=null) {
            return e.atom().notSet()!=null;
        } else if(e.labeledElement()!=null && e.labeledElement().atom()!=null) {
            return e.labeledElement().atom().notSet()!=null;
        }

        return false;
    }

    /**
     * Returns the text of the specified element context.
     * @param e element context
     * @return the text of the specified element context
     */
    public static String getElementText(ANTLRv4Parser.ElementContext e) {
        if(isParserRule(e)) {
            return e.labeledElement().atom().ruleref().getText();
        } else if(isLexerRule(e)) {

            if(e.labeledElement().atom()!=null) {
                return e.labeledElement().atom().getText();
            }
            /*else {
                // default case
                return e.labeledElement().atom().terminal().TOKEN_REF().getText();
            }*/
        } else if(isStringLiteral(e)) {

            if(e.labeledElement().atom()!=null) {
                return e.labeledElement().atom().getText();
            }
            /*else {
                // default case
                return e.labeledElement().atom().terminal().STRING_LITERAL().getText();
            }*/
        }

        return null;
    }

    /**
     * Indicates whether the specified element context is labelled.
     * @param e the element context to check
     * @return {@code true} if the specified element context is labeled; {@code false} otherwise
     */
    public static boolean isLabeledElement(ANTLRv4Parser.ElementContext e) {
        return e.labeledElement() != null && e.labeledElement().identifier() != null;
    }

    /**
     * Returns the start location of the specified token.
     * @param t token
     * @return the start location of the specified token
     */
    public static CodeLocation tokenToCodeLocationStart(Token t) {
        return CodeLocation.newBuilder().
                withIndex(t.getStartIndex()).
                withCharPosInLine(t.getCharPositionInLine()).withLine(t.getLine()).build();
    }

    /**
     * Returns the stop/end location of the specified token.
     * @param t token
     * @param ctx parser rule context the token belongs to
     * @return the stop/end location of the specified token
     */
    public static CodeLocation tokenToCodeLocationStop(Token t, ParserRuleContext ctx) {
        int startIndex = t.getStartIndex();
        int stopIndex = t.getStopIndex();
        int diff = stopIndex - startIndex;

        return CodeLocation.newBuilder().
                withIndex(stopIndex).
                withCharPosInLine(t.getCharPositionInLine()+diff).
                withLine(/*TODO 14.02.2018 does not work for multi-line-tokens*/t.getLine()).build();
    }

    /**
     * Returns the code range of the specified context.
     * @param ctx the context to check
     * @return the code range of the specified context
     */
    public static CodeRange ctxToCodeRange(ParserRuleContext ctx) {
        // consider for stop line: https://stackoverflow.com/a/17487805
        CodeLocation start = tokenToCodeLocationStart(ctx.start);
        CodeLocation stop = tokenToCodeLocationStart(ctx.stop);
        return CodeRange.newBuilder().withStart(start).
                withStop(tokenToCodeLocationStop(ctx.stop,ctx))
                .withLength(stop.getIndex()-start.getIndex()+1/*+1 because of inclusive vs. exclusive*/).build();
    }

    /**
     * Indicates whether the specified element context is a labeled block element.
     * @param e element context to check
     * @return {@code true} if the specified element context is a labeled block element; {@code false} otherwise
     */
    public static boolean isLabeledBlockElement(ANTLRv4Parser.ElementContext e) {
        return isLabeledElement(e) && e.labeledElement().block()!=null;
    }

    /**
     * Indicates whether the specified element context is a block element.
     * @param e element context to check
     * @return {@code true} if the specified element context is a block element; {@code false} otherwise
     */
    public static boolean isBlockElement(ANTLRv4Parser.ElementContext e) {
        return e.labeledElement()!=null && e.labeledElement().block()!=null;
    }

    /**
     * Indicates whether the specified element context is an unlabeled block element.
     * @param e element context to check
     * @return {@code true} if the specified element context is an unlabeled block element; {@code false} otherwise
     */
    public static boolean isUnlabeledBlockElement(ANTLRv4Parser.ElementContext e) {
        return e.labeledElement()!=null && !isLabeledElement(e) && e.labeledElement().block()!=null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy