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

eu.cqse.check.framework.shallowparser.languages.groovy.GroovySkipToEndOfStatementRecognizer Maven / Gradle / Ivy

Go to download

The Teamscale Custom Check API allows users to extend Teamscale by writing custom analyses that create findings.

There is a newer version: 2024.7.2
Show newest version
/*
 * Copyright (c) CQSE GmbH
 *
 * 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 eu.cqse.check.framework.shallowparser.languages.groovy;

import static eu.cqse.check.framework.scanner.ETokenType.BACKSLASH;
import static eu.cqse.check.framework.scanner.ETokenType.COLON;
import static eu.cqse.check.framework.scanner.ETokenType.DOT;
import static eu.cqse.check.framework.scanner.ETokenType.LBRACK;
import static eu.cqse.check.framework.scanner.ETokenType.LPAREN;
import static eu.cqse.check.framework.scanner.ETokenType.MINUSMINUS;
import static eu.cqse.check.framework.scanner.ETokenType.NOT;
import static eu.cqse.check.framework.scanner.ETokenType.PLUSPLUS;
import static eu.cqse.check.framework.scanner.ETokenType.QUESTION;
import static eu.cqse.check.framework.scanner.ETokenType.RBRACE;
import static eu.cqse.check.framework.scanner.ETokenType.RBRACK;
import static eu.cqse.check.framework.scanner.ETokenType.RPAREN;
import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON;

import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import eu.cqse.check.framework.scanner.ETokenType;
import eu.cqse.check.framework.scanner.IToken;
import eu.cqse.check.framework.shallowparser.TokenStreamUtils;
import eu.cqse.check.framework.shallowparser.framework.ParserState;
import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;

/**
 * Recognizer for simple statements in Groovy. We need a separate recognizer as the rules for
 * statement continuation are non-trivial due to the optional semicolon.
 */
/* package */ class GroovySkipToEndOfStatementRecognizer extends RecognizerBase {

	/** Token types that force a statement to continue even in a new line. */
	private static final EnumSet CONTINUE_TOKENS = EnumSet.of(BACKSLASH, DOT, QUESTION, COLON, NOT);

	/**
	 * Token types of class OPERATOR that do not force a statement continuation.
	 */
	private static final EnumSet NON_CONTINUATION_OPERATORS = EnumSet.of(MINUSMINUS, PLUSPLUS);

	/** Matched tokens for nesting in complex Xtend statements. */
	private static final Map NESTING_MATCH = new EnumMap<>(ETokenType.class);

	static {
		NESTING_MATCH.put(LPAREN, RPAREN);
		NESTING_MATCH.put(LBRACK, RBRACK);
	}

	/**
	 * Defines if we should include the lastToken into the matching decision from the start on.
	 */
	private boolean forceMatch = false;

	/** @see #forceMatch */
	public void setForceMatch(boolean forceMatch) {
		this.forceMatch = forceMatch;
	}

	/** {@inheritDoc} */
	@Override
	protected int matchesLocally(ParserState parserState, List tokens,
			int startOffset) {
		if (startOffset < 0) {
			return NO_MATCH;
		}

		IToken lastToken = null;
		if (!forceMatch && startOffset > 0) {
			lastToken = tokens.get(startOffset - 1);
		}

		Stack expectedClosing = new Stack<>();

		while (true) {
			if (startOffset >= tokens.size()) {
				return startOffset;
			}

			IToken token = tokens.get(startOffset);
			ETokenType tokenType = token.getType();

			if (!expectedClosing.isEmpty() && tokenType == expectedClosing.peek()) {
				expectedClosing.pop();
			} else if (expectedClosing.isEmpty() && tokenType == SEMICOLON) {
				return startOffset + 1;
			} else if (expectedClosing.isEmpty() && TokenStreamUtils.startsNewStatement(token, lastToken,
					CONTINUE_TOKENS, NON_CONTINUATION_OPERATORS, RBRACE)) {
				return startOffset;
			} else if (NESTING_MATCH.containsKey(tokenType)) {
				expectedClosing.push(NESTING_MATCH.get(tokenType));
			} else {
				int next = startSubExpressionRecognizer(tokens, startOffset, parserState);
				startOffset = next;
				lastToken = tokens.get(startOffset - 1);
				continue;
			}

			lastToken = token;
			startOffset += 1;
		}
	}

	/**
	 * Directly starts a subparse. Returns either the new offset from the subparsers or increments the
	 * offset, if no subparse was triggered.
	 */
	private static int startSubExpressionRecognizer(List tokens, int startOffset,
			ParserState parserState) {
		GroovySubExpressionRecognizer subExpressionRecognizer = new GroovySubExpressionRecognizer();
		int next = subExpressionRecognizer.matches(parserState, tokens, startOffset);

		if (next == NO_MATCH) {
			return startOffset + 1;
		}
		return next;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy