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.preprocessor.c;
018
019import java.util.List;
020import java.util.Set;
021import java.util.regex.Matcher;
022import java.util.regex.Pattern;
023
024import org.conqat.lib.commons.collections.Pair;
025import org.conqat.lib.commons.string.StringUtils;
026
027import eu.cqse.check.framework.scanner.ELanguage;
028import eu.cqse.check.framework.scanner.IToken;
029import eu.cqse.check.framework.scanner.ScannerUtils;
030import eu.cqse.check.framework.shallowparser.framework.ShallowEntity;
031
032/**
033 * An implementation of the C preprocessor that is adjusted to application in
034 * static analysis. For this, various options of preprocessing can be adjusted.
035 * This class adds include handling to its base classes.
036 * 
037 * The design of the preprocessor uses inheritance to separate the aspects macro
038 * handling, include handling, and conditionals to separate classes. This is
039 * meant to keep the classes small and easier to understand, while still
040 * providing a final common class, that can be easily extended by subclassing
041 * and overriding certain methods, which would be hard when using
042 * delegation/composition.
043 */
044public class CPreprocessor extends ConditionalHandlingCPreprocessorBase {
045
046        /** Patterns used for parsing include statements. */
047        public static final Pattern INCLUDE_PATTERN = Pattern.compile("#\\s*include +[\"<](.+?)[\">]");
048
049        /**
050         * A post processing operation that removes all tokens that have been inserted
051         * by the C++ preprocessor as result of the macro expansion.
052         * 
053         * Modifies the given collection.
054         */
055        public static void removePreprocessorTokens(List<ShallowEntity> entities) {
056                ShallowEntity.filterTokens(entities,
057                                token -> token.getOriginId().equals(MacroHandlingCPreprocessorBase.MACRO_ORIGIN));
058                ShallowEntity.collapseEmptyEntities(entities);
059        }
060
061        /** Constructor. */
062        public CPreprocessor(IMacroProvider macroProvider) {
063                super(macroProvider);
064        }
065
066        /** {@inheritDoc} */
067        @Override
068        protected void handleInclude(String uniformPath, IToken token, List<IToken> result, int inclusionLimit,
069                        Set<String> seenPaths) {
070                if (inclusionLimit <= 0) {
071                        return;
072                }
073
074                Matcher matcher = INCLUDE_PATTERN.matcher(token.getText());
075                if (!matcher.find()) {
076                        return;
077                }
078
079                String includedName = matcher.group(1);
080                IncludedTokens pathAndTokens = resolveIncludedTokens(uniformPath, includedName);
081                result.addAll(preprocess(pathAndTokens.getFirst(), pathAndTokens.getSecond(), inclusionLimit - 1, seenPaths));
082        }
083
084        /**
085         * Returns the included tokens for a given include name.
086         * 
087         * @param includingUniformPath
088         *            the uniform path of the file that includes this. May be null if no
089         *            information is available.
090         * @param includedName
091         *            the name of the file to be included.
092         * @return the pair of the name of the resolved uniform path of the include file
093         *         and the file's tokens. The resolved uniform path may be null, if it
094         *         is not available.
095         */
096        protected IncludedTokens resolveIncludedTokens(String includingUniformPath, String includedName) {
097                Pair<String, String> content = resolveIncludeContent(includingUniformPath, includedName);
098
099                return new IncludedTokens(content.getFirst(),
100                                ScannerUtils.getTokens(content.getSecond(), ELanguage.CPP, includedName));
101        }
102
103        /**
104         * Returns the resolved uniform path and content of an included file. The
105         * default implementation returns a pair of null and the empty string.
106         * 
107         * @param includingUniformPath
108         *            the uniform path of the file that includes this. May be null if no
109         *            information is available.
110         * @param includedName
111         *            the name of the file to be included.
112         * @return the pair of the name of the resolved uniform path of the include file
113         *         and the file's content. The resolved uniform path may be null, if it
114         *         is not available.
115         */
116        protected Pair<String, String> resolveIncludeContent(String includingUniformPath, String includedName) {
117                return new Pair<>(null, StringUtils.EMPTY_STRING);
118        }
119
120}