blob: 2e4f8053408a244931a895a3a6427243f268a831 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
* Anton Leherbauer (Wind River Systems)
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.cdt.core.dom.parser.IScannerExtensionConfiguration;
import org.eclipse.cdt.core.index.IIndexMacro;
import org.eclipse.cdt.core.parser.EndOfFileException;
import org.eclipse.cdt.core.parser.FileContent;
import org.eclipse.cdt.core.parser.IExtendedScannerInfo;
import org.eclipse.cdt.core.parser.IMacro;
import org.eclipse.cdt.core.parser.IParserLogService;
import org.eclipse.cdt.core.parser.IPreprocessorDirective;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.IncludeFileContentProvider;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.core.parser.OffsetLimitReachedException;
import org.eclipse.cdt.core.parser.ParseError;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.util.CharArrayIntMap;
import org.eclipse.cdt.core.parser.util.CharArrayMap;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics;
import org.eclipse.cdt.internal.core.parser.EmptyFilesProvider;
import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator.EvalException;
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent.InclusionKind;
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
import org.eclipse.cdt.internal.core.parser.scanner.MacroDefinitionParser.InvalidMacroDefinitionException;
import org.eclipse.cdt.internal.core.parser.scanner.ScannerContext.BranchKind;
import org.eclipse.cdt.internal.core.parser.scanner.ScannerContext.CodeState;
import org.eclipse.cdt.internal.core.parser.scanner.ScannerContext.Conditional;
import org.eclipse.core.runtime.IAdaptable;
/**
* C-Preprocessor providing tokens for the parsers. The class should not be used directly, rather than that
* you should be using the {@link IScanner} interface.
* @since 5.0
*/
public class CPreprocessor implements ILexerLog, IScanner, IAdaptable {
public static final String PROP_VALUE = "CPreprocessor"; //$NON-NLS-1$
public static final int tDEFINED= IToken.FIRST_RESERVED_PREPROCESSOR;
public static final int tEXPANDED_IDENTIFIER= IToken.FIRST_RESERVED_PREPROCESSOR+1;
public static final int tSCOPE_MARKER= IToken.FIRST_RESERVED_PREPROCESSOR+2;
public static final int tSPACE= IToken.FIRST_RESERVED_PREPROCESSOR+3;
public static final int tNOSPACE= IToken.FIRST_RESERVED_PREPROCESSOR+4;
public static final int tMACRO_PARAMETER= IToken.FIRST_RESERVED_PREPROCESSOR+5;
private static final int ORIGIN_PREPROCESSOR_DIRECTIVE = OffsetLimitReachedException.ORIGIN_PREPROCESSOR_DIRECTIVE;
private static final int ORIGIN_INACTIVE_CODE = OffsetLimitReachedException.ORIGIN_INACTIVE_CODE;
private static final char[] EMPTY_CHAR_ARRAY = new char[0];
private static final char[] ONE = "1".toCharArray(); //$NON-NLS-1$
// standard built-ins
private static final ObjectStyleMacro __CDT_PARSER__= new ObjectStyleMacro("__CDT_PARSER__".toCharArray(), ONE); //$NON-NLS-1$
private static final ObjectStyleMacro __cplusplus = new ObjectStyleMacro("__cplusplus".toCharArray(), ONE); //$NON-NLS-1$
private static final ObjectStyleMacro __STDC__ = new ObjectStyleMacro("__STDC__".toCharArray(), ONE); //$NON-NLS-1$
private static final ObjectStyleMacro __STDC_HOSTED__ = new ObjectStyleMacro("__STDC_HOSTED__".toCharArray(), ONE); //$NON-NLS-1$
private static final ObjectStyleMacro __STDC_VERSION__ = new ObjectStyleMacro("__STDC_VERSION__".toCharArray(), "199901L".toCharArray()); //$NON-NLS-1$ //$NON-NLS-2$
private static final DynamicMacro __FILE__= new FileMacro("__FILE__".toCharArray()); //$NON-NLS-1$
private static final DynamicMacro __DATE__= new DateMacro("__DATE__".toCharArray()); //$NON-NLS-1$
private static final DynamicMacro __TIME__ = new TimeMacro("__TIME__".toCharArray()); //$NON-NLS-1$
private static final DynamicMacro __LINE__ = new LineMacro("__LINE__".toCharArray()); //$NON-NLS-1$
private static final int NO_EXPANSION = 0x01;
private static final int PROTECT_DEFINED = 0x02;
private static final int STOP_AT_NL = 0x04;
private static final int CHECK_NUMBERS = 0x08;
private interface IIncludeFileTester<T> {
T checkFile(String path, boolean isHeuristicMatch, IncludeSearchPathElement onPath);
}
final private IIncludeFileTester<InternalFileContent> createCodeReaderTester= new IIncludeFileTester<InternalFileContent>() {
public InternalFileContent checkFile(String path, boolean isHeuristicMatch, IncludeSearchPathElement onPath) {
final InternalFileContent fc= fFileContentProvider.getContentForInclusion(path);
if (fc != null) {
fc.setFoundByHeuristics(isHeuristicMatch);
fc.setFoundOnPath(onPath);
}
return fc;
}
};
private static class IncludeResolution {String fLocation; boolean fHeuristic;}
final private IIncludeFileTester<IncludeResolution> createPathTester= new IIncludeFileTester<IncludeResolution>() {
public IncludeResolution checkFile(String path, boolean isHeuristicMatch, IncludeSearchPathElement onPath) {
if (fFileContentProvider.getInclusionExists(path)) {
IncludeResolution res= new IncludeResolution();
res.fHeuristic= isHeuristicMatch;
res.fLocation= path;
return res;
}
return null;
}
};
private final class TokenSequence implements ITokenSequence {
private final boolean fStopAtNewline;
TokenSequence(boolean stopAtNewline) {
fStopAtNewline= stopAtNewline;
}
public Token nextToken() throws OffsetLimitReachedException {
final Lexer lexer= fCurrentContext.getLexer();
Token t= lexer.nextToken();
if (t.getType() == IToken.tPOUND && lexer.currentTokenIsFirstOnLine()) {
executeDirective(lexer, t.getOffset(), true);
t= lexer.currentToken();
}
if (fStopAtNewline && t.getType() == Lexer.tNEWLINE)
return new Token(IToken.tEND_OF_INPUT, null, 0, 0);
return t;
}
public int getLastEndOffset() {
return fCurrentContext.getLexer().getLastEndOffset();
}
public Token currentToken() {
Token t= fCurrentContext.currentLexerToken();
if (fStopAtNewline && t.getType() == Lexer.tNEWLINE)
return new Token(IToken.tEND_OF_INPUT, null, 0, 0);
return t;
}
}
TokenSequence fInputToMacroExpansion= new TokenSequence(false);
TokenSequence fLineInputToMacroExpansion= new TokenSequence(true);
final private IParserLogService fLog;
final private InternalFileContentProvider fFileContentProvider;
private IIncludeFileResolutionHeuristics fIncludeFileResolutionHeuristics;
private final ExpressionEvaluator fExpressionEvaluator;
private final MacroDefinitionParser fMacroDefinitionParser;
private final MacroExpander fMacroExpander;
// configuration
final private LexerOptions fLexOptions= new LexerOptions();
final private char[] fAdditionalNumericLiteralSuffixes;
final private CharArrayIntMap fKeywords;
final private CharArrayIntMap fPPKeywords;
private IncludeSearchPathElement[] fIncludeSearchPath;
private String[][] fPreIncludedFiles= null;
private int fContentAssistLimit= -1;
private boolean fHandledCompletion= false;
private boolean fSplitShiftRightOperator= false;
// state information
private final CharArrayMap<PreprocessorMacro> fMacroDictionary = new CharArrayMap<PreprocessorMacro>(512);
private final LocationMap fLocationMap;
/** Set of already included files */
private final HashSet<String> fAllIncludedFiles= new HashSet<String>();
private final Lexer fRootLexer;
private final ScannerContext fRootContext;
protected ScannerContext fCurrentContext;
private boolean isCancelled= false;
private boolean fIsFirstFetchToken= true;
private Token fPrefetchedTokens;
private Token fLastToken;
public CPreprocessor(FileContent fileContent, IScannerInfo info, ParserLanguage language, IParserLogService log,
IScannerExtensionConfiguration configuration, IncludeFileContentProvider readerFactory) {
if (readerFactory instanceof InternalFileContentProvider) {
fFileContentProvider= (InternalFileContentProvider) readerFactory;
} else if (readerFactory == null) {
fFileContentProvider= EmptyFilesProvider.getInstance();
} else {
throw new IllegalArgumentException("Illegal reader factory"); //$NON-NLS-1$
}
InternalFileContent content;
if (fileContent instanceof InternalFileContent) {
content= (InternalFileContent) fileContent;
} else {
throw new IllegalArgumentException("Illegal file content object"); //$NON-NLS-1$
}
fLog = log;
fAdditionalNumericLiteralSuffixes= nonNull(configuration.supportAdditionalNumericLiteralSuffixes());
fLexOptions.fSupportDollarInIdentifiers= configuration.support$InIdentifiers();
fLexOptions.fSupportAtSignInIdentifiers= configuration.supportAtSignInIdentifiers();
fLexOptions.fSupportMinAndMax = configuration.supportMinAndMaxOperators();
fLexOptions.fSupportSlashPercentComments= configuration.supportSlashPercentComments();
fLexOptions.fSupportUTFLiterals = configuration.supportUTFLiterals();
fLocationMap= new LocationMap(fLexOptions);
fKeywords= new CharArrayIntMap(40, -1);
fPPKeywords= new CharArrayIntMap(40, -1);
configureKeywords(language, configuration);
fExpressionEvaluator= new ExpressionEvaluator();
fMacroDefinitionParser= new MacroDefinitionParser();
fMacroExpander= new MacroExpander(this, fMacroDictionary, fLocationMap, fLexOptions);
fIncludeFileResolutionHeuristics= fFileContentProvider.getIncludeHeuristics();
final String filePath= content.getFileLocation();
configureIncludeSearchPath(new File(filePath).getParentFile(), info);
setupMacroDictionary(configuration, info, language);
ILocationCtx ctx= fLocationMap.pushTranslationUnit(filePath, content.getSource());
fAllIncludedFiles.add(filePath);
fFileContentProvider.reportTranslationUnitFile(filePath);
fRootLexer= new Lexer(content.getSource(), fLexOptions, this, this);
fRootContext= fCurrentContext= new ScannerContext(ctx, null, fRootLexer);
if (info instanceof IExtendedScannerInfo) {
final IExtendedScannerInfo einfo= (IExtendedScannerInfo) info;
fPreIncludedFiles= new String[][] {einfo.getMacroFiles(), einfo.getIncludeFiles()};
}
}
public void setSplitShiftROperator(boolean val) {
fSplitShiftRightOperator= val;
}
public void setComputeImageLocations(boolean val) {
fLexOptions.fCreateImageLocations= val;
}
public void setContentAssistMode(int offset) {
fContentAssistLimit= offset;
fRootLexer.setContentAssistMode(offset);
}
public boolean isContentAssistMode() {
return fRootLexer.isContentAssistMode();
}
public void setProcessInactiveCode(boolean val) {
fRootContext.setParseInactiveCode(val);
}
public void setScanComments(boolean val) {
}
public ILocationResolver getLocationResolver() {
return fLocationMap;
}
private void configureKeywords(ParserLanguage language, IScannerExtensionConfiguration configuration) {
Keywords.addKeywordsPreprocessor(fPPKeywords);
if (language == ParserLanguage.C) {
Keywords.addKeywordsC(fKeywords);
} else {
Keywords.addKeywordsCpp(fKeywords);
}
CharArrayIntMap additionalKeywords= configuration.getAdditionalKeywords();
if (additionalKeywords != null) {
fKeywords.putAll(additionalKeywords);
}
additionalKeywords= configuration.getAdditionalPreprocessorKeywords();
if (additionalKeywords != null) {
fPPKeywords.putAll(additionalKeywords);
}
}
protected String getCurrentFilename() {
return fLocationMap.getCurrentFilePath();
}
private char[] nonNull(char[] array) {
return array == null ? EMPTY_CHAR_ARRAY : array;
}
private void configureIncludeSearchPath(File directory, IScannerInfo info) {
String[] searchPath= info.getIncludePaths();
int idx= 0;
if (info instanceof IExtendedScannerInfo) {
final IExtendedScannerInfo einfo= (IExtendedScannerInfo) info;
final String[] quoteIncludeSearchPath= einfo.getLocalIncludePath();
if (quoteIncludeSearchPath != null && quoteIncludeSearchPath.length > 0) {
fIncludeSearchPath= new IncludeSearchPathElement[quoteIncludeSearchPath.length + searchPath.length];
for (String qip : quoteIncludeSearchPath) {
fIncludeSearchPath[idx++]= new IncludeSearchPathElement(makeAbsolute(directory, qip), true);
}
}
}
if (fIncludeSearchPath == null) {
fIncludeSearchPath= new IncludeSearchPathElement[searchPath.length];
}
for (String path : searchPath) {
fIncludeSearchPath[idx++]= new IncludeSearchPathElement(makeAbsolute(directory, path), false);
}
}
private String makeAbsolute(File directory, String inlcudePath) {
if (directory == null || new File(inlcudePath).isAbsolute()) {
return inlcudePath;
}
return ScannerUtility.createReconciledPath(directory.getAbsolutePath(), inlcudePath);
}
private void setupMacroDictionary(IScannerExtensionConfiguration config, IScannerInfo info, ParserLanguage lang) {
// built in macros
fMacroDictionary.put(__CDT_PARSER__.getNameCharArray(), __CDT_PARSER__);
fMacroDictionary.put(__STDC__.getNameCharArray(), __STDC__);
fMacroDictionary.put(__FILE__.getNameCharArray(), __FILE__);
fMacroDictionary.put(__DATE__.getNameCharArray(), __DATE__);
fMacroDictionary.put(__TIME__.getNameCharArray(), __TIME__);
fMacroDictionary.put(__LINE__.getNameCharArray(), __LINE__);
if (lang == ParserLanguage.CPP) {
fMacroDictionary.put(__cplusplus.getNameCharArray(), __cplusplus);
} else {
fMacroDictionary.put(__STDC_HOSTED__.getNameCharArray(), __STDC_HOSTED__);
fMacroDictionary.put(__STDC_VERSION__.getNameCharArray(), __STDC_VERSION__);
}
IMacro[] toAdd = config.getAdditionalMacros();
if (toAdd != null) {
for (final IMacro macro : toAdd) {
addMacroDefinition(macro.getSignature(), macro.getExpansion());
}
}
final Map<String, String> macroDict= info.getDefinedSymbols();
if (macroDict != null) {
for (Map.Entry<String, String> entry : macroDict.entrySet()) {
final String key= entry.getKey();
final String value= entry.getValue().trim();
addMacroDefinition(key.toCharArray(), value.toCharArray());
}
}
Collection<PreprocessorMacro> predefined= fMacroDictionary.values();
for (PreprocessorMacro macro : predefined) {
fLocationMap.registerPredefinedMacro(macro);
}
}
private void beforeFirstFetchToken() {
if (fPreIncludedFiles != null) {
handlePreIncludedFiles();
}
final String location = fLocationMap.getTranslationUnitPath();
InternalFileContent content= fFileContentProvider.getContentForContextToHeaderGap(location);
if (content != null && content.getKind() == InclusionKind.FOUND_IN_INDEX) {
processInclusionFromIndex(0, location, content);
}
}
private void handlePreIncludedFiles() {
final String[] imacro= fPreIncludedFiles[0];
if (imacro != null && imacro.length > 0) {
final char[] buffer= createSyntheticFile(imacro);
ILocationCtx ctx= fLocationMap.pushPreInclusion(new CharArray(buffer), 0, true);
fCurrentContext= new ScannerContext(ctx, fCurrentContext, new Lexer(buffer, fLexOptions, this, this));
ScannerContext preCtx= fCurrentContext;
try {
while (internalFetchToken(preCtx, CHECK_NUMBERS, false).getType() != IToken.tEND_OF_INPUT) {
// just eat the tokens
}
final ILocationCtx locationCtx = fCurrentContext.getLocationCtx();
fLocationMap.popContext(locationCtx);
fCurrentContext= fCurrentContext.getParent();
assert fCurrentContext == fRootContext;
} catch (OffsetLimitReachedException e) {
}
}
final String[] include= fPreIncludedFiles[1];
if (include != null && include.length > 0) {
final char[] buffer= createSyntheticFile(include);
ILocationCtx ctx= fLocationMap.pushPreInclusion(new CharArray(buffer), 0, false);
fCurrentContext= new ScannerContext(ctx, fCurrentContext, new Lexer(buffer, fLexOptions, this, this));
}
fPreIncludedFiles= null;
}
private char[] createSyntheticFile(String[] files) {
int totalLength= 0;
final char[] instruction= "#include <".toCharArray(); //$NON-NLS-1$
for (String file : files) {
totalLength+= instruction.length + 2 + file.length();
}
final char[] buffer= new char[totalLength];
int pos= 0;
for (String file : files) {
final char[] fileName= file.toCharArray();
System.arraycopy(instruction, 0, buffer, pos, instruction.length);
pos+= instruction.length;
System.arraycopy(fileName, 0, buffer, pos, fileName.length);
pos+= fileName.length;
buffer[pos++]= '>';
buffer[pos++]= '\n';
}
return buffer;
}
public PreprocessorMacro addMacroDefinition(char[] key, char[] value) {
final Lexer lex= new Lexer(key, fLexOptions, ILexerLog.NULL, null);
try {
PreprocessorMacro result= fMacroDefinitionParser.parseMacroDefinition(lex, ILexerLog.NULL, value);
fLocationMap.registerPredefinedMacro(result);
fMacroDictionary.put(result.getNameCharArray(), result);
return result;
} catch (Exception e) {
fLog.traceLog("Invalid macro definition: '" + String.valueOf(key) + "'"); //$NON-NLS-1$//$NON-NLS-2$
return null;
}
}
public Map<String, IMacroBinding> getMacroDefinitions() {
Map<String, IMacroBinding> hashMap = new HashMap<String, IMacroBinding>(fMacroDictionary.size());
for (char[] key : fMacroDictionary.keys()) {
hashMap.put(String.valueOf(key), fMacroDictionary.get(key));
}
return hashMap;
}
public boolean isOnTopContext() {
ScannerContext ctx= fCurrentContext;
while (ctx != null && ctx.getLocationCtx() instanceof LocationCtxMacroExpansion) {
ctx= ctx.getParent();
}
return ctx == fRootContext;
}
public void cancel() {
isCancelled= true;
}
/**
* Returns the next token from the preprocessor without concatenating string literals
* and also without splitting the shift-right operator.
*/
private Token fetchToken() throws OffsetLimitReachedException {
if (fIsFirstFetchToken) {
beforeFirstFetchToken();
fIsFirstFetchToken= false;
}
Token t= fPrefetchedTokens;
if (t != null) {
fPrefetchedTokens= (Token) t.getNext();
t.setNext(null);
return t;
}
try {
t= internalFetchToken(fRootContext, CHECK_NUMBERS, false);
} catch (OffsetLimitReachedException e) {
fHandledCompletion= true;
throw e;
}
final int offset= fLocationMap.getSequenceNumberForOffset(t.getOffset());
final int endOffset= fLocationMap.getSequenceNumberForOffset(t.getEndOffset());
t.setOffset(offset, endOffset);
t.setNext(null);
return t;
}
private void pushbackToken(Token t) {
t.setNext(fPrefetchedTokens);
fPrefetchedTokens= t;
}
/**
* Returns next token for the parser. String literals are not concatenated. When
* the end is reached tokens with type {@link IToken#tEND_OF_INPUT}.
* @throws OffsetLimitReachedException see {@link Lexer}.
*/
public IToken nextTokenRaw() throws OffsetLimitReachedException {
if (isCancelled) {
throw new ParseError(ParseError.ParseErrorKind.TIMEOUT_OR_CANCELLED);
}
Token t1= fetchToken();
switch (t1.getType()) {
case IToken.tCOMPLETION:
fHandledCompletion= true;
break;
case IToken.tEND_OF_INPUT:
if (fContentAssistLimit >= 0) {
int useType= fHandledCompletion ? IToken.tEOC : IToken.tCOMPLETION;
int sequenceNumber= fLocationMap.getSequenceNumberForOffset(fContentAssistLimit);
t1= new Token(useType, null, sequenceNumber, sequenceNumber);
fHandledCompletion= true;
}
break;
case IToken.t_PRAGMA:
handlePragmaOperator(t1);
return nextTokenRaw();
}
if (fLastToken != null) {
fLastToken.setNext(t1);
}
fLastToken= t1;
return t1;
}
private void handlePragmaOperator(Token t1) throws OffsetLimitReachedException {
Token t2= fetchToken();
int end;
if (t2.getType() == IToken.tLPAREN) {
Token t3= fetchToken();
end= t3.getEndOffset();
final int tt = t3.getType();
if (tt == IToken.tSTRING || tt == IToken.tLSTRING || tt == IToken.tUTF16STRING || tt == IToken.tUTF32STRING) {
Token t4= fetchToken();
end= t4.getEndOffset();
if (t4.getType() == IToken.tRPAREN) {
fLocationMap.encounterPragmaOperator(t1.getOffset(), t3.getOffset(), t3.getEndOffset(), t4.getEndOffset());
return;
} else {
end= t3.getEndOffset();
pushbackToken(t4);
}
} else {
if (t3.getType() == IToken.tRPAREN) {
// Consume closing parenthesis
end= t3.getEndOffset();
} else {
end= t2.getEndOffset();
pushbackToken(t3);
}
}
} else {
end= t1.getEndOffset();
pushbackToken(t2);
}
fLocationMap.encounterProblem(IProblem.PREPROCESSOR_INVALID_DIRECTIVE, t1.getCharImage(), t1.getOffset(), end);
}
/**
* Returns next token for the parser. String literals are concatenated.
* @throws EndOfFileException when the end of the translation unit has been reached.
* @throws OffsetLimitReachedException see {@link Lexer}.
*/
public IToken nextToken() throws EndOfFileException {
if (isCancelled) {
throw new ParseError(ParseError.ParseErrorKind.TIMEOUT_OR_CANCELLED);
}
Token t1= fetchToken();
final int tt1= t1.getType();
switch (tt1) {
case IToken.tCOMPLETION:
fHandledCompletion= true;
break;
case IToken.tEND_OF_INPUT:
if (fContentAssistLimit < 0) {
fPrefetchedTokens= t1;
throw new EndOfFileException(t1.getOffset());
}
int useType= fHandledCompletion ? IToken.tEOC : IToken.tCOMPLETION;
int sequenceNumber= fLocationMap.getSequenceNumberForOffset(fContentAssistLimit);
t1= new Token(useType, null, sequenceNumber, sequenceNumber);
fHandledCompletion= true;
break;
case IToken.t_PRAGMA:
handlePragmaOperator(t1);
return nextToken();
case IToken.tSTRING:
case IToken.tLSTRING:
case IToken.tUTF16STRING:
case IToken.tUTF32STRING:
StringType st = StringType.fromToken(tt1);
Token t2;
StringBuffer buf= null;
int endOffset= 0;
loop: while (true) {
t2= fetchToken();
final int tt2= t2.getType();
switch (tt2) {
case IToken.tLSTRING:
case IToken.tSTRING:
case IToken.tUTF16STRING:
case IToken.tUTF32STRING:
st = StringType.max(st, StringType.fromToken(tt2));
if (buf == null) {
buf= new StringBuffer();
appendStringContent(buf, t1);
}
appendStringContent(buf, t2);
endOffset= t2.getEndOffset();
continue loop;
case IToken.tINACTIVE_CODE_START:
// no support for inactive code after a string literal
skipInactiveCode();
continue loop;
case IToken.t_PRAGMA:
handlePragmaOperator(t2);
continue loop;
default:
break loop;
}
}
pushbackToken(t2);
if (buf != null) {
char[] prefix = st.getPrefix();
char[] image= new char[buf.length() + prefix.length + 2];
int off= -1;
for (char c : prefix)
image[++off] = c;
image[++off]= '"';
buf.getChars(0, buf.length(), image, ++off);
image[image.length - 1]= '"';
t1= new TokenWithImage(st.getTokenValue(), null, t1.getOffset(), endOffset, image);
}
break;
case IToken.tSHIFTR:
if (fSplitShiftRightOperator) {
int offset= t1.getOffset();
endOffset= t1.getEndOffset();
t1.setType(IToken.tGT_in_SHIFTR);
t1.setOffset(offset, offset+1);
t2= new Token(IToken.tGT_in_SHIFTR, t1.fSource, offset+1, endOffset);
pushbackToken(t2);
}
}
if (fLastToken != null) {
fLastToken.setNext(t1);
}
fLastToken= t1;
return t1;
}
public void skipInactiveCode() throws OffsetLimitReachedException {
final Lexer lexer= fCurrentContext.getLexer();
if (lexer != null) {
CodeState state= fCurrentContext.getCodeState();
while (state != CodeState.eActive) {
state= skipBranch(lexer, false);
}
fCurrentContext.clearInactiveCodeMarkerToken();
}
}
public int getCodeBranchNesting() {
return fCurrentContext.getCodeBranchNesting();
}
private void appendStringContent(StringBuffer buf, Token t1) {
final char[] image= t1.getCharImage();
final int length= image.length;
int start = 1;
for (char c : image) {
if (c == '"')
break;
start++;
}
if (length > 1) {
final int diff= image[length - 1] == '"' ? length - start - 1 : length - start;
if (diff > 0) {
buf.append(image, start, diff);
}
}
}
Token internalFetchToken(final ScannerContext uptoEndOfCtx, int options, boolean withinExpansion) throws OffsetLimitReachedException {
Token ppToken= fCurrentContext.currentLexerToken();
while (true) {
switch (ppToken.getType()) {
case Lexer.tBEFORE_INPUT:
ppToken= fCurrentContext.nextPPToken();
continue;
case Lexer.tNEWLINE:
if ((options & STOP_AT_NL) != 0) {
return ppToken;
}
ppToken= fCurrentContext.nextPPToken();
continue;
case Lexer.tOTHER_CHARACTER:
handleProblem(IProblem.SCANNER_BAD_CHARACTER, ppToken.getCharImage(),
ppToken.getOffset(), ppToken.getEndOffset());
ppToken= fCurrentContext.nextPPToken();
continue;
case IToken.tEND_OF_INPUT:
if (fCurrentContext == uptoEndOfCtx || uptoEndOfCtx == null) {
return ppToken;
}
final ILocationCtx locationCtx = fCurrentContext.getLocationCtx();
fLocationMap.popContext(locationCtx);
fCurrentContext= fCurrentContext.getParent();
assert fCurrentContext != null;
ppToken= fCurrentContext.currentLexerToken();
continue;
case IToken.tPOUND:
{
final Lexer lexer= fCurrentContext.getLexer();
if (lexer != null && lexer.currentTokenIsFirstOnLine()) {
executeDirective(lexer, ppToken.getOffset(), withinExpansion);
ppToken= fCurrentContext.currentLexerToken();
continue;
}
break;
}
case IToken.tIDENTIFIER:
fCurrentContext.nextPPToken(); // consume the identifier
if ((options & NO_EXPANSION) == 0) {
final Lexer lexer= fCurrentContext.getLexer();
if (lexer != null && expandMacro(ppToken, lexer, options, withinExpansion)) {
ppToken= fCurrentContext.currentLexerToken();
continue;
}
final char[] name= ppToken.getCharImage();
int tokenType = fKeywords.get(name);
if (tokenType != fKeywords.undefined) {
ppToken.setType(tokenType);
}
}
return ppToken;
case IToken.tINTEGER:
if ((options & CHECK_NUMBERS) != 0) {
checkNumber(ppToken, false);
}
break;
case IToken.tFLOATINGPT:
if ((options & CHECK_NUMBERS) != 0) {
checkNumber(ppToken, true);
}
break;
}
fCurrentContext.nextPPToken();
return ppToken;
}
}
private void checkNumber(Token number, final boolean isFloat) {
final char[] image= number.getCharImage();
boolean hasExponent = false;
// Integer constants written in binary are a non-standard extension
// supported by GCC since 4.3 and by some other C compilers
// They consist of a prefix 0b or 0B, followed by a sequence of 0 and 1 digits
// see http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html
boolean isBin = false;
boolean isHex = false;
boolean isOctal = false;
boolean hasDot= false;
int pos= 0;
if (image.length > 1) {
if (image[0] == '0') {
switch (image[++pos]) {
case 'b':
case 'B':
isBin = true;
++pos;
break;
case 'x':
case 'X':
isHex = true;
++pos;
break;
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
isOctal = true;
++pos;
break;
case '8': case '9':
handleProblem(IProblem.SCANNER_BAD_OCTAL_FORMAT, image, number.getOffset(), number.getEndOffset());
return;
}
}
}
loop: for (; pos < image.length; pos++) {
if (isBin) {
switch (image[pos]) {
case '0': case'1':
continue;
default:
// 0 and 1 are the only allowed digits for binary integers
// No floating point, exponents etc. are allowed
break loop;
}
}
switch (image[pos]) {
// octal digits
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
continue;
// decimal digits
case '8': case '9':
if (isOctal) {
handleProblem(IProblem.SCANNER_BAD_OCTAL_FORMAT, image, number.getOffset(), number.getEndOffset());
return;
}
continue;
// hex digits
case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'f': case 'F':
if (isHex && !hasExponent) {
continue;
}
break loop;
case '.':
if (hasDot) {
handleProblem(IProblem.SCANNER_BAD_FLOATING_POINT, image, number.getOffset(), number.getEndOffset());
return;
}
hasDot= true;
continue;
// check for exponent or hex digit
case 'E': case 'e':
if (isHex && !hasExponent) {
continue;
}
if (isFloat && !isHex && !hasExponent && pos+1 < image.length) {
switch (image[pos+1]) {
case '+': case '-':
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
hasExponent = true;
++pos;
continue;
}
}
break loop;
// check for hex float exponent
case 'p': case 'P':
if (isFloat && isHex && !hasExponent && pos+1 < image.length) {
switch (image[pos+1]) {
case '+': case '-':
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
hasExponent = true;
++pos;
continue;
}
}
break loop;
default:
break loop;
}
}
// check the suffix
loop: for (; pos < image.length; pos++) {
final char c= image[pos];
switch (c) {
case 'u': case 'U': case 'L': case 'l':
continue;
case 'f': case 'F':
if (isFloat) {
continue loop;
}
}
for (int i=0; i<fAdditionalNumericLiteralSuffixes.length; i++) {
if (fAdditionalNumericLiteralSuffixes[i] == c) {
continue loop;
}
}
if (isBin) {
// The check for bin has to come before float, otherwise binary integers
// with float components get flagged as BAD_FLOATING_POINT
handleProblem(IProblem.SCANNER_BAD_BINARY_FORMAT, image, number.getOffset(), number.getEndOffset());
} else if (isFloat) {
handleProblem(IProblem.SCANNER_BAD_FLOATING_POINT, image, number.getOffset(), number.getEndOffset());
} else if (isHex) {
handleProblem(IProblem.SCANNER_BAD_HEX_FORMAT, image, number.getOffset(), number.getEndOffset());
} else if (isOctal) {
handleProblem(IProblem.SCANNER_BAD_OCTAL_FORMAT, image, number.getOffset(), number.getEndOffset());
} else {
handleProblem(IProblem.SCANNER_BAD_DECIMAL_FORMAT, image, number.getOffset(), number.getEndOffset());
}
return;
}
}
private <T> T findInclusion(final String includeDirective, final boolean quoteInclude,
final boolean includeNext, final String currentFile, final IIncludeFileTester<T> tester) {
T reader = null;
// Filename is an absolute path
if (new File(includeDirective).isAbsolute()) {
return tester.checkFile(includeDirective, false, null);
}
// Filename is a Linux absolute path on a windows machine
if (File.separatorChar == '\\' && includeDirective.length() > 0) {
final char firstChar = includeDirective.charAt(0);
if (firstChar == '\\' || firstChar == '/') {
if (currentFile != null && currentFile.length() > 1 && currentFile.charAt(1) == ':') {
return tester.checkFile(currentFile.substring(0, 2) + includeDirective, false, null);
}
return tester.checkFile(includeDirective, false, null);
}
}
if (currentFile != null && quoteInclude && !includeNext) {
// Check to see if we find a match in the current directory
final File currentDir= new File(currentFile).getParentFile();
if (currentDir != null) {
final String fileLocation = ScannerUtility.createReconciledPath(currentDir.getAbsolutePath(), includeDirective);
reader = tester.checkFile(fileLocation, false, null);
if (reader != null) {
return reader;
}
}
}
// Now we need to search for the file on the include search path.
// If this is a include_next directive then the search starts with the directory
// in the search path after the one where the current file was found.
IncludeSearchPathElement searchAfter= null;
if (includeNext && currentFile != null) {
searchAfter = fCurrentContext.getFoundOnPath();
if (searchAfter == null) {
// the current file was found without search path
String directive= fCurrentContext.getFoundViaDirective();
if (directive == null) {
directive= new File(currentFile).getName();
}
searchAfter = findFileInIncludePath(currentFile, directive);
}
}
for (IncludeSearchPathElement path : fIncludeSearchPath) {
if (searchAfter != null) {
if (searchAfter.equals(path)) {
searchAfter= null;
}
} else if (quoteInclude || !path.isForQuoteIncludesOnly()) {
String fileLocation = path.getLocation(includeDirective);
if (fileLocation != null) {
reader= tester.checkFile(fileLocation, false, path);
if (reader != null) {
return reader;
}
}
}
}
if (fIncludeFileResolutionHeuristics != null) {
String location= fIncludeFileResolutionHeuristics.findInclusion(includeDirective, currentFile);
if (location != null) {
return tester.checkFile(location, true, null);
}
}
return null;
}
private IncludeSearchPathElement findFileInIncludePath(String file, String includeDirective) {
for (IncludeSearchPathElement path : fIncludeSearchPath) {
String fileLocation = path.getLocation(includeDirective);
if (file.equals(fileLocation)) {
return path;
}
}
return null;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer("Scanner @ file:"); //$NON-NLS-1$
buffer.append(fCurrentContext.toString());
buffer.append(" line: "); //$NON-NLS-1$
buffer.append(fLocationMap.getCurrentLineNumber(fCurrentContext.currentLexerToken().getOffset()));
return buffer.toString();
}
private void addMacroDefinition(IIndexMacro macro) {
try {
final char[] expansionImage = macro.getExpansionImage();
if (expansionImage == null) {
// this is an undef
fMacroDictionary.remove(macro.getNameCharArray());
} else {
PreprocessorMacro result= MacroDefinitionParser.parseMacroDefinition(macro.getNameCharArray(), macro.getParameterList(), expansionImage);
final IASTFileLocation loc= macro.getFileLocation();
fLocationMap.registerMacroFromIndex(result, loc, -1);
fMacroDictionary.put(result.getNameCharArray(), result);
}
} catch (Exception e) {
fLog.traceLog("Invalid macro definition: '" + macro.getName() + "'"); //$NON-NLS-1$//$NON-NLS-2$
}
}
public ILocationResolver getLocationMap() {
return fLocationMap;
}
public void handleComment(boolean isBlockComment, int offset, int endOffset) {
fLocationMap.encounteredComment(offset, endOffset, isBlockComment);
}
public void handleProblem(int id, char[] arg, int offset, int endOffset) {
fLocationMap.encounterProblem(id, arg, offset, endOffset);
}
/**
* Assumes that the pound token has not yet been consumed
* @since 5.0
*/
private void executeDirective(final Lexer lexer, final int startOffset, boolean withinExpansion) throws OffsetLimitReachedException {
final Token ident= lexer.nextToken();
switch (ident.getType()) {
case IToken.tCOMPLETION:
lexer.nextToken();
Token completionToken= new TokenWithImage(ident.getType(), null,
startOffset, ident.getEndOffset(), ("#" + ident.getImage()).toCharArray()); //$NON-NLS-1$
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, completionToken);
case Lexer.tNEWLINE:
return;
case IToken.tEND_OF_INPUT:
case IToken.tINTEGER:
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
return;
case IToken.tIDENTIFIER:
break;
default:
int endOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
handleProblem(IProblem.PREPROCESSOR_INVALID_DIRECTIVE, ident.getCharImage(), startOffset, endOffset);
return;
}
// we have an identifier
final char[] name = ident.getCharImage();
final int type = fPPKeywords.get(name);
int condEndOffset;
switch (type) {
case IPreprocessorDirective.ppImport:
case IPreprocessorDirective.ppInclude:
executeInclude(lexer, startOffset, false, fCurrentContext.getCodeState() == CodeState.eActive, withinExpansion);
break;
case IPreprocessorDirective.ppInclude_next:
executeInclude(lexer, startOffset, true, fCurrentContext.getCodeState() == CodeState.eActive, withinExpansion);
break;
case IPreprocessorDirective.ppDefine:
CodeState state= fCurrentContext.getCodeState();
if (state == CodeState.eSkipInactive) {
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
} else {
executeDefine(lexer, startOffset, state == CodeState.eActive);
}
break;
case IPreprocessorDirective.ppUndef:
state= fCurrentContext.getCodeState();
if (state == CodeState.eSkipInactive) {
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
} else {
executeUndefine(lexer, startOffset, fCurrentContext.getCodeState() == CodeState.eActive);
}
break;
case IPreprocessorDirective.ppIfdef:
if (executeIfdef(lexer, startOffset, false, withinExpansion) == CodeState.eSkipInactive)
skipOverConditionalCode(lexer, withinExpansion);
break;
case IPreprocessorDirective.ppIfndef:
if (executeIfdef(lexer, startOffset, true, withinExpansion) == CodeState.eSkipInactive)
skipOverConditionalCode(lexer, withinExpansion);
break;
case IPreprocessorDirective.ppIf:
if (executeIf(lexer, startOffset, false, withinExpansion) == CodeState.eSkipInactive)
skipOverConditionalCode(lexer, withinExpansion);
break;
case IPreprocessorDirective.ppElif:
if (executeIf(lexer, startOffset, true, withinExpansion) == CodeState.eSkipInactive) {
skipOverConditionalCode(lexer, withinExpansion);
}
break;
case IPreprocessorDirective.ppElse:
if (executeElse(lexer, startOffset, withinExpansion) == CodeState.eSkipInactive) {
skipOverConditionalCode(lexer, withinExpansion);
}
break;
case IPreprocessorDirective.ppEndif:
executeEndif(lexer, startOffset, withinExpansion);
break;
case IPreprocessorDirective.ppWarning:
case IPreprocessorDirective.ppError:
int condOffset= lexer.nextToken().getOffset();
condEndOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
// Missing argument
if (condEndOffset < condOffset) {
condOffset= condEndOffset;
}
if (fCurrentContext.getCodeState() == CodeState.eActive) {
int endOffset= lexer.currentToken().getEndOffset();
final char[] warning= lexer.getInputChars(condOffset, condEndOffset);
final int id= type == IPreprocessorDirective.ppError
? IProblem.PREPROCESSOR_POUND_ERROR
: IProblem.PREPROCESSOR_POUND_WARNING;
handleProblem(id, warning, condOffset, condEndOffset);
fLocationMap.encounterPoundError(startOffset, condOffset, condEndOffset, endOffset);
}
break;
case IPreprocessorDirective.ppPragma:
condOffset= lexer.nextToken().getOffset();
condEndOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
// Missing argument
if (condEndOffset < condOffset) {
condOffset= condEndOffset;
}
if (fCurrentContext.getCodeState() == CodeState.eActive) {
int endOffset= lexer.currentToken().getEndOffset();
fLocationMap.encounterPoundPragma(startOffset, condOffset, condEndOffset, endOffset);
}
break;
case IPreprocessorDirective.ppIgnore:
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
break;
default:
int endOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
if (fCurrentContext.getCodeState() == CodeState.eActive) {
handleProblem(IProblem.PREPROCESSOR_INVALID_DIRECTIVE, ident.getCharImage(), startOffset, endOffset);
}
break;
}
}
private boolean hasFileBeenIncluded(String location) {
Boolean itHas= fFileContentProvider.hasFileBeenIncludedInCurrentTranslationUnit(location);
if (itHas != null) {
return itHas.booleanValue();
}
return fAllIncludedFiles.contains(location);
}
private void executeInclude(final Lexer lexer, int poundOffset, boolean include_next, boolean active, boolean withinExpansion) throws OffsetLimitReachedException {
if (withinExpansion) {
final char[] name= lexer.currentToken().getCharImage();
final int endOffset = lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
handleProblem(IProblem.PREPROCESSOR_INVALID_DIRECTIVE, name, poundOffset, endOffset);
return;
}
lexer.setInsideIncludeDirective(true);
final Token header= lexer.nextToken();
lexer.setInsideIncludeDirective(false);
int condEndOffset= header.getEndOffset();
final int[] nameOffsets= new int[] {header.getOffset(), condEndOffset};
char[] headerName= null;
boolean userInclude= true;
switch (header.getType()) {
case Lexer.tSYSTEM_HEADER_NAME:
userInclude= false;
headerName = extractHeaderName(header.getCharImage(), '<', '>', nameOffsets);
condEndOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
break;
case Lexer.tQUOTE_HEADER_NAME:
headerName = extractHeaderName(header.getCharImage(), '"', '"', nameOffsets);
condEndOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
break;
case IToken.tCOMPLETION:
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, header);
case IToken.tIDENTIFIER:
TokenList tl= new TokenList();
condEndOffset= nameOffsets[1]= getTokensWithinPPDirective(false, tl, false);
Token t= tl.first();
if (t != null) {
switch (t.getType()) {
case IToken.tSTRING:
headerName = extractHeaderName(t.getCharImage(), '"', '"', new int[]{0,0});
break;
case IToken.tLT:
userInclude= false;
boolean complete= false;
StringBuffer buf= new StringBuffer();
t= (Token) t.getNext();
while (t != null) {
if (t.getType() == IToken.tGT) {
complete= true;
break;
}
buf.append(t.getImage());
t= (Token) t.getNext();
}
if (complete) {
headerName= new char[buf.length()];
buf.getChars(0, buf.length(), headerName, 0);
}
}
}
break;
default:
condEndOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
break;
}
if (headerName == null || headerName.length == 0) {
if (active) {
handleProblem(IProblem.PREPROCESSOR_INVALID_DIRECTIVE,
lexer.getInputChars(poundOffset, condEndOffset), poundOffset, condEndOffset);
}
return;
}
String path= null;
boolean reported= false;
boolean isHeuristic= false;
final String includeDirective = new String(headerName);
if (!active) {
// test if the include is inactive just because it was included before (bug 167100)
final IncludeResolution resolved= findInclusion(includeDirective, userInclude, include_next,
getCurrentFilename(), createPathTester);
if (resolved != null && hasFileBeenIncluded(resolved.fLocation)) {
path= resolved.fLocation;
isHeuristic= resolved.fHeuristic;
}
} else {
final InternalFileContent fi= findInclusion(includeDirective, userInclude, include_next,
getCurrentFilename(), createCodeReaderTester);
if (fi != null) {
path= fi.getFileLocation();
isHeuristic= fi.isFoundByHeuristics();
switch (fi.getKind()) {
case FOUND_IN_INDEX:
processInclusionFromIndex(poundOffset, path, fi);
break;
case USE_SOURCE:
AbstractCharArray source= fi.getSource();
if (source != null && !isCircularInclusion(path)) {
reported= true;
fAllIncludedFiles.add(path);
ILocationCtx ctx= fLocationMap.pushInclusion(poundOffset, nameOffsets[0], nameOffsets[1],
condEndOffset, source, path, headerName, userInclude, isHeuristic, fi.isSource());
ScannerContext fctx= new ScannerContext(ctx, fCurrentContext, new Lexer(source,
fLexOptions, this, this));
fctx.setFoundOnPath(fi.getFoundOnPath(), includeDirective);
fCurrentContext= fctx;
}
break;
case SKIP_FILE:
break;
}
} else {
final int len = headerName.length + 2;
StringBuilder name= new StringBuilder(len);
name.append(userInclude ? '"' : '<');
name.append(headerName);
name.append(userInclude ? '"' : '>');
final char[] nameChars= new char[len];
name.getChars(0, len, nameChars, 0);
handleProblem(IProblem.PREPROCESSOR_INCLUSION_NOT_FOUND, nameChars, poundOffset, condEndOffset);
}
}
if (!reported) {
fLocationMap.encounterPoundInclude(poundOffset, nameOffsets[0], nameOffsets[1], condEndOffset, headerName, path, userInclude, active, isHeuristic);
}
}
private void processInclusionFromIndex(int offset, String path, InternalFileContent fi) {
List<IIndexMacro> mdefs= fi.getMacroDefinitions();
for (IIndexMacro macro : mdefs) {
addMacroDefinition(macro);
}
fLocationMap.skippedFile(fLocationMap.getSequenceNumberForOffset(offset), fi);
}
private char[] extractHeaderName(final char[] image, final char startDelim, final char endDelim, int[] offsets) {
char[] headerName;
int start= 0;
int length= image.length;
if (length > 0 && image[length - 1] == endDelim) {
length--;
offsets[1]--;
if (length > 0 && image[0] == startDelim) {
offsets[0]++;
start++;
length--;
}
}
headerName= new char[length];
System.arraycopy(image, start, headerName, 0, length);
return headerName;
}
private boolean isCircularInclusion(String filename) {
ILocationCtx checkContext= fCurrentContext.getLocationCtx();
while (checkContext != null) {
if (filename.equals(checkContext.getFilePath())) {
return true;
}
checkContext= checkContext.getParent();
}
return false;
}
private void executeDefine(final Lexer lexer, int startOffset, boolean isActive) throws OffsetLimitReachedException {
try {
ObjectStyleMacro macrodef = fMacroDefinitionParser.parseMacroDefinition(lexer, this);
if (isActive)
fMacroDictionary.put(macrodef.getNameCharArray(), macrodef);
final Token name= fMacroDefinitionParser.getNameToken();
fLocationMap.encounterPoundDefine(startOffset, name.getOffset(), name.getEndOffset(),
macrodef.getExpansionOffset(), macrodef.getExpansionEndOffset(), isActive, macrodef);
} catch (InvalidMacroDefinitionException e) {
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
handleProblem(IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, e.fName, e.fStartOffset, e.fEndOffset);
}
}
private void executeUndefine(Lexer lexer, int startOffset, boolean isActive) throws OffsetLimitReachedException {
final Token name= lexer.nextToken();
final int tt= name.getType();
if (tt != IToken.tIDENTIFIER) {
if (tt == IToken.tCOMPLETION) {
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, name);
}
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
handleProblem(IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, name.getCharImage(), startOffset, name.getEndOffset());
return;
}
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
final int endOffset= lexer.currentToken().getEndOffset();
final char[] namechars= name.getCharImage();
PreprocessorMacro definition;
if (isActive) {
definition= fMacroDictionary.remove(namechars, 0, namechars.length);
} else {
definition= fMacroDictionary.get(namechars);
}
fLocationMap.encounterPoundUndef(definition, startOffset, name.getOffset(), name.getEndOffset(), endOffset, namechars, isActive);
}
private CodeState executeIfdef(Lexer lexer, int offset, boolean isIfndef, boolean withinExpansion) throws OffsetLimitReachedException {
final Token name= lexer.nextToken();
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
final int tt= name.getType();
final int nameOffset = name.getOffset();
final int nameEndOffset = name.getEndOffset();
final int endOffset= lexer.currentToken().getEndOffset();
boolean isActive= false;
PreprocessorMacro macro= null;
final Conditional conditional= fCurrentContext.newBranch(BranchKind.eIf, withinExpansion);
if (conditional.canHaveActiveBranch(withinExpansion)) {
// we need an identifier
if (tt != IToken.tIDENTIFIER) {
if (tt == IToken.tCOMPLETION) {
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, name);
}
// report problem and treat as inactive
handleProblem(IProblem.PREPROCESSOR_DEFINITION_NOT_FOUND, name.getCharImage(), offset, nameEndOffset);
} else {
final char[] namechars= name.getCharImage();
macro= fMacroDictionary.get(namechars);
isActive= (macro == null) == isIfndef;
if (macro == null) {
macro = new UndefinedMacro(namechars);
}
}
}
if (isIfndef) {
fLocationMap.encounterPoundIfndef(offset, nameOffset, nameEndOffset, endOffset, isActive, macro);
} else {
fLocationMap.encounterPoundIfdef(offset, nameOffset, nameEndOffset, endOffset, isActive, macro);
}
return fCurrentContext.setBranchState(conditional, isActive, withinExpansion, offset);
}
private CodeState executeIf(Lexer lexer, int startOffset, boolean isElif, boolean withinExpansion) throws OffsetLimitReachedException {
Conditional cond= fCurrentContext.newBranch(isElif ? BranchKind.eElif : BranchKind.eIf, withinExpansion);
if (cond == null) {
char[] name= lexer.currentToken().getCharImage();
int condEndOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
handleProblem(IProblem.PREPROCESSOR_UNBALANCE_CONDITION, name, startOffset, condEndOffset);
return fCurrentContext.getCodeState();
}
boolean isActive= false;
IASTName[] refs= IASTName.EMPTY_NAME_ARRAY;
int condOffset= lexer.nextToken().getOffset();
int condEndOffset, endOffset;
if (cond.canHaveActiveBranch(withinExpansion)) {
TokenList condition= new TokenList();
condEndOffset= getTokensWithinPPDirective(true, condition, withinExpansion);
endOffset= lexer.currentToken().getEndOffset();
if (condition.first() == null) {
handleProblem(IProblem.SCANNER_EXPRESSION_SYNTAX_ERROR, null, startOffset, endOffset);
} else {
try {
fExpressionEvaluator.clearMacrosInDefinedExpression();
isActive= fExpressionEvaluator.evaluate(condition, fMacroDictionary, fLocationMap);
refs = fExpressionEvaluator.clearMacrosInDefinedExpression();
} catch (EvalException e) {
handleProblem(e.getProblemID(), e.getProblemArg(), condOffset, endOffset);
}
}
} else {
condEndOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
endOffset= lexer.currentToken().getEndOffset();
}
if (isElif) {
fLocationMap.encounterPoundElif(startOffset, condOffset, condEndOffset, endOffset, isActive, refs);
} else {
fLocationMap.encounterPoundIf(startOffset, condOffset, condEndOffset, endOffset, isActive, refs);
}
return fCurrentContext.setBranchState(cond, isActive, withinExpansion, startOffset);
}
private CodeState executeElse(final Lexer lexer, final int startOffset,boolean withinExpansion)
throws OffsetLimitReachedException {
final int endOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
Conditional cond= fCurrentContext.newBranch(BranchKind.eElse, withinExpansion);
if (cond == null) {
handleProblem(IProblem.PREPROCESSOR_UNBALANCE_CONDITION, Keywords.cELSE, startOffset, endOffset);
return fCurrentContext.getCodeState();
}
final boolean isActive= cond.canHaveActiveBranch(withinExpansion);
fLocationMap.encounterPoundElse(startOffset, endOffset, isActive);
return fCurrentContext.setBranchState(cond, isActive, withinExpansion, startOffset);
}
private CodeState executeEndif(Lexer lexer, int startOffset, boolean withinExpansion) throws OffsetLimitReachedException {
final int endOffset= lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
final Conditional cond= fCurrentContext.newBranch(BranchKind.eEnd, withinExpansion);
if (cond == null) {
handleProblem(IProblem.PREPROCESSOR_UNBALANCE_CONDITION, Keywords.cENDIF, startOffset, endOffset);
} else {
fLocationMap.encounterPoundEndIf(startOffset, endOffset);
}
return fCurrentContext.setBranchEndState(cond, withinExpansion, startOffset);
}
/**
* Runs the preprocessor on the rest of the line, storing the tokens in the holder supplied.
* Macro expansion is reported to the location map.
* In case isCondition is set to <code>true</code>, identifiers with image 'defined' are
* converted to the defined-token and its argument is not macro expanded.
* Returns the end-offset of the last token that was consumed.
*/
private int getTokensWithinPPDirective(boolean isCondition, TokenList result, boolean withinExpansion) throws OffsetLimitReachedException {
final ScannerContext scannerCtx= fCurrentContext;
scannerCtx.clearInactiveCodeMarkerToken();
int options= STOP_AT_NL;
if (isCondition)
options |= PROTECT_DEFINED;
loop: while (true) {
Token t= internalFetchToken(scannerCtx, options, withinExpansion);
switch (t.getType()) {
case IToken.tEND_OF_INPUT:
case IToken.tCOMPLETION:
scannerCtx.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE); // make sure the exception is thrown.
break loop;
case Lexer.tNEWLINE:
break loop;
case IToken.tIDENTIFIER:
if (isCondition && CharArrayUtils.equals(Keywords.cDEFINED, t.getCharImage())) {
t.setType(CPreprocessor.tDEFINED);
options |= NO_EXPANSION;
}
break;
case IToken.tLPAREN:
break;
default:
options &= ~NO_EXPANSION;
break;
}
result.append(t);
}
// make sure an exception is thrown if we are running content assist at the end of the line
return scannerCtx.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
}
private void skipOverConditionalCode(final Lexer lexer, boolean withinExpansion) throws OffsetLimitReachedException {
CodeState state= CodeState.eSkipInactive;
while (state == CodeState.eSkipInactive) {
state= skipBranch(lexer, withinExpansion);
}
}
private CodeState skipBranch(final Lexer lexer, boolean withinExpansion) throws OffsetLimitReachedException {
while (true) {
final Token pound = lexer.nextDirective();
int tt = pound.getType();
if (tt != IToken.tPOUND) {
if (tt == IToken.tCOMPLETION) {
// completion in inactive code
throw new OffsetLimitReachedException(ORIGIN_INACTIVE_CODE, pound);
}
// must be the end of the lexer
return CodeState.eActive;
}
final Token ident = lexer.nextToken();
tt = ident.getType();
if (tt != IToken.tIDENTIFIER) {
if (tt == IToken.tCOMPLETION) {
// completion in inactive directive
throw new OffsetLimitReachedException(ORIGIN_INACTIVE_CODE, ident);
}
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
continue;
}
// we have an identifier
final char[] name = ident.getCharImage();
final int type = fPPKeywords.get(name);
switch (type) {
case IPreprocessorDirective.ppImport:
case IPreprocessorDirective.ppInclude:
executeInclude(lexer, ident.getOffset(), false, false, withinExpansion);
break;
case IPreprocessorDirective.ppInclude_next:
executeInclude(lexer, ident.getOffset(), true, false, withinExpansion);
break;
case IPreprocessorDirective.ppIfdef:
return executeIfdef(lexer, pound.getOffset(), false, withinExpansion);
case IPreprocessorDirective.ppIfndef:
return executeIfdef(lexer, pound.getOffset(), true, withinExpansion);
case IPreprocessorDirective.ppIf:
return executeIf(lexer, pound.getOffset(), false, withinExpansion);
case IPreprocessorDirective.ppElif:
return executeIf(lexer, pound.getOffset(), true, withinExpansion);
case IPreprocessorDirective.ppElse:
return executeElse(lexer, pound.getOffset(), withinExpansion);
case IPreprocessorDirective.ppEndif:
return executeEndif(lexer, pound.getOffset(), withinExpansion);
default:
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
break;
}
}
}
/**
* The method assumes that the identifier is consumed.
* <p>
* Checks whether the identifier causes a macro expansion. May advance the current lexer
* over newlines to check for the opening bracket succeeding the identifier.
* <p>
* If applicable the macro is expanded and the resulting tokens are put onto a new context.
* @param identifier the token where macro expansion may occur.
* @param lexer the input for the expansion.
* @param stopAtNewline whether or not tokens to be read are limited to the current line.
* @param isPPCondition whether the expansion is inside of a preprocessor condition. This
* implies a specific handling for the defined token.
*/
private boolean expandMacro(final Token identifier, Lexer lexer, int options, boolean withinExpansion) throws OffsetLimitReachedException {
final char[] name= identifier.getCharImage();
PreprocessorMacro macro= fMacroDictionary.get(name);
if (macro == null) {
return false;
}
boolean stopAtNewline= (options & STOP_AT_NL) != 0;
if (macro instanceof FunctionStyleMacro) {
Token t= lexer.currentToken();
if (!stopAtNewline) {
while (t.getType() == Lexer.tNEWLINE) {
t= lexer.nextToken();
}
}
if (t.getType() != IToken.tLPAREN) {
return false;
}
}
final boolean contentAssist = fContentAssistLimit>=0 && fCurrentContext == fRootContext;
final ITokenSequence input= stopAtNewline ? fLineInputToMacroExpansion : fInputToMacroExpansion;
final MacroExpander expander = withinExpansion ? new MacroExpander(this, fMacroDictionary,
fLocationMap, fLexOptions) : fMacroExpander;
TokenList replacement= expander.expand(input, (options & PROTECT_DEFINED) != 0, macro, identifier, contentAssist);
final IASTName[] expansions= expander.clearImplicitExpansions();
final ImageLocationInfo[] ili= expander.clearImageLocationInfos();
final Token last= replacement.last();
final int length= last == null ? 0 : last.getEndOffset();
ILocationCtx ctx= fLocationMap.pushMacroExpansion(
identifier.getOffset(), identifier.getEndOffset(), lexer.getLastEndOffset(), length, macro, expansions, ili);
fCurrentContext= new ScannerContext(ctx, fCurrentContext, replacement);
return true;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(fMacroExpander.getClass())) {
return fMacroExpander;
}
return null;
}
}