001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 the ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package eu.cqse.check.framework.shallowparser.languages.cpp;
018
019import static eu.cqse.check.framework.scanner.ETokenType.LBRACK;
020import static eu.cqse.check.framework.scanner.ETokenType.RBRACK;
021import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_EXPRESSION;
022
023import java.util.EnumSet;
024import java.util.List;
025
026import eu.cqse.check.framework.scanner.ETokenType;
027import eu.cqse.check.framework.scanner.IToken;
028import eu.cqse.check.framework.shallowparser.TokenStreamUtils;
029import eu.cqse.check.framework.shallowparser.framework.ParserState;
030import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
031import eu.cqse.check.framework.shallowparser.languages.base.CStyleShallowParserBase;
032import eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates;
033
034/**
035 * Recognizer that finds C++ lambdas and continues parsing within these.
036 */
037public class CppLambdaRecognizer extends RecognizerBase<EGenericParserStates> {
038
039        private EnumSet<ETokenType> typeOrIdentifier;
040
041        /**
042         * Constructor. Needs to know which token types represent built-in types or
043         * identifiers to avoid treating int[0] {} as a lambda expression.
044         */
045        public CppLambdaRecognizer(EnumSet<ETokenType> typeOrIdentifier) {
046                this.typeOrIdentifier = typeOrIdentifier;
047        }
048
049        /** {@inheritDoc} */
050        @Override
051        protected int matchesLocally(ParserState<EGenericParserStates> parserState, List<IToken> tokens, int startOffset) {
052                if (isPrecededByType(tokens, startOffset)) {
053                        return NO_MATCH;
054                }
055                int currentOffset = skipCaptureList(tokens, startOffset);
056                if (currentOffset == NO_MATCH) {
057                        return NO_MATCH;
058                }
059
060                currentOffset = CStyleShallowParserBase.skipOptionalParameters(tokens, currentOffset);
061                if (currentOffset == NO_MATCH) {
062                        return NO_MATCH;
063                }
064
065                // At this point we cannot be sure if this is really a lambda, but the
066                // rule in the parser does the final check whether this is a lambda or
067                // not.
068                return parserState.parse(IN_EXPRESSION, tokens, startOffset);
069        }
070
071        /**
072         * Whether the token at the current offset is preceded by a type (built-in or
073         * user-defined)
074         */
075        private boolean isPrecededByType(List<IToken> tokens, int currentOffset) {
076                IToken preceedingToken = tokens.get(currentOffset - 1);
077                return typeOrIdentifier.contains(preceedingToken.getType());
078        }
079
080        /**
081         * Skips over the capture list and returns the new offset or
082         * {@link RecognizerBase#NO_MATCH} if none is found.
083         */
084        private static int skipCaptureList(List<IToken> tokens, int currentOffset) {
085                if (!TokenStreamUtils.hasTokenTypeSequence(tokens, currentOffset, LBRACK)) {
086                        return NO_MATCH;
087                }
088
089                // if a second bracket follows, this is not a lambda but rather an annotation
090                if (currentOffset + 1 >= tokens.size() || tokens.get(currentOffset + 1).getType() == LBRACK) {
091                        return NO_MATCH;
092                }
093
094                int closingBracket = TokenStreamUtils.findMatchingClosingToken(tokens, currentOffset + 1, LBRACK, RBRACK);
095                if (closingBracket == TokenStreamUtils.NOT_FOUND) {
096                        return NO_MATCH;
097                }
098                return closingBracket + 1;
099        }
100
101}