| /******************************************************************************* |
| * Copyright (c) 2001 Rational Software Corp. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v0.5 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v05.html |
| * |
| * Contributors: |
| * Rational Software - initial implementation |
| ******************************************************************************/ |
| package org.eclipse.cdt.internal.core.parser; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.util.ArrayList; |
| import java.util.EmptyStackException; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| import java.util.Vector; |
| |
| import org.eclipse.cdt.core.parser.BacktrackException; |
| import org.eclipse.cdt.core.parser.EndOfFileException; |
| import org.eclipse.cdt.core.parser.ILineOffsetReconciler; |
| import org.eclipse.cdt.core.parser.IMacroDescriptor; |
| import org.eclipse.cdt.core.parser.IParser; |
| import org.eclipse.cdt.core.parser.IParserLogService; |
| 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.ISourceElementRequestor; |
| import org.eclipse.cdt.core.parser.IToken; |
| import org.eclipse.cdt.core.parser.Keywords; |
| import org.eclipse.cdt.core.parser.NullSourceElementRequestor; |
| import org.eclipse.cdt.core.parser.OffsetLimitReachedException; |
| import org.eclipse.cdt.core.parser.ParserFactory; |
| import org.eclipse.cdt.core.parser.ParserFactoryException; |
| import org.eclipse.cdt.core.parser.ParserLanguage; |
| import org.eclipse.cdt.core.parser.ParserMode; |
| import org.eclipse.cdt.core.parser.ScannerException; |
| import org.eclipse.cdt.core.parser.ScannerInfo; |
| import org.eclipse.cdt.core.parser.ast.ExpressionEvaluationException; |
| import org.eclipse.cdt.core.parser.ast.IASTExpression; |
| import org.eclipse.cdt.core.parser.ast.IASTFactory; |
| import org.eclipse.cdt.core.parser.ast.IASTInclusion; |
| |
| /** |
| * @author jcamelon |
| * |
| */ |
| |
| public class Scanner implements IScanner { |
| |
| |
| protected final IParserLogService log; |
| private final static String SCRATCH = "<scratch>"; |
| private Reader backupReader; |
| private IProblemFactory problemFactory = new ScannerProblemFactory(); |
| private boolean initialContextInitialized = false; |
| private final String filename; |
| private final Reader reader; |
| protected IToken finalToken; |
| |
| protected void handleProblem( int problemID, String argument, int beginningOffset, boolean warning, boolean error ) throws ScannerException |
| { |
| handleProblem( problemID, argument, beginningOffset, warning, error, true ); |
| } |
| |
| protected void handleProblem( int problemID, String argument, int beginningOffset, boolean warning, boolean error, boolean extra ) throws ScannerException |
| { |
| Map arguments = new HashMap(); |
| if( argument != null ) |
| { |
| String attributes [] = problemFactory.getRequiredAttributesForId( problemID ); |
| arguments.put( attributes[ 0 ], argument ); |
| } |
| IProblem p = problemFactory.createProblem( problemID, beginningOffset, getCurrentOffset(), contextStack.getCurrentLineNumber(), getCurrentFile().toCharArray(), arguments, warning, error ); |
| if( (! requestor.acceptProblem( p )) && extra ) |
| throw new ScannerException( p ); |
| } |
| |
| public Scanner(Reader reader, String filename, IScannerInfo info, ISourceElementRequestor requestor, ParserMode parserMode, ParserLanguage language, IParserLogService log ) { |
| this.log = log; |
| this.requestor = requestor; |
| this.mode = parserMode; |
| this.filename = filename; |
| this.reader = reader; |
| this.language = language; |
| astFactory = ParserFactory.createASTFactory( mode, language ); |
| this.backupReader = reader; |
| contextStack = new ContextStack( log ); |
| try { |
| //this is a hack to get around a sudden EOF experience |
| contextStack.push( |
| new ScannerContext( |
| new StringReader("\n"), |
| START, |
| ScannerContext.ContextKind.SENTINEL, null), requestor); |
| |
| } catch( ContextException ce ) { |
| //won't happen since we aren't adding an include or a macro |
| } |
| |
| originalConfig = info; |
| if( info.getDefinedSymbols() != null ) |
| definitions.putAll( info.getDefinedSymbols() ); |
| |
| if( info.getIncludePaths() != null ) |
| overwriteIncludePath( info.getIncludePaths() ); |
| |
| |
| } |
| |
| private void setupInitialContext() |
| { |
| String resolvedFilename = filename == null ? TEXT : filename; |
| IScannerContext context = null; |
| try |
| { |
| if( offsetLimit == NO_OFFSET_LIMIT ) |
| context = new ScannerContext(reader, resolvedFilename, ScannerContext.ContextKind.TOP, null ); |
| else |
| context = new LimitedScannerContext( reader, resolvedFilename, ScannerContext.ContextKind.TOP, offsetLimit ); |
| contextStack.push( context, requestor ); |
| } catch( ContextException ce ) |
| { |
| handleInternalError(); |
| } |
| initialContextInitialized = true; |
| } |
| public void addIncludePath(String includePath) { |
| includePathNames.add(includePath); |
| includePaths.add( new File( includePath ) ); |
| } |
| |
| public void overwriteIncludePath(String [] newIncludePaths) { |
| if( newIncludePaths == null ) return; |
| includePathNames = null; |
| includePaths = null; |
| includePathNames = new ArrayList(); |
| includePaths = new ArrayList(); |
| |
| for( int i = 0; i < newIncludePaths.length; ++i ) |
| includePathNames.add( newIncludePaths[i] ); |
| |
| Iterator i = includePathNames.iterator(); |
| while( i.hasNext() ) |
| { |
| String path = (String) i.next(); |
| includePaths.add( new File( path )); |
| } |
| |
| |
| } |
| |
| public void addDefinition(String key, IMacroDescriptor macro) { |
| definitions.put(key, macro); |
| } |
| |
| public void addDefinition(String key, String value) { |
| definitions.put(key, value); |
| } |
| |
| public final Object getDefinition(String key) { |
| return definitions.get(key); |
| } |
| |
| public final String[] getIncludePaths() { |
| return (String[])includePathNames.toArray(); |
| } |
| |
| protected boolean skipOverWhitespace() throws ScannerException { |
| int c = getChar(); |
| boolean result = false; |
| while ((c != NOCHAR) && ((c == ' ') || (c == '\t'))) |
| { |
| c = getChar(); |
| result = true; |
| } |
| if (c != NOCHAR) |
| ungetChar(c); |
| return result; |
| |
| } |
| |
| protected String getRestOfPreprocessorLine() throws ScannerException { |
| StringBuffer buffer = new StringBuffer(); |
| skipOverWhitespace(); |
| int c = getChar(); |
| boolean inString = false; |
| boolean inChar = false; |
| while (true) { |
| while ((c != '\n') |
| && (c != '\r') |
| && (c != '\\') |
| && (c != '/') |
| && (c != '"' || ( c == '"' && inChar ) ) |
| && (c != '\'' || ( c == '\'' && inString ) ) |
| && (c != NOCHAR)) { |
| buffer.append((char) c); |
| c = getChar( true ); |
| } |
| |
| if (c == '/') { |
| //only care about comments outside of a quote |
| if( inString || inChar ){ |
| buffer.append( (char) c ); |
| c = getChar( true ); |
| continue; |
| } |
| |
| // we need to peek ahead at the next character to see if |
| // this is a comment or not |
| int next = getChar(); |
| if (next == '/') { |
| // single line comment |
| skipOverSinglelineComment(); |
| break; |
| } else if (next == '*') { |
| // multiline comment |
| if (skipOverMultilineComment()) |
| break; |
| else |
| c = getChar( true ); |
| continue; |
| } else { |
| // we are not in a comment |
| buffer.append((char) c); |
| c = next; |
| continue; |
| } |
| } else if( c == '"' ){ |
| inString = !inString; |
| buffer.append((char) c); |
| c = getChar( true ); |
| continue; |
| } else if( c == '\'' ){ |
| inChar = !inChar; |
| buffer.append((char) c); |
| c = getChar( true ); |
| continue; |
| } else if( c == '\\' ){ |
| c = getChar(true); |
| if( c == '\r' ){ |
| c = getChar(true); |
| if( c == '\n' ){ |
| c = getChar(true); |
| } |
| } else if( c == '\n' ){ |
| c = getChar(true); |
| } else { |
| buffer.append('\\'); |
| if( c == '"' || c == '\'' ){ |
| buffer.append((char)c); |
| c = getChar( true ); |
| } |
| } |
| continue; |
| } else { |
| ungetChar(c); |
| break; |
| } |
| } |
| |
| return buffer.toString(); |
| } |
| |
| protected void skipOverTextUntilNewline() throws ScannerException { |
| for (;;) { |
| switch (getChar()) { |
| case NOCHAR : |
| case '\n' : |
| return; |
| case '\\' : |
| getChar(); |
| } |
| } |
| } |
| |
| private void setCurrentToken(IToken t) { |
| if (currentToken != null) |
| currentToken.setNext(t); |
| finalToken = t; |
| currentToken = t; |
| } |
| |
| protected void resetStorageBuffer() |
| { |
| if( storageBuffer != null ) |
| storageBuffer = null; |
| } |
| |
| protected IToken newToken(int t, String i, IScannerContext c) { |
| setCurrentToken(new Token(t, i, c)); |
| return currentToken; |
| } |
| |
| protected IToken newToken(int t, String i) { |
| setCurrentToken(new Token(t, i)); |
| return currentToken; |
| } |
| |
| protected String getNextIdentifier() throws ScannerException { |
| StringBuffer buffer = new StringBuffer(); |
| skipOverWhitespace(); |
| int c = getChar(); |
| |
| if (((c >= 'a') && (c <= 'z')) |
| || ((c >= 'A') && (c <= 'Z')) | (c == '_')) { |
| buffer.append((char) c); |
| |
| c = getChar(); |
| while (((c >= 'a') && (c <= 'z')) |
| || ((c >= 'A') && (c <= 'Z')) |
| || ((c >= '0') && (c <= '9')) |
| || (c == '_')) { |
| buffer.append((char) c); |
| c = getChar(); |
| } |
| } |
| ungetChar(c); |
| |
| return buffer.toString(); |
| } |
| |
| protected void handleInclusion(String fileName, boolean useIncludePaths, int nameOffset, int beginOffset, int endOffset ) throws ScannerException { |
| |
| FileReader inclusionReader = null; |
| String newPath = null; |
| if( useIncludePaths ) // search include paths for this file |
| { |
| // iterate through the include paths |
| Iterator iter = includePaths.iterator(); |
| |
| while (iter.hasNext()) { |
| |
| File pathFile = (File)iter.next(); |
| String path = pathFile.getPath(); |
| if( !pathFile.exists() && path.indexOf('\"') != -1 ) |
| { |
| StringTokenizer tokenizer = new StringTokenizer(path, "\"" ); //$NON-NLS-1$ |
| StringBuffer buffer = new StringBuffer(path.length() ); |
| while( tokenizer.hasMoreTokens() ){ |
| buffer.append( tokenizer.nextToken() ); |
| } |
| pathFile = new File( buffer.toString() ); |
| } |
| if (pathFile.isDirectory()) { |
| newPath = pathFile.getPath() + File.separatorChar + fileName; |
| File includeFile = new File(newPath); |
| if (includeFile.exists() && includeFile.isFile()) { |
| try { |
| inclusionReader = new FileReader(includeFile); |
| break; |
| } catch (FileNotFoundException fnf) { |
| // do nothing - check the next directory |
| } |
| } |
| } |
| } |
| |
| if (inclusionReader == null ) |
| { |
| handleProblem( IProblem.PREPROCESSOR_INCLUSION_NOT_FOUND, fileName, beginOffset, false, true, true ); |
| } |
| } |
| else // local inclusion |
| { |
| String currentFilename = contextStack.getCurrentContext().getFilename(); |
| File currentIncludeFile = new File( currentFilename ); |
| String parentDirectory = currentIncludeFile.getParentFile().getAbsolutePath(); |
| currentIncludeFile = null; |
| newPath = parentDirectory + File.separatorChar + fileName; |
| File includeFile = new File( newPath ); |
| if (includeFile.exists() && includeFile.isFile()) { |
| try { |
| inclusionReader = |
| new FileReader(includeFile); |
| } catch (FileNotFoundException fnf) { |
| // the spec says that if finding in the local directory fails, search the include paths |
| handleInclusion( fileName, true, nameOffset, beginOffset, endOffset ); |
| } |
| } |
| else |
| { |
| // the spec says that if finding in the local directory fails, search the include paths |
| handleInclusion( fileName, true, nameOffset, beginOffset, endOffset ); |
| } |
| } |
| if (inclusionReader != null) { |
| IASTInclusion inclusion = null; |
| try |
| { |
| inclusion = |
| astFactory.createInclusion( |
| fileName, |
| newPath, |
| !useIncludePaths, |
| beginOffset, |
| nameOffset, |
| nameOffset + fileName.length(), |
| endOffset); |
| } |
| catch (Exception e) |
| { |
| /* do nothing */ |
| } |
| |
| try |
| { |
| contextStack.updateContext(inclusionReader, newPath, ScannerContext.ContextKind.INCLUSION, inclusion, requestor ); |
| } |
| catch (ContextException e1) |
| { |
| handleProblem( e1.getId(), fileName, beginOffset, false, true, true ); |
| } |
| } |
| } |
| |
| // constants |
| private static final int NOCHAR = -1; |
| |
| private static final String TEXT = "<text>"; |
| private static final String START = "<initial reader>"; |
| private static final String EXPRESSION = "<expression>"; |
| private static final String PASTING = "<pasting>"; |
| |
| private static final String DEFINED = "defined"; |
| private static final String _PRAGMA = "_Pragma"; |
| private static final String POUND_DEFINE = "#define "; |
| |
| private ContextStack contextStack = null; |
| private IScannerContext lastContext = null; |
| |
| private IScannerInfo originalConfig; |
| private List includePathNames = new ArrayList(); |
| private List includePaths = new ArrayList(); |
| private Hashtable definitions = new Hashtable(); |
| private StringBuffer storageBuffer = null; |
| |
| private int count = 0; |
| private static HashMap cppKeywords = new HashMap(); |
| private static HashMap cKeywords = new HashMap(); |
| private static HashMap ppDirectives = new HashMap(); |
| |
| private IToken currentToken = null; |
| private IToken cachedToken = null; |
| |
| private boolean passOnToClient = true; |
| private BranchTracker branches = new BranchTracker(); |
| |
| // these are scanner configuration aspects that we perhaps want to tweak |
| // eventually, these should be configurable by the client, but for now |
| // we can just leave it internal |
| private boolean enableDigraphReplacement = true; |
| private boolean enableTrigraphReplacement = true; |
| private boolean enableTrigraphReplacementInStrings = true; |
| private boolean throwExceptionOnBadCharacterRead = false; |
| private boolean atEOF = false; |
| |
| private boolean tokenizingMacroReplacementList = false; |
| public void setTokenizingMacroReplacementList( boolean mr ){ |
| tokenizingMacroReplacementList = mr; |
| } |
| |
| private final ParserMode mode; |
| |
| public int getCharacter() throws ScannerException |
| { |
| if( ! initialContextInitialized ) |
| setupInitialContext(); |
| |
| return getChar(); |
| } |
| |
| private int getChar() throws ScannerException |
| { |
| return getChar( false ); |
| } |
| |
| private int getChar( boolean insideString ) throws ScannerException { |
| int c = NOCHAR; |
| |
| lastContext = contextStack.getCurrentContext(); |
| |
| if (contextStack.getCurrentContext() == null) |
| // past the end of file |
| return c; |
| |
| c = accountForUndo(c); |
| |
| int baseOffset = lastContext.getOffset() - lastContext.undoStackSize() - 1; |
| |
| if (enableTrigraphReplacement && (!insideString || enableTrigraphReplacementInStrings)) { |
| // Trigraph processing |
| enableTrigraphReplacement = false; |
| if (c == '?') { |
| c = getChar(insideString); |
| if (c == '?') { |
| c = getChar(insideString); |
| switch (c) { |
| case '(': |
| expandDefinition("??(", "[", baseOffset); |
| c = getChar(insideString); |
| break; |
| case ')': |
| expandDefinition("??)", "]", baseOffset); |
| c = getChar(insideString); |
| break; |
| case '<': |
| expandDefinition("??<", "{", baseOffset); |
| c = getChar(insideString); |
| break; |
| case '>': |
| expandDefinition("??>", "}", baseOffset); |
| c = getChar(insideString); |
| break; |
| case '=': |
| expandDefinition("??=", "#", baseOffset); |
| c = getChar(insideString); |
| break; |
| case '/': |
| expandDefinition("??/", "\\", baseOffset); |
| c = getChar(insideString); |
| break; |
| case '\'': |
| expandDefinition("??\'", "^", baseOffset); |
| c = getChar(insideString); |
| break; |
| case '!': |
| expandDefinition("??!", "|", baseOffset); |
| c = getChar(insideString); |
| break; |
| case '-': |
| expandDefinition("??-", "~", baseOffset); |
| c = getChar(insideString); |
| break; |
| default: |
| // Not a trigraph |
| ungetChar(c); |
| ungetChar('?'); |
| c = '?'; |
| } |
| } else { |
| // Not a trigraph |
| ungetChar(c); |
| c = '?'; |
| } |
| } |
| enableTrigraphReplacement = true; |
| } |
| |
| if (!insideString) |
| { |
| if (c == '\\') { |
| c = getChar(false); |
| if (c == '\r') { |
| c = getChar(false); |
| if (c == '\n') |
| { |
| c = getChar(false); |
| } |
| } else if (c == '\n') |
| { |
| c = getChar(false); |
| |
| } else // '\' is not the last character on the line |
| { |
| ungetChar(c); |
| c = '\\'; |
| } |
| } else if (enableDigraphReplacement) { |
| enableDigraphReplacement = false; |
| // Digraph processing |
| if (c == '<') { |
| c = getChar(false); |
| if (c == '%') { |
| expandDefinition("<%", "{", baseOffset); |
| c = getChar(false); |
| } else if (c == ':') { |
| expandDefinition("<:", "[", baseOffset); |
| c = getChar(false); |
| } else { |
| // Not a digraph |
| ungetChar(c); |
| c = '<'; |
| } |
| } else if (c == ':') { |
| c = getChar(false); |
| if (c == '>') { |
| expandDefinition(":>", "]", baseOffset); |
| c = getChar(false); |
| } else { |
| // Not a digraph |
| ungetChar(c); |
| c = ':'; |
| } |
| } else if (c == '%') { |
| c = getChar(false); |
| if (c == '>') { |
| expandDefinition("%>", "}", baseOffset); |
| c = getChar(false); |
| } else if (c == ':') { |
| expandDefinition("%:", "#", baseOffset); |
| c = getChar(false); |
| } else { |
| // Not a digraph |
| ungetChar(c); |
| c = '%'; |
| } |
| } |
| enableDigraphReplacement = true; |
| } |
| } |
| |
| return c; |
| } |
| |
| |
| |
| protected int accountForUndo(int c) |
| { |
| boolean done; |
| do { |
| done = true; |
| |
| if (contextStack.getCurrentContext().undoStackSize() != 0 ) { |
| c = contextStack.getCurrentContext().popUndo(); |
| } else { |
| try { |
| c = contextStack.getCurrentContext().read(); |
| if (c == NOCHAR) { |
| if (contextStack.rollbackContext(requestor) == false) { |
| c = NOCHAR; |
| break; |
| } else { |
| done = false; |
| } |
| } |
| } catch (IOException e) { |
| if (contextStack.rollbackContext(requestor) == false) { |
| c = NOCHAR; |
| } else { |
| done = false; |
| } |
| } |
| } |
| } while (!done); |
| return c; |
| } |
| |
| private void ungetChar(int c) throws ScannerException{ |
| contextStack.getCurrentContext().pushUndo(c); |
| try |
| { |
| contextStack.undoRollback( lastContext, requestor ); |
| } |
| catch (ContextException e) |
| { |
| handleProblem( e.getId(), contextStack.getCurrentContext().getFilename(), getCurrentOffset(), false, true, true ); |
| } |
| } |
| |
| protected boolean lookAheadForTokenPasting() throws ScannerException |
| { |
| int c = getChar(); |
| if( c == '#' ) |
| { |
| c = getChar(); |
| if( c == '#' ) |
| { |
| return true; |
| } |
| else |
| { |
| ungetChar( c ); |
| } |
| } |
| |
| ungetChar( c ); |
| return false; |
| |
| } |
| |
| protected void consumeUntilOutOfMacroExpansion() throws ScannerException |
| { |
| while( contextStack.getCurrentContext().getKind() == IScannerContext.ContextKind.MACROEXPANSION ) |
| getChar(); |
| } |
| |
| public IToken nextToken() throws ScannerException, EndOfFileException, OffsetLimitReachedException { |
| return nextToken( true, false ); |
| } |
| |
| public IToken nextToken(boolean pasting) throws ScannerException, EndOfFileException, OffsetLimitReachedException { |
| return nextToken( pasting, false ); |
| } |
| |
| public IToken nextToken( boolean pasting, boolean lookingForNextAlready ) throws ScannerException, EndOfFileException, OffsetLimitReachedException |
| { |
| if( ! initialContextInitialized ) |
| setupInitialContext(); |
| |
| if( cachedToken != null ){ |
| setCurrentToken( cachedToken ); |
| cachedToken = null; |
| return currentToken; |
| } |
| |
| count++; |
| boolean possibleWideLiteral = true; |
| boolean wideLiteral = false; |
| int c = getChar(); |
| |
| while (c != NOCHAR) { |
| if ( ! passOnToClient ) { |
| |
| |
| while (c != NOCHAR && c != '#' ) |
| { |
| c = getChar(); |
| if( c == '/' ) |
| { |
| c = getChar(); |
| if( c == '/' ) |
| { |
| skipOverSinglelineComment(); |
| c = getChar(); |
| continue; |
| } |
| else if( c == '*' ) |
| { |
| skipOverMultilineComment(); |
| c = getChar(); |
| continue; |
| } |
| } |
| } |
| |
| if( c == NOCHAR ) continue; |
| } |
| |
| if ((c == ' ') || (c == '\r') || (c == '\t') || (c == '\n')) { |
| c = getChar(); |
| continue; |
| } else if (c == 'L' && possibleWideLiteral ) { |
| int oldChar = c; |
| c = getChar(); |
| if(!(c == '"' || c == '\'')) { |
| // we have made a mistake |
| ungetChar(c); |
| c = oldChar; |
| possibleWideLiteral = false; |
| continue; |
| } |
| wideLiteral = true; |
| continue; |
| } else if (c == '"') { |
| int beginOffset = getCurrentOffset(); |
| // string |
| StringBuffer buff = new StringBuffer(); |
| int beforePrevious = NOCHAR; |
| int previous = c; |
| c = getChar(true); |
| |
| for( ; ; ) |
| { |
| if ( ( c =='"' ) && ( previous != '\\' || beforePrevious == '\\') ) break; |
| if ( ( c == '\n' ) && ( previous != '\\' || beforePrevious == '\\') ) |
| { |
| handleProblem( IProblem.SCANNER_UNBOUNDED_STRING, null, beginOffset, false, true, true ); |
| } |
| |
| if( c == NOCHAR) break; |
| buff.append((char) c); |
| beforePrevious = previous; |
| previous = c; |
| c = getChar(true); |
| } |
| |
| if (c != NOCHAR ) |
| { |
| int type = wideLiteral ? IToken.tLSTRING : IToken.tSTRING; |
| |
| //If the next token is going to be a string as well, we need to concatenate |
| //it with this token. |
| IToken returnToken = newToken( type, buff.toString(), contextStack.getCurrentContext()); |
| |
| if (!lookingForNextAlready) { |
| IToken next = null; |
| try{ |
| next = nextToken( true, true ); |
| } catch( EndOfFileException e ){ |
| next = null; |
| } |
| |
| while( next != null && ( next.getType() == IToken.tSTRING || |
| next.getType() == IToken.tLSTRING ) ){ |
| returnToken.setImage( returnToken.getImage() + next.getImage() ); |
| returnToken.setNext( null ); |
| currentToken = returnToken; |
| try{ |
| next = nextToken( true, true ); |
| } catch( EndOfFileException e ){ |
| next = null; |
| } |
| } |
| |
| cachedToken = next; |
| |
| } |
| |
| currentToken = returnToken; |
| returnToken.setNext( null ); |
| |
| return returnToken; |
| |
| } else |
| handleProblem( IProblem.SCANNER_UNBOUNDED_STRING, null, beginOffset, false, true, true ); |
| |
| |
| } else if ( |
| ((c >= 'a') && (c <= 'z')) |
| || ((c >= 'A') && (c <= 'Z')) || (c == '_')) { |
| |
| int baseOffset = lastContext.getOffset() - lastContext.undoStackSize() - 1; |
| |
| // String buffer is slow, we need a better way such as memory mapped files |
| StringBuffer buff = new StringBuffer(); |
| buff.append((char) c); |
| |
| c = getChar(); |
| |
| while (((c >= 'a') && (c <= 'z')) |
| || ((c >= 'A') && (c <= 'Z')) |
| || ((c >= '0') && (c <= '9')) |
| || (c == '_')) { |
| buff.append((char) c); |
| c = getChar(); |
| } |
| |
| ungetChar(c); |
| |
| String ident = buff.toString(); |
| |
| if (ident.equals(DEFINED)) |
| return newToken(IToken.tINTEGER, handleDefinedMacro()); |
| |
| if( ident.equals(_PRAGMA) && language == ParserLanguage.C ) |
| { |
| handlePragmaOperator(); |
| c = getChar(); |
| continue; |
| } |
| |
| Object mapping = definitions.get(ident); |
| |
| if (mapping != null) { |
| if( contextStack.shouldExpandDefinition( POUND_DEFINE + ident ) ) { |
| expandDefinition(ident, mapping, baseOffset); |
| c = getChar(); |
| continue; |
| } |
| } |
| |
| Object tokenTypeObject; |
| |
| if( language == ParserLanguage.CPP ) |
| tokenTypeObject = cppKeywords.get(ident); |
| else |
| tokenTypeObject = cKeywords.get(ident); |
| |
| int tokenType = IToken.tIDENTIFIER; |
| if (tokenTypeObject != null) |
| tokenType = ((Integer) tokenTypeObject).intValue(); |
| |
| if( pasting ) |
| { |
| if( lookAheadForTokenPasting() ) |
| { |
| if( storageBuffer == null ) |
| storageBuffer = buff; |
| else |
| storageBuffer.append( ident ); |
| |
| c = getChar(); |
| continue; |
| } |
| else |
| { |
| if( storageBuffer != null ) |
| { |
| storageBuffer.append( ident ); |
| try |
| { |
| contextStack.updateContext( new StringReader( storageBuffer.toString()), PASTING, IScannerContext.ContextKind.MACROEXPANSION, null, requestor ); |
| } |
| catch (ContextException e) |
| { |
| handleProblem( e.getId(), contextStack.getCurrentContext().getFilename(), getCurrentOffset(), false, true, true ); |
| } |
| storageBuffer = null; |
| c = getChar(); |
| continue; |
| } |
| } |
| } |
| |
| return newToken(tokenType, ident, contextStack.getCurrentContext()); |
| } else if ((c >= '0') && (c <= '9') || c == '.' ) { |
| int beginOffset = getCurrentOffset(); |
| StringBuffer buff; |
| |
| if( pasting ) |
| { |
| if( storageBuffer != null ) |
| buff = storageBuffer; |
| else |
| buff = new StringBuffer(); |
| } |
| else |
| buff = new StringBuffer(); |
| |
| |
| boolean hex = false; |
| boolean floatingPoint = ( c == '.' ) ? true : false; |
| boolean firstCharZero = ( c== '0' )? true : false; |
| |
| buff.append((char) c); |
| |
| c = getChar(); |
| |
| if( ! firstCharZero && floatingPoint && !(c >= '0' && c <= '9') ){ |
| //if pasting, there could actually be a float here instead of just a . |
| if( buff.toString().equals( "." ) ){ |
| if( c == '*' ){ |
| return newToken( IToken.tDOTSTAR, ".*", contextStack.getCurrentContext() ); |
| } else if( c == '.' ){ |
| if( getChar() == '.' ) |
| return newToken( IToken.tELLIPSIS, "..." ); |
| else |
| handleProblem( IProblem.SCANNER_BAD_FLOATING_POINT, null, beginOffset, false, true, true ); |
| } else { |
| ungetChar( c ); |
| return newToken( IToken.tDOT, ".", contextStack.getCurrentContext() ); |
| } |
| } |
| } else if (c == 'x') { |
| if( ! firstCharZero ) |
| { |
| handleProblem( IProblem.SCANNER_BAD_HEX_FORMAT, null, beginOffset, false, true, true ); |
| c = getChar(); |
| continue; |
| } |
| buff.append( (char)c ); |
| hex = true; |
| c = getChar(); |
| } |
| |
| while ((c >= '0' && c <= '9') |
| || (hex |
| && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))) { |
| buff.append((char) c); |
| c = getChar(); |
| } |
| |
| if( c == '.' ) |
| { |
| buff.append( (char)c); |
| |
| floatingPoint = true; |
| c= getChar(); |
| while ((c >= '0' && c <= '9') |
| || (hex |
| && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))) |
| { |
| buff.append((char) c); |
| c = getChar(); |
| } |
| } |
| |
| |
| if (c == 'e' || c == 'E' || (hex && (c == 'p' || c == 'P'))) |
| { |
| if( ! floatingPoint ) floatingPoint = true; |
| // exponent type for floating point |
| buff.append((char)c); |
| c = getChar(); |
| |
| // optional + or - |
| if( c == '+' || c == '-' ) |
| { |
| buff.append( (char)c ); |
| c = getChar(); |
| } |
| |
| // digit sequence of exponent part |
| while ((c >= '0' && c <= '9') ) |
| { |
| buff.append((char) c); |
| c = getChar(); |
| } |
| |
| // optional suffix |
| if( c == 'l' || c == 'L' || c == 'f' || c == 'F' ) |
| { |
| buff.append( (char)c ); |
| c = getChar(); |
| } |
| } else { |
| if( floatingPoint ){ |
| //floating-suffix |
| if( c == 'l' || c == 'L' || c == 'f' || c == 'F' ){ |
| c = getChar(); |
| } |
| } else { |
| //integer suffix |
| if( c == 'u' || c == 'U' ){ |
| c = getChar(); |
| if( c == 'l' || c == 'L') |
| c = getChar(); |
| if( c == 'l' || c == 'L') |
| c = getChar(); |
| } else if( c == 'l' || c == 'L' ){ |
| c = getChar(); |
| if( c == 'l' || c == 'L') |
| c = getChar(); |
| if( c == 'u' || c == 'U' ) |
| c = getChar(); |
| } |
| } |
| } |
| |
| ungetChar( c ); |
| |
| if( pasting ) |
| { |
| if( lookAheadForTokenPasting() ) |
| { |
| storageBuffer = buff; |
| c = getChar(); |
| continue; |
| } |
| else |
| { |
| if( storageBuffer != null ) |
| { |
| try |
| { |
| contextStack.updateContext( new StringReader( buff.toString()), PASTING, IScannerContext.ContextKind.MACROEXPANSION, null, requestor ); |
| } |
| catch (ContextException e) |
| { |
| handleProblem( e.getId(), contextStack.getCurrentContext().getFilename(), getCurrentOffset(), false, true, true ); |
| } |
| storageBuffer = null; |
| c = getChar(); |
| continue; |
| } |
| } |
| } |
| |
| int tokenType; |
| String result = buff.toString(); |
| |
| if( floatingPoint && result.equals(".") ) |
| tokenType = IToken.tDOT; |
| else |
| tokenType = floatingPoint ? IToken.tFLOATINGPT : IToken.tINTEGER; |
| |
| return newToken( |
| tokenType, |
| result, |
| contextStack.getCurrentContext()); |
| |
| } else if (c == '#') { |
| int beginningOffset = contextStack.getCurrentContext().getOffset() - 1; |
| // lets prepare for a preprocessor statement |
| StringBuffer buff = new StringBuffer(); |
| buff.append((char) c); |
| |
| // we are allowed arbitrary whitespace after the '#' and before the rest of the text |
| boolean skipped = skipOverWhitespace(); |
| |
| c = getChar(); |
| |
| if( c == '#' ) |
| { |
| if( skipped ) |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "# #", beginningOffset, false, true, true ); |
| else |
| return newToken( tPOUNDPOUND, "##" ); |
| } else if( tokenizingMacroReplacementList ) { |
| ungetChar( c ); |
| return newToken( tPOUND, "#" ); |
| } |
| |
| while (((c >= 'a') && (c <= 'z')) |
| || ((c >= 'A') && (c <= 'Z')) || (c == '_') ) { |
| buff.append((char) c); |
| c = getChar(); |
| } |
| ungetChar(c); |
| |
| String token = buff.toString(); |
| |
| Object directive = ppDirectives.get(token); |
| if (directive == null) { |
| if (true) |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#"+token, beginningOffset, false, true, true ); |
| } else { |
| int type = ((Integer) directive).intValue(); |
| switch (type) { |
| case PreprocessorDirectives.DEFINE : |
| if ( ! passOnToClient ) { |
| skipOverTextUntilNewline(); |
| c = getChar(); |
| continue; |
| } |
| |
| poundDefine(beginningOffset); |
| |
| c = getChar(); |
| continue; |
| |
| case PreprocessorDirectives.INCLUDE : |
| if (! passOnToClient ) { |
| skipOverTextUntilNewline(); |
| c = getChar(); |
| continue; |
| } |
| |
| poundInclude( beginningOffset ); |
| |
| c = getChar(); |
| continue; |
| case PreprocessorDirectives.UNDEFINE : |
| if (! passOnToClient) { |
| skipOverTextUntilNewline(); |
| c = getChar(); |
| continue; |
| } |
| skipOverWhitespace(); |
| // definition |
| String toBeUndefined = getNextIdentifier(); |
| |
| definitions.remove(toBeUndefined); |
| |
| skipOverTextUntilNewline(); |
| c = getChar(); |
| continue; |
| case PreprocessorDirectives.IF : |
| // get the rest of the line |
| int currentOffset = getCurrentOffset(); |
| String expression = getRestOfPreprocessorLine(); |
| |
| boolean expressionEvalResult = false; |
| try{ |
| expressionEvalResult = evaluateExpression(expression, currentOffset); |
| } catch( ScannerException e ){} |
| |
| passOnToClient = branches.poundif( expressionEvalResult ); |
| c = getChar(); |
| continue; |
| |
| |
| |
| case PreprocessorDirectives.IFDEF : |
| skipOverWhitespace(); |
| String definition = getNextIdentifier(); |
| Object mapping = definitions.get(definition); |
| |
| if (mapping == null) { |
| // not defined |
| passOnToClient = branches.poundif( false ); |
| skipOverTextUntilNewline(); |
| } else { |
| passOnToClient = branches.poundif( true ); |
| // continue along, act like nothing is wrong :-) |
| c = getChar(); |
| } |
| continue; |
| case PreprocessorDirectives.ENDIF : |
| String restOfLine = getRestOfPreprocessorLine().trim(); |
| if( ! restOfLine.equals( "" ) && true ) |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#endif " + restOfLine, beginningOffset, false, true, true ); |
| passOnToClient = branches.poundendif(); |
| c = getChar(); |
| continue; |
| |
| case PreprocessorDirectives.IFNDEF : |
| skipOverWhitespace(); |
| String def = getNextIdentifier(); |
| Object map = definitions.get(def); |
| |
| if (map != null) { |
| // not defined |
| skipOverTextUntilNewline(); |
| passOnToClient = branches.poundif( false ); |
| } else { |
| passOnToClient = branches.poundif( true ); |
| // continue along, act like nothing is wrong :-) |
| c = getChar(); |
| } |
| continue; |
| |
| case PreprocessorDirectives.ELSE : |
| try |
| { |
| passOnToClient = branches.poundelse(); |
| } |
| catch( EmptyStackException ese ) |
| { |
| handleProblem( IProblem.PREPROCESSOR_UNBALANCE_CONDITION, |
| token, |
| beginningOffset, |
| false, true, true ); |
| } |
| |
| skipOverTextUntilNewline(); |
| c = getChar(); |
| continue; |
| |
| case PreprocessorDirectives.ELIF : |
| int co = getCurrentOffset(); |
| String elsifExpression = getRestOfPreprocessorLine().trim(); |
| |
| if (elsifExpression.equals("")) |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#elif", beginningOffset, false, true, true ); |
| |
| boolean elsifResult = false; |
| elsifResult = evaluateExpression(elsifExpression, co ); |
| |
| try |
| { |
| passOnToClient = branches.poundelif( elsifResult ); |
| } |
| catch( EmptyStackException ese ) |
| { |
| handleProblem( IProblem.PREPROCESSOR_UNBALANCE_CONDITION, |
| token + ' ' + elsifExpression, |
| beginningOffset, |
| false, true, true ); |
| } |
| c = getChar(); |
| continue; |
| |
| case PreprocessorDirectives.LINE : |
| //TO DO |
| skipOverTextUntilNewline(); |
| c = getChar(); |
| continue; |
| case PreprocessorDirectives.ERROR : |
| if (! passOnToClient) { |
| skipOverTextUntilNewline(); |
| c = getChar(); |
| continue; |
| } |
| handleProblem( IProblem.PREPROCESSOR_POUND_ERROR, getRestOfPreprocessorLine(), beginningOffset, false, true, true ); |
| c = getChar(); |
| continue; |
| case PreprocessorDirectives.PRAGMA : |
| skipOverTextUntilNewline(); |
| c = getChar(); |
| continue; |
| case PreprocessorDirectives.BLANK : |
| String remainderOfLine = |
| getRestOfPreprocessorLine().trim(); |
| if (!remainderOfLine.equals("")) { |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "# " + remainderOfLine, beginningOffset, false, true, true ); |
| } |
| c = getChar(); |
| continue; |
| default : |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#" + token, beginningOffset, false, true, true ); |
| } |
| } |
| } else { |
| switch (c) { |
| case '\'' : |
| return processCharacterLiteral( c, wideLiteral ); |
| case ':' : |
| c = getChar(); |
| switch (c) { |
| case ':' : |
| return newToken( |
| IToken.tCOLONCOLON, |
| "::", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tCOLON, |
| ":", |
| contextStack.getCurrentContext()); |
| } |
| case ';' : |
| return newToken(IToken.tSEMI, ";", contextStack.getCurrentContext()); |
| case ',' : |
| return newToken(IToken.tCOMMA, ",", contextStack.getCurrentContext()); |
| case '?' : |
| return newToken(IToken.tQUESTION, "?", contextStack.getCurrentContext()); |
| case '(' : |
| return newToken(IToken.tLPAREN, "(", contextStack.getCurrentContext()); |
| case ')' : |
| return newToken(IToken.tRPAREN, ")", contextStack.getCurrentContext()); |
| case '[' : |
| return newToken(IToken.tLBRACKET, "[", contextStack.getCurrentContext()); |
| case ']' : |
| return newToken(IToken.tRBRACKET, "]", contextStack.getCurrentContext()); |
| case '{' : |
| return newToken(IToken.tLBRACE, "{", contextStack.getCurrentContext()); |
| case '}' : |
| return newToken(IToken.tRBRACE, "}", contextStack.getCurrentContext()); |
| case '+' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tPLUSASSIGN, |
| "+=", |
| contextStack.getCurrentContext()); |
| case '+' : |
| return newToken( |
| IToken.tINCR, |
| "++", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tPLUS, |
| "+", |
| contextStack.getCurrentContext()); |
| } |
| case '-' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tMINUSASSIGN, |
| "-=", |
| contextStack.getCurrentContext()); |
| case '-' : |
| return newToken( |
| IToken.tDECR, |
| "--", |
| contextStack.getCurrentContext()); |
| case '>' : |
| c = getChar(); |
| switch (c) { |
| case '*' : |
| return newToken( |
| IToken.tARROWSTAR, |
| "->*", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tARROW, |
| "->", |
| contextStack.getCurrentContext()); |
| } |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tMINUS, |
| "-", |
| contextStack.getCurrentContext()); |
| } |
| case '*' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tSTARASSIGN, |
| "*=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tSTAR, |
| "*", |
| contextStack.getCurrentContext()); |
| } |
| case '%' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tMODASSIGN, |
| "%=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tMOD, |
| "%", |
| contextStack.getCurrentContext()); |
| } |
| case '^' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tXORASSIGN, |
| "^=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tXOR, |
| "^", |
| contextStack.getCurrentContext()); |
| } |
| case '&' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tAMPERASSIGN, |
| "&=", |
| contextStack.getCurrentContext()); |
| case '&' : |
| return newToken( |
| IToken.tAND, |
| "&&", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tAMPER, |
| "&", |
| contextStack.getCurrentContext()); |
| } |
| case '|' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tBITORASSIGN, |
| "|=", |
| contextStack.getCurrentContext()); |
| case '|' : |
| return newToken( |
| IToken.tOR, |
| "||", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tBITOR, |
| "|", |
| contextStack.getCurrentContext()); |
| } |
| case '~' : |
| return newToken(IToken.tCOMPL, "~", contextStack.getCurrentContext()); |
| case '!' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tNOTEQUAL, |
| "!=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tNOT, |
| "!", |
| contextStack.getCurrentContext()); |
| } |
| case '=' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tEQUAL, |
| "==", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tASSIGN, |
| "=", |
| contextStack.getCurrentContext()); |
| } |
| case '<' : |
| c = getChar(); |
| switch (c) { |
| case '<' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tSHIFTLASSIGN, |
| "<<=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tSHIFTL, |
| "<<", |
| contextStack.getCurrentContext()); |
| } |
| case '=' : |
| return newToken( |
| IToken.tLTEQUAL, |
| "<=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| if( forInclusion ) |
| temporarilyReplaceDefinitionsMap(); |
| return newToken(IToken.tLT, "<", contextStack.getCurrentContext()); |
| } |
| case '>' : |
| c = getChar(); |
| switch (c) { |
| case '>' : |
| c = getChar(); |
| switch (c) { |
| case '=' : |
| return newToken( |
| IToken.tSHIFTRASSIGN, |
| ">>=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tSHIFTR, |
| ">>", |
| contextStack.getCurrentContext()); |
| } |
| case '=' : |
| return newToken( |
| IToken.tGTEQUAL, |
| ">=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| if( forInclusion ) |
| restoreDefinitionsMap(); |
| return newToken(IToken.tGT, ">", contextStack.getCurrentContext()); |
| } |
| case '.' : |
| c = getChar(); |
| switch (c) { |
| case '.' : |
| c = getChar(); |
| switch (c) { |
| case '.' : |
| return newToken( |
| IToken.tELLIPSIS, |
| "...", |
| contextStack.getCurrentContext()); |
| default : |
| break; |
| } |
| break; |
| case '*' : |
| return newToken( |
| IToken.tDOTSTAR, |
| ".*", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tDOT, |
| ".", |
| contextStack.getCurrentContext()); |
| } |
| break; |
| case '/' : |
| c = getChar(); |
| switch (c) { |
| case '/' : |
| skipOverSinglelineComment(); |
| c = getChar(); |
| continue; |
| case '*' : |
| skipOverMultilineComment(); |
| c = getChar(); |
| continue; |
| case '=' : |
| return newToken( |
| IToken.tDIVASSIGN, |
| "/=", |
| contextStack.getCurrentContext()); |
| default : |
| ungetChar(c); |
| return newToken( |
| IToken.tDIV, |
| "/", |
| contextStack.getCurrentContext()); |
| } |
| default : |
| handleProblem( IProblem.SCANNER_BAD_CHARACTER, new Character( (char)c ).toString(), getCurrentOffset(), false, true, throwExceptionOnBadCharacterRead ); |
| c = getChar(); |
| continue; |
| } |
| |
| throwEOF(); |
| } |
| } |
| |
| if (( getDepth() != 0) && !atEOF ) |
| { |
| atEOF = true; |
| handleProblem( IProblem.SCANNER_UNEXPECTED_EOF, null, getCurrentOffset(), false, true, true ); |
| } |
| |
| // we're done |
| throwEOF(); |
| return null; |
| } |
| |
| |
| |
| /** |
| * |
| */ |
| protected void handlePragmaOperator() throws ScannerException |
| { |
| // until we know what to do with pragmas, do the equivalent as |
| // to what we do for #pragma blah blah blah (ignore it) |
| getRestOfPreprocessorLine(); |
| } |
| |
| /** |
| * @param c |
| * @param wideLiteral |
| */ |
| protected IToken processCharacterLiteral(int c, boolean wideLiteral) |
| throws ScannerException |
| { |
| int beginOffset = getCurrentOffset(); |
| int type = wideLiteral ? IToken.tLCHAR : IToken.tCHAR; |
| |
| StringBuffer buffer = new StringBuffer(); |
| int prev = c; |
| int prevPrev = c; |
| c = getChar(true); |
| |
| for( ; ; ) |
| { |
| // error conditions |
| if( ( c == '\n' ) || |
| ( ( c == '\\' || c =='\'' )&& prev == '\\' ) || |
| ( c == NOCHAR ) ) |
| { |
| handleProblem( IProblem.SCANNER_BAD_CHARACTER, new Character( (char)c ).toString(),beginOffset, false, true, throwExceptionOnBadCharacterRead ); |
| c = ' '; |
| } |
| // exit condition |
| if ( ( c =='\'' ) && ( prev != '\\' || prevPrev == '\\' ) ) break; |
| |
| buffer.append( (char)c); |
| prevPrev = prev; |
| prev = c; |
| c = getChar(true); |
| } |
| |
| return newToken( type, buffer.toString(), contextStack.getCurrentContext()); |
| } |
| |
| |
| |
| protected String getCurrentFile() |
| { |
| return contextStack.getMostRelevantFileContext() != null ? contextStack.getMostRelevantFileContext().getFilename() : ""; |
| } |
| |
| |
| protected int getCurrentOffset() |
| { |
| return contextStack.getMostRelevantFileContext() != null ? contextStack.getMostRelevantFileContext().getOffset() : -1; |
| } |
| |
| |
| protected static class endOfMacroTokenException extends Exception {}; |
| // the static instance we always use |
| protected static endOfMacroTokenException endOfMacroToken = new endOfMacroTokenException(); |
| |
| public IToken nextTokenForStringizing() throws ScannerException, EndOfFileException, OffsetLimitReachedException |
| { |
| int beginOffset = getCurrentOffset(); |
| int c = getChar(); |
| StringBuffer tokenImage = new StringBuffer(); |
| |
| try { |
| while (c != NOCHAR) { |
| |
| if ((c == ' ') || (c == '\r') || (c == '\t') || (c == '\n')) { |
| |
| if (tokenImage.length() > 0) throw endOfMacroToken; |
| c = getChar(); |
| continue; |
| |
| } else if (c == '"') { |
| |
| if (tokenImage.length() > 0) throw endOfMacroToken; |
| |
| // string |
| StringBuffer buff = new StringBuffer(); |
| c = getChar(true); |
| |
| for( ; ; ) |
| { |
| if ( c =='"' ) break; |
| if( c == NOCHAR) break; |
| buff.append((char) c); |
| c = getChar(true); |
| } |
| |
| if (c != NOCHAR ) |
| { |
| return newToken( IToken.tSTRING, buff.toString(), contextStack.getCurrentContext()); |
| |
| } else { |
| handleProblem( IProblem.SCANNER_UNBOUNDED_STRING, null, beginOffset, false, true, true ); |
| c = getChar(); |
| continue; |
| } |
| |
| } else { |
| switch (c) { |
| case '\'' : |
| if (tokenImage.length() > 0) throw endOfMacroToken; |
| return processCharacterLiteral( c, false ); |
| case ',' : |
| if (tokenImage.length() > 0) throw endOfMacroToken; |
| return newToken(IToken.tCOMMA, ",", contextStack.getCurrentContext()); |
| case '(' : |
| if (tokenImage.length() > 0) throw endOfMacroToken; |
| return newToken(IToken.tLPAREN, "(", contextStack.getCurrentContext()); |
| case ')' : |
| if (tokenImage.length() > 0) throw endOfMacroToken; |
| return newToken(IToken.tRPAREN, ")", contextStack.getCurrentContext()); |
| case '/' : |
| if (tokenImage.length() > 0) throw endOfMacroToken; |
| c = getChar(); |
| switch (c) { |
| case '/' : |
| skipOverSinglelineComment(); |
| c = getChar(); |
| continue; |
| case '*' : |
| skipOverMultilineComment(); |
| c = getChar(); |
| continue; |
| default: |
| tokenImage.append('/'); |
| continue; |
| } |
| default : |
| tokenImage.append((char)c); |
| c = getChar(); |
| } |
| } |
| } |
| } catch (endOfMacroTokenException e) { |
| // unget the first character after the end of token |
| ungetChar(c); |
| } |
| |
| // return completed token |
| if (tokenImage.length() > 0) { |
| return newToken(IToken.tIDENTIFIER, tokenImage.toString(), contextStack.getCurrentContext()); |
| } |
| |
| // we're done |
| throwEOF(); |
| return null; |
| } |
| |
| |
| /** |
| * |
| */ |
| protected void throwEOF() throws EndOfFileException, OffsetLimitReachedException { |
| if( offsetLimit == NO_OFFSET_LIMIT ) |
| throw new EndOfFileException(); |
| |
| if( finalToken.getEndOffset() == offsetLimit ) |
| throw new OffsetLimitReachedException(finalToken); |
| throw new OffsetLimitReachedException( null ); |
| } |
| |
| |
| static { |
| cppKeywords.put( Keywords.AND, new Integer(IToken.t_and)); |
| cppKeywords.put( Keywords.AND_EQ, new Integer(IToken.t_and_eq)); |
| cppKeywords.put( Keywords.ASM, new Integer(IToken.t_asm)); |
| cppKeywords.put( Keywords.AUTO, new Integer(IToken.t_auto)); |
| cppKeywords.put( Keywords.BITAND, new Integer(IToken.t_bitand)); |
| cppKeywords.put( Keywords.BITOR, new Integer(IToken.t_bitor)); |
| cppKeywords.put( Keywords.BOOL, new Integer(IToken.t_bool)); |
| cppKeywords.put( Keywords.BREAK, new Integer(IToken.t_break)); |
| cppKeywords.put( Keywords.CASE, new Integer(IToken.t_case)); |
| cppKeywords.put( Keywords.CATCH, new Integer(IToken.t_catch)); |
| cppKeywords.put( Keywords.CHAR, new Integer(IToken.t_char)); |
| cppKeywords.put( Keywords.CLASS, new Integer(IToken.t_class)); |
| cppKeywords.put( Keywords.COMPL, new Integer(IToken.t_compl)); |
| cppKeywords.put( Keywords.CONST, new Integer(IToken.t_const)); |
| cppKeywords.put( Keywords.CONST_CAST, new Integer(IToken.t_const_cast)); |
| cppKeywords.put( Keywords.CONTINUE, new Integer(IToken.t_continue)); |
| cppKeywords.put( Keywords.DEFAULT, new Integer(IToken.t_default)); |
| cppKeywords.put( Keywords.DELETE, new Integer(IToken.t_delete)); |
| cppKeywords.put( Keywords.DO, new Integer(IToken.t_do)); |
| cppKeywords.put( Keywords.DOUBLE, new Integer(IToken.t_double)); |
| cppKeywords.put( Keywords.DYNAMIC_CAST, new Integer(IToken.t_dynamic_cast)); |
| cppKeywords.put( Keywords.ELSE, new Integer(IToken.t_else)); |
| cppKeywords.put( Keywords.ENUM, new Integer(IToken.t_enum)); |
| cppKeywords.put( Keywords.EXPLICIT, new Integer(IToken.t_explicit)); |
| cppKeywords.put( Keywords.EXPORT, new Integer(IToken.t_export)); |
| cppKeywords.put( Keywords.EXTERN, new Integer(IToken.t_extern)); |
| cppKeywords.put( Keywords.FALSE, new Integer(IToken.t_false)); |
| cppKeywords.put( Keywords.FLOAT, new Integer(IToken.t_float)); |
| cppKeywords.put( Keywords.FOR, new Integer(IToken.t_for)); |
| cppKeywords.put( Keywords.FRIEND, new Integer(IToken.t_friend)); |
| cppKeywords.put( Keywords.GOTO, new Integer(IToken.t_goto)); |
| cppKeywords.put( Keywords.IF, new Integer(IToken.t_if)); |
| cppKeywords.put( Keywords.INLINE, new Integer(IToken.t_inline)); |
| cppKeywords.put( Keywords.INT, new Integer(IToken.t_int)); |
| cppKeywords.put( Keywords.LONG, new Integer(IToken.t_long)); |
| cppKeywords.put( Keywords.MUTABLE, new Integer(IToken.t_mutable)); |
| cppKeywords.put( Keywords.NAMESPACE, new Integer(IToken.t_namespace)); |
| cppKeywords.put( Keywords.NEW, new Integer(IToken.t_new)); |
| cppKeywords.put( Keywords.NOT, new Integer(IToken.t_not)); |
| cppKeywords.put( Keywords.NOT_EQ, new Integer(IToken.t_not_eq)); |
| cppKeywords.put( Keywords.OPERATOR, new Integer(IToken.t_operator)); |
| cppKeywords.put( Keywords.OR, new Integer(IToken.t_or)); |
| cppKeywords.put( Keywords.OR_EQ, new Integer(IToken.t_or_eq)); |
| cppKeywords.put( Keywords.PRIVATE, new Integer(IToken.t_private)); |
| cppKeywords.put( Keywords.PROTECTED, new Integer(IToken.t_protected)); |
| cppKeywords.put( Keywords.PUBLIC, new Integer(IToken.t_public)); |
| cppKeywords.put( Keywords.REGISTER, new Integer(IToken.t_register)); |
| cppKeywords.put( Keywords.REINTERPRET_CAST, new Integer(IToken.t_reinterpret_cast)); |
| cppKeywords.put( Keywords.RETURN, new Integer(IToken.t_return)); |
| cppKeywords.put( Keywords.SHORT, new Integer(IToken.t_short)); |
| cppKeywords.put( Keywords.SIGNED, new Integer(IToken.t_signed)); |
| cppKeywords.put( Keywords.SIZEOF, new Integer(IToken.t_sizeof)); |
| cppKeywords.put( Keywords.STATIC, new Integer(IToken.t_static)); |
| cppKeywords.put( Keywords.STATIC_CAST, new Integer(IToken.t_static_cast)); |
| cppKeywords.put( Keywords.STRUCT, new Integer(IToken.t_struct)); |
| cppKeywords.put( Keywords.SWITCH, new Integer(IToken.t_switch)); |
| cppKeywords.put( Keywords.TEMPLATE, new Integer(IToken.t_template)); |
| cppKeywords.put( Keywords.THIS, new Integer(IToken.t_this)); |
| cppKeywords.put( Keywords.THROW, new Integer(IToken.t_throw)); |
| cppKeywords.put( Keywords.TRUE, new Integer(IToken.t_true)); |
| cppKeywords.put( Keywords.TRY, new Integer(IToken.t_try)); |
| cppKeywords.put( Keywords.TYPEDEF, new Integer(IToken.t_typedef)); |
| cppKeywords.put( Keywords.TYPEID, new Integer(IToken.t_typeid)); |
| cppKeywords.put( Keywords.TYPENAME, new Integer(IToken.t_typename)); |
| cppKeywords.put( Keywords.UNION, new Integer(IToken.t_union)); |
| cppKeywords.put( Keywords.UNSIGNED, new Integer(IToken.t_unsigned)); |
| cppKeywords.put( Keywords.USING, new Integer(IToken.t_using)); |
| cppKeywords.put( Keywords.VIRTUAL, new Integer(IToken.t_virtual)); |
| cppKeywords.put( Keywords.VOID, new Integer(IToken.t_void)); |
| cppKeywords.put( Keywords.VOLATILE, new Integer(IToken.t_volatile)); |
| cppKeywords.put( Keywords.WCHAR_T, new Integer(IToken.t_wchar_t)); |
| cppKeywords.put( Keywords.WHILE, new Integer(IToken.t_while)); |
| cppKeywords.put( Keywords.XOR, new Integer(IToken.t_xor)); |
| cppKeywords.put( Keywords.XOR_EQ, new Integer(IToken.t_xor_eq)); |
| |
| ppDirectives.put("#define", new Integer(PreprocessorDirectives.DEFINE)); |
| ppDirectives.put("#undef",new Integer(PreprocessorDirectives.UNDEFINE)); |
| ppDirectives.put("#if", new Integer(PreprocessorDirectives.IF)); |
| ppDirectives.put("#ifdef", new Integer(PreprocessorDirectives.IFDEF)); |
| ppDirectives.put("#ifndef", new Integer(PreprocessorDirectives.IFNDEF)); |
| ppDirectives.put("#else", new Integer(PreprocessorDirectives.ELSE)); |
| ppDirectives.put("#endif", new Integer(PreprocessorDirectives.ENDIF)); |
| ppDirectives.put( |
| "#include", |
| new Integer(PreprocessorDirectives.INCLUDE)); |
| ppDirectives.put("#line", new Integer(PreprocessorDirectives.LINE)); |
| ppDirectives.put("#error", new Integer(PreprocessorDirectives.ERROR)); |
| ppDirectives.put("#pragma", new Integer(PreprocessorDirectives.PRAGMA)); |
| ppDirectives.put("#elif", new Integer(PreprocessorDirectives.ELIF)); |
| ppDirectives.put("#", new Integer(PreprocessorDirectives.BLANK)); |
| |
| cKeywords.put( Keywords.AUTO, new Integer(IToken.t_auto)); |
| cKeywords.put( Keywords.BREAK, new Integer(IToken.t_break)); |
| cKeywords.put( Keywords.CASE, new Integer(IToken.t_case)); |
| cKeywords.put( Keywords.CHAR, new Integer(IToken.t_char)); |
| cKeywords.put( Keywords.CONST, new Integer(IToken.t_const)); |
| cKeywords.put( Keywords.CONTINUE, new Integer(IToken.t_continue)); |
| cKeywords.put( Keywords.DEFAULT, new Integer(IToken.t_default)); |
| cKeywords.put( Keywords.DELETE, new Integer(IToken.t_delete)); |
| cKeywords.put( Keywords.DO, new Integer(IToken.t_do)); |
| cKeywords.put( Keywords.DOUBLE, new Integer(IToken.t_double)); |
| cKeywords.put( Keywords.ELSE, new Integer(IToken.t_else)); |
| cKeywords.put( Keywords.ENUM, new Integer(IToken.t_enum)); |
| cKeywords.put( Keywords.EXTERN, new Integer(IToken.t_extern)); |
| cKeywords.put( Keywords.FLOAT, new Integer(IToken.t_float)); |
| cKeywords.put( Keywords.FOR, new Integer(IToken.t_for)); |
| cKeywords.put( Keywords.GOTO, new Integer(IToken.t_goto)); |
| cKeywords.put( Keywords.IF, new Integer(IToken.t_if)); |
| cKeywords.put( Keywords.INLINE, new Integer(IToken.t_inline)); |
| cKeywords.put( Keywords.INT, new Integer(IToken.t_int)); |
| cKeywords.put( Keywords.LONG, new Integer(IToken.t_long)); |
| cKeywords.put( Keywords.REGISTER, new Integer(IToken.t_register)); |
| cKeywords.put( Keywords.RESTRICT, new Integer(IToken.t_restrict)); |
| cKeywords.put( Keywords.RETURN, new Integer(IToken.t_return)); |
| cKeywords.put( Keywords.SHORT, new Integer(IToken.t_short)); |
| cKeywords.put( Keywords.SIGNED, new Integer(IToken.t_signed)); |
| cKeywords.put( Keywords.SIZEOF, new Integer(IToken.t_sizeof)); |
| cKeywords.put( Keywords.STATIC, new Integer(IToken.t_static)); |
| cKeywords.put( Keywords.STRUCT, new Integer(IToken.t_struct)); |
| cKeywords.put( Keywords.SWITCH, new Integer(IToken.t_switch)); |
| cKeywords.put( Keywords.TYPEDEF, new Integer(IToken.t_typedef)); |
| cKeywords.put( Keywords.UNION, new Integer(IToken.t_union)); |
| cKeywords.put( Keywords.UNSIGNED, new Integer(IToken.t_unsigned)); |
| cKeywords.put( Keywords.VOID, new Integer(IToken.t_void)); |
| cKeywords.put( Keywords.VOLATILE, new Integer(IToken.t_volatile)); |
| cKeywords.put( Keywords.WHILE, new Integer(IToken.t_while)); |
| cKeywords.put( Keywords._BOOL, new Integer(IToken.t__Bool)); |
| cKeywords.put( Keywords._COMPLEX, new Integer(IToken.t__Complex)); |
| cKeywords.put( Keywords._IMAGINARY, new Integer(IToken.t__Imaginary)); |
| |
| } |
| |
| static public class PreprocessorDirectives { |
| static public final int DEFINE = 0; |
| static public final int UNDEFINE = 1; |
| static public final int IF = 2; |
| static public final int IFDEF = 3; |
| static public final int IFNDEF = 4; |
| static public final int ELSE = 5; |
| static public final int ENDIF = 6; |
| static public final int INCLUDE = 7; |
| static public final int LINE = 8; |
| static public final int ERROR = 9; |
| static public final int PRAGMA = 10; |
| static public final int BLANK = 11; |
| static public final int ELIF = 12; |
| } |
| |
| public final int getCount() { |
| return count; |
| } |
| |
| public final int getDepth() { |
| return branches.getDepth(); |
| } |
| |
| protected boolean evaluateExpression(String expression, int beginningOffset ) |
| throws ScannerException { |
| |
| if( mode == ParserMode.QUICK_PARSE ) |
| { |
| if( expression.trim().equals( "0" ) ) |
| return false; |
| return true; |
| } |
| else |
| { |
| final NullSourceElementRequestor nullCallback = new NullSourceElementRequestor(); |
| IParser parser = null; |
| try |
| { |
| IScanner trial = |
| ParserFactory.createScanner( |
| new StringReader(expression + ";"), |
| EXPRESSION, |
| new ScannerInfo( definitions, originalConfig.getIncludePaths()), |
| ParserMode.QUICK_PARSE, language, nullCallback, log ); |
| parser = ParserFactory.createParser(trial, nullCallback, ParserMode.QUICK_PARSE, language, log ); |
| } catch( ParserFactoryException pfe ) |
| { |
| handleInternalError(); |
| } |
| try { |
| IASTExpression exp = parser.expression(null); |
| if( exp.evaluateExpression() == 0 ) |
| return false; |
| return true; |
| } catch( BacktrackException backtrack ) |
| { |
| handleProblem( IProblem.PREPROCESSOR_CONDITIONAL_EVAL_ERROR, expression, beginningOffset, false, true, true ); |
| } |
| catch (ExpressionEvaluationException e) { |
| handleProblem( IProblem.PREPROCESSOR_CONDITIONAL_EVAL_ERROR, expression, beginningOffset, false, true, true ); |
| } catch (EndOfFileException e) { |
| handleProblem( IProblem.PREPROCESSOR_CONDITIONAL_EVAL_ERROR, expression, beginningOffset, false, true, true ); |
| } |
| return true; |
| } |
| } |
| |
| |
| protected void skipOverSinglelineComment() throws ScannerException { |
| |
| StringBuffer comment = new StringBuffer("//"); |
| int c; |
| |
| loop: |
| for (;;) { |
| c = getChar(); |
| comment.append((char)c); |
| switch (c) { |
| case NOCHAR : |
| case '\n' : |
| break loop; |
| default : |
| break; |
| } |
| } |
| |
| } |
| |
| protected boolean skipOverMultilineComment() throws ScannerException { |
| int state = 0; |
| boolean encounteredNewline = false; |
| StringBuffer comment = new StringBuffer("/*"); |
| // simple state machine to handle multi-line comments |
| // state 0 == no end of comment in site |
| // state 1 == encountered *, expecting / |
| // state 2 == we are no longer in a comment |
| |
| int c = getChar(); |
| comment.append((char)c); |
| while (state != 2 && c != NOCHAR) { |
| if (c == '\n') |
| encounteredNewline = true; |
| |
| switch (state) { |
| case 0 : |
| if (c == '*') |
| state = 1; |
| break; |
| case 1 : |
| if (c == '/') |
| state = 2; |
| else if (c != '*') |
| state = 0; |
| break; |
| } |
| c = getChar(); |
| comment.append((char)c); |
| } |
| |
| if (c == NOCHAR) |
| handleProblem( IProblem.SCANNER_UNEXPECTED_EOF, null, getCurrentOffset(), false, true, true ); |
| |
| ungetChar(c); |
| |
| return encounteredNewline; |
| } |
| |
| protected void poundInclude( int beginningOffset ) throws ScannerException { |
| |
| skipOverWhitespace(); |
| int baseOffset = lastContext.getOffset() - lastContext.undoStackSize(); |
| String includeLine = getRestOfPreprocessorLine(); |
| StringBuffer fileName = new StringBuffer(); |
| boolean useIncludePath = true; |
| int startOffset = baseOffset; |
| int endOffset = baseOffset; |
| |
| if (! includeLine.equals("")) { |
| Scanner helperScanner = new Scanner( |
| new StringReader(includeLine), |
| null, |
| new ScannerInfo(definitions, originalConfig.getIncludePaths()), |
| new NullSourceElementRequestor(), |
| mode, |
| language, log ); |
| helperScanner.setForInclusion( true ); |
| IToken t = null; |
| |
| try { |
| t = helperScanner.nextToken(false); |
| } catch (OffsetLimitReachedException e) { |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#include " + includeLine, beginningOffset, false, true, true ); |
| return; |
| } catch (EndOfFileException eof) { |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#include " + includeLine, beginningOffset, false, true, true ); |
| return; |
| } |
| |
| try { |
| if (t.getType() == IToken.tSTRING) { |
| fileName.append(t.getImage()); |
| startOffset = baseOffset + t.getOffset(); |
| endOffset = baseOffset + t.getEndOffset(); |
| useIncludePath = false; |
| |
| // This should throw EOF |
| t = helperScanner.nextToken(false); |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#include " + includeLine, beginningOffset, false, true, true ); |
| return; |
| } else if (t.getType() == IToken.tLT) { |
| |
| try { |
| |
| t = helperScanner.nextToken(false); |
| startOffset = baseOffset + t.getOffset(); |
| |
| while (t.getType() != IToken.tGT) { |
| fileName.append(t.getImage()); |
| helperScanner.skipOverWhitespace(); |
| int c = helperScanner.getChar(); |
| if (c == '\\') fileName.append('\\'); else helperScanner.ungetChar(c); |
| t = helperScanner.nextToken(false); |
| } |
| |
| endOffset = baseOffset + t.getEndOffset(); |
| |
| } catch (EndOfFileException eof) { |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#include " + includeLine, beginningOffset, false, true, true ); |
| return; |
| } |
| |
| // This should throw EOF |
| t = helperScanner.nextToken(false); |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#include " + includeLine, beginningOffset, false, true, true ); |
| |
| return; |
| |
| } else |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#include " + includeLine, beginningOffset, false, true, true ); |
| } |
| catch (OffsetLimitReachedException e) { |
| handleInternalError(); |
| } |
| catch( EndOfFileException eof ) |
| { |
| // good |
| } |
| |
| } else |
| handleProblem( IProblem.PREPROCESSOR_INVALID_DIRECTIVE, "#include " + includeLine, beginningOffset, false, true, true ); |
| |
| String f = fileName.toString(); |
| |
| if( mode == ParserMode.QUICK_PARSE ) |
| { |
| if( requestor != null ) |
| { |
| IASTInclusion i = null; |
| try |
| { |
| i = |
| astFactory.createInclusion( |
| f, |
| "", |
| !useIncludePath, |
| beginningOffset, |
| startOffset, |
| startOffset + f.length(), |
| endOffset); |
| } |
| catch (Exception e) |
| { |
| /* do nothing */ |
| } |
| if( i != null ) |
| { |
| i.enterScope( requestor ); |
| i.exitScope( requestor ); |
| } |
| } |
| } |
| else |
| handleInclusion(f.trim(), useIncludePath, startOffset, beginningOffset, endOffset); |
| } |
| |
| protected static final Hashtable emptyMap = new Hashtable(); |
| protected Hashtable holderMap = null; |
| |
| protected void temporarilyReplaceDefinitionsMap() |
| { |
| holderMap = definitions; |
| definitions = emptyMap; |
| } |
| |
| protected void restoreDefinitionsMap() |
| { |
| definitions = holderMap; |
| holderMap = null; |
| } |
| |
| |
| protected boolean forInclusion = false; |
| /** |
| * @param b |
| */ |
| protected void setForInclusion(boolean b) |
| { |
| forInclusion = b; |
| } |
| |
| protected void poundDefine(int beginning) throws ScannerException, EndOfFileException { |
| skipOverWhitespace(); |
| // definition |
| String key = getNextIdentifier(); |
| int offset = contextStack.getCurrentContext().getOffset() - key.length() - contextStack.getCurrentContext().undoStackSize(); |
| |
| // store the previous definition to check against later |
| Object previousDefinition = definitions.get( key ); |
| |
| // get the next character |
| // the C++ standard says that macros must not put |
| // whitespace between the end of the definition |
| // identifier and the opening parenthesis |
| int c = getChar(); |
| if (c == '(') { |
| StringBuffer buffer = new StringBuffer(); |
| c = getChar(true); |
| while (c != ')') { |
| if( c == '\\' ){ |
| c = getChar(); |
| if( c == '\r' ) |
| c = getChar(); |
| |
| if( c == '\n' ){ |
| c = getChar(); |
| continue; |
| } else { |
| ungetChar( c ); |
| handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, "#define " + buffer.toString() + '\\' + (char)c, beginning, false, true, true ); |
| return; |
| } |
| } else if( c == '\r' || c == '\n' ){ |
| handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, "#define " + buffer.toString() + (char)c, beginning, false, true, true ); |
| return; |
| } else if( c == NOCHAR ){ |
| handleProblem( IProblem.SCANNER_UNEXPECTED_EOF, null, beginning, false, true, true ); |
| return; |
| } |
| |
| buffer.append((char) c); |
| c = getChar(true); |
| } |
| |
| String parameters = buffer.toString(); |
| |
| // replace StringTokenizer later -- not performant |
| StringTokenizer tokenizer = new StringTokenizer(parameters, ","); |
| ArrayList parameterIdentifiers = |
| new ArrayList(tokenizer.countTokens()); |
| while (tokenizer.hasMoreTokens()) { |
| parameterIdentifiers.add(tokenizer.nextToken().trim()); |
| } |
| |
| skipOverWhitespace(); |
| |
| ArrayList macroReplacementTokens = new ArrayList(); |
| String replacementString = getRestOfPreprocessorLine(); |
| |
| if( ! replacementString.equals( "" ) ) |
| { |
| IScanner helperScanner=null; |
| try { |
| helperScanner = |
| ParserFactory.createScanner( |
| new StringReader(replacementString), |
| SCRATCH, |
| new ScannerInfo(), |
| mode, |
| language, |
| new NullSourceElementRequestor(), log); |
| } catch (ParserFactoryException e1) { |
| } |
| helperScanner.setTokenizingMacroReplacementList( true ); |
| IToken t = null; |
| try { |
| t = helperScanner.nextToken(false); |
| } catch (OffsetLimitReachedException e2) { |
| handleInternalError(); |
| } |
| try { |
| while (true) { |
| //each # preprocessing token in the replacement list shall be followed |
| //by a parameter as the next reprocessing token in the list |
| if( t.getType() == tPOUND ){ |
| macroReplacementTokens.add( t ); |
| t = helperScanner.nextToken(false); |
| int index = parameterIdentifiers.indexOf(t.getImage()); |
| if (index == -1 ) { |
| //not found |
| |
| handleProblem( IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, "#define " + key + " " + replacementString, |
| beginning, false, true, true ); |
| return; |
| } |
| } |
| |
| macroReplacementTokens.add(t); |
| t = helperScanner.nextToken(false); |
| } |
| } |
| catch( OffsetLimitReachedException olre ) |
| { |
| handleInternalError(); |
| } |
| catch( EndOfFileException eof ) |
| { |
| // good |
| } |
| } |
| |
| IMacroDescriptor descriptor = new MacroDescriptor(); |
| descriptor.initialize( |
| key, |
| parameterIdentifiers, |
| macroReplacementTokens, |
| key + "(" + parameters + ")"); |
| |
| checkValidMacroRedefinition(key, previousDefinition, descriptor, beginning); |
| addDefinition(key, descriptor); |
| |
| } |
| else if ((c == '\n') || (c == '\r')) |
| { |
| checkValidMacroRedefinition(key, previousDefinition, "", beginning); |
| addDefinition( key, "" ); |
| } |
| else if ((c == ' ') || (c == '\t') ) { |
| // this is a simple definition |
| skipOverWhitespace(); |
| |
| // get what we are to map the name to and add it to the definitions list |
| String value = getRestOfPreprocessorLine(); |
| |
| checkValidMacroRedefinition(key, previousDefinition, value, beginning); |
| addDefinition( key, value ); |
| |
| } else if (c == '/') { |
| // this could be a comment |
| c = getChar(); |
| if (c == '/') // one line comment |
| { |
| skipOverSinglelineComment(); |
| checkValidMacroRedefinition(key, previousDefinition, "", beginning); |
| addDefinition(key, ""); |
| } else if (c == '*') // multi-line comment |
| { |
| if (skipOverMultilineComment()) { |
| // we have gone over a newline |
| // therefore, this symbol was defined to an empty string |
| checkValidMacroRedefinition(key, previousDefinition, "", beginning); |
| addDefinition(key, ""); |
| } else { |
| String value = getRestOfPreprocessorLine(); |
| |
| checkValidMacroRedefinition(key, previousDefinition, "", beginning); |
| addDefinition(key, value); |
| } |
| } else { |
| // this is not a comment |
| // it is a bad statement |
| handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, "#define " + key + " /" + getRestOfPreprocessorLine(), beginning, false, true, true ); |
| return; |
| } |
| } else { |
| log.traceLog("Scanner : Encountered unexpected character " + ((char) c)); |
| handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, "#define " + key + (char)c + getRestOfPreprocessorLine(), beginning, false, true, true ); |
| return; |
| } |
| |
| try |
| { |
| astFactory.createMacro( key, beginning, offset, offset + key.length(), contextStack.getCurrentContext().getOffset() ).acceptElement( requestor ); |
| } |
| catch (Exception e) |
| { |
| /* do nothing */ |
| } |
| } |
| |
| |
| |
| protected void checkValidMacroRedefinition( |
| String key, |
| Object previousDefinition, |
| Object newDefinition, int beginningOffset ) |
| throws ScannerException |
| { |
| if( mode != ParserMode.QUICK_PARSE && previousDefinition != null ) |
| { |
| if( newDefinition instanceof IMacroDescriptor ) |
| { |
| if( previousDefinition instanceof IMacroDescriptor ) |
| { |
| if( ((IMacroDescriptor)previousDefinition).compatible( (IMacroDescriptor) newDefinition ) ) |
| return; |
| } |
| } |
| else if( newDefinition instanceof String ) |
| { |
| if( previousDefinition instanceof String ) |
| { |
| Scanner previous = new Scanner( new StringReader( (String)previousDefinition ), "redef-test", new ScannerInfo(), new NullSourceElementRequestor(), |
| mode, language, log ); |
| Scanner current = new Scanner( new StringReader( (String)newDefinition ), "redef-test", new ScannerInfo(), new NullSourceElementRequestor(), |
| mode, language, log ); |
| for ( ; ; ) |
| { |
| IToken p = null; |
| IToken c = null; |
| try |
| { |
| p = previous.nextToken(); |
| c = current.nextToken(); |
| |
| if ( c.equals( p ) ) continue; |
| break; |
| |
| } |
| catch( OffsetLimitReachedException olre ) |
| { |
| handleInternalError(); |
| } |
| catch( EndOfFileException eof ) |
| { |
| if( ( p != null ) && ( c == null ) ) |
| break; |
| if( p == null ) |
| { |
| try |
| { |
| c = current.nextToken(); |
| break; |
| } |
| catch( OffsetLimitReachedException olre ) |
| { |
| handleInternalError(); |
| } |
| catch( EndOfFileException eof2 ) |
| { |
| return; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_REDEFN, key, beginningOffset, false, true, true ); |
| } |
| } |
| |
| /** |
| * |
| */ |
| protected void handleInternalError() { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| protected Vector getMacroParameters (String params, boolean forStringizing) throws ScannerException { |
| |
| Scanner tokenizer = new Scanner(new StringReader(params), TEXT, new ScannerInfo( definitions, originalConfig.getIncludePaths() ), new NullSourceElementRequestor(), mode, language, log); |
| tokenizer.setThrowExceptionOnBadCharacterRead(false); |
| Vector parameterValues = new Vector(); |
| Token t = null; |
| String str = new String(); |
| boolean space = false; |
| int nParen = 0; |
| |
| try { |
| while (true) { |
| int c = tokenizer.getCharacter(); |
| if ((c != ' ') && (c != '\t') && (c != '\r') && (c != '\n')) { |
| space = false; |
| } |
| if (c != NOCHAR) tokenizer.ungetChar(c); |
| |
| t = (Token)(forStringizing ? tokenizer.nextTokenForStringizing() : tokenizer.nextToken(false)); |
| if (t.type == IToken.tLPAREN) { |
| nParen++; |
| } else if (t.type == IToken.tRPAREN) { |
| nParen--; |
| } else if (t.type == IToken.tCOMMA && nParen == 0) { |
| parameterValues.add(str); |
| str = ""; |
| space = false; |
| continue; |
| } |
| |
| if (space) |
| str += ' '; |
| |
| switch (t.type) { |
| case IToken.tSTRING : str += '\"' + t.image + '\"'; break; |
| case IToken.tLSTRING : str += "L\"" + t.image + '\"'; break; |
| case IToken.tCHAR : str += '\'' + t.image + '\''; break; |
| default : str += t.image; break; |
| } |
| space = true; |
| } |
| } |
| catch( OffsetLimitReachedException olre ) |
| { |
| handleInternalError(); |
| } |
| catch (EndOfFileException e) { |
| // Good |
| parameterValues.add(str); |
| } |
| |
| |
| return parameterValues; |
| } |
| |
| protected void expandDefinition(String symbol, Object expansion, int symbolOffset) |
| throws ScannerException |
| { |
| // All the tokens generated by the macro expansion |
| // will have dimensions (offset and length) equal to the expanding symbol. |
| if (expansion instanceof String ) { |
| String replacementValue = (String) expansion; |
| try |
| { |
| contextStack.updateContext( new StringReader(replacementValue), (POUND_DEFINE + symbol ), ScannerContext.ContextKind.MACROEXPANSION, null, requestor, symbolOffset, symbol.length()); |
| } |
| catch (ContextException e) |
| { |
| handleProblem( e.getId(), contextStack.getCurrentContext().getFilename(), getCurrentOffset(), false, true, true ); |
| consumeUntilOutOfMacroExpansion(); |
| return; |
| } |
| } else if (expansion instanceof IMacroDescriptor ) { |
| IMacroDescriptor macro = (IMacroDescriptor) expansion; |
| skipOverWhitespace(); |
| int c = getChar(); |
| |
| if (c == '(') { |
| StringBuffer buffer = new StringBuffer(); |
| int bracketCount = 1; |
| c = getChar(); |
| |
| while (true) { |
| if (c == '(') |
| ++bracketCount; |
| else if (c == ')') |
| --bracketCount; |
| |
| if(bracketCount == 0 || c == NOCHAR) |
| break; |
| buffer.append((char) c); |
| c = getChar( true ); |
| } |
| |
| // Position of the closing ')' |
| int endMacroOffset = lastContext.getOffset() - lastContext.undoStackSize() - 1; |
| |
| String betweenTheBrackets = buffer.toString().trim(); |
| |
| Vector parameterValues = getMacroParameters(betweenTheBrackets, false); |
| Vector parameterValuesForStringizing = getMacroParameters(betweenTheBrackets, true); |
| Token t = null; |
| |
| // create a string that represents what needs to be tokenized |
| buffer = new StringBuffer(); |
| List tokens = macro.getTokenizedExpansion(); |
| List parameterNames = macro.getParameters(); |
| |
| if (parameterNames.size() != parameterValues.size()) |
| { |
| handleProblem( IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, symbol, getCurrentOffset(), false, true, true ); |
| consumeUntilOutOfMacroExpansion(); |
| return; |
| } |
| |
| int numberOfTokens = tokens.size(); |
| |
| for (int i = 0; i < numberOfTokens; ++i) { |
| t = (Token) tokens.get(i); |
| if (t.type == IToken.tIDENTIFIER) { |
| |
| // is this identifier in the parameterNames |
| // list? |
| int index = parameterNames.indexOf(t.image); |
| if (index == -1 ) { |
| // not found |
| // just add image to buffer |
| buffer.append(t.image ); |
| } else { |
| buffer.append( |
| (String) parameterValues.elementAt(index) ); |
| } |
| } else if (t.type == tPOUND) { |
| //next token should be a parameter which needs to be turned into |
| //a string literal |
| t = (Token) tokens.get( ++i ); |
| int index = parameterNames.indexOf(t.image); |
| if( index == -1 ){ |
| handleProblem( IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, macro.getName(), getCurrentOffset(), false, true, true ); |
| return; |
| } else { |
| buffer.append('\"'); |
| String value = (String)parameterValuesForStringizing.elementAt(index); |
| char val [] = value.toCharArray(); |
| char ch; |
| int length = value.length(); |
| for( int j = 0; j < length; j++ ){ |
| ch = val[j]; |
| if( ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' ){ |
| //Each occurance of whitespace becomes a single space character |
| while( ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' ){ |
| ch = val[++j]; |
| } |
| buffer.append(' '); |
| } |
| //a \ character is inserted before each " and \ |
| if( ch == '\"' || ch == '\\' ){ |
| buffer.append('\\'); |
| buffer.append(ch); |
| } else { |
| buffer.append(ch); |
| } |
| } |
| buffer.append('\"'); |
| } |
| } else { |
| switch( t.type ) |
| { |
| case IToken.tSTRING: buffer.append('\"' + t.image + '\"'); break; |
| case IToken.tLSTRING: buffer.append("L\"" + t.image + '\"'); break; |
| case IToken.tCHAR: buffer.append('\'' + t.image + '\''); break; |
| default: buffer.append(t.image); break; |
| } |
| } |
| |
| boolean pastingNext = false; |
| |
| if( i != numberOfTokens - 1) |
| { |
| IToken t2 = (IToken) tokens.get(i+1); |
| if( t2.getType() == tPOUNDPOUND ) { |
| pastingNext = true; |
| i++; |
| } |
| } |
| |
| if( t.getType() != tPOUNDPOUND && ! pastingNext ) |
| if (i < (numberOfTokens-1)) // Do not append to the last one |
| buffer.append( " " ); |
| } |
| String finalString = buffer.toString(); |
| try |
| { |
| contextStack.updateContext( |
| new StringReader(finalString), |
| POUND_DEFINE + macro.getSignature(), ScannerContext.ContextKind.MACROEXPANSION, null, requestor, symbolOffset, endMacroOffset - symbolOffset + 1 ); |
| } |
| catch (ContextException e) |
| { |
| handleProblem( e.getId(), contextStack.getCurrentContext().getFilename(), getCurrentOffset(), false, true, true ); |
| consumeUntilOutOfMacroExpansion(); |
| return; |
| } |
| } else |
| { |
| handleProblem( IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, symbol, getCurrentOffset(), false, true, true ); |
| consumeUntilOutOfMacroExpansion(); |
| return; |
| } |
| |
| } else { |
| log.traceLog( |
| "Unexpected class stored in definitions table. " |
| + expansion.getClass().getName() ); |
| } |
| |
| } |
| |
| protected String handleDefinedMacro() throws ScannerException { |
| int o = getCurrentOffset(); |
| skipOverWhitespace(); |
| |
| int c = getChar(); |
| |
| String definitionIdentifier = null; |
| if (c == '(') { |
| |
| definitionIdentifier = getNextIdentifier(); |
| skipOverWhitespace(); |
| c = getChar(); |
| if (c != ')') |
| { |
| handleProblem( IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, "defined()", o, false, true, true); |
| return "0"; |
| } |
| } |
| else |
| { |
| ungetChar(c); |
| definitionIdentifier = getNextIdentifier(); |
| } |
| |
| if (definitions.get(definitionIdentifier) != null) |
| return "1"; |
| |
| return "0"; |
| } |
| |
| |
| |
| private ParserLanguage language = ParserLanguage.CPP; |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.internal.core.parser.IScanner#setCppNature(boolean) |
| */ |
| public void setLanguage( ParserLanguage value) { |
| language = value; |
| } |
| |
| public void setThrowExceptionOnBadCharacterRead( boolean throwOnBad ){ |
| throwExceptionOnBadCharacterRead = throwOnBad; |
| } |
| |
| private final ISourceElementRequestor requestor; |
| private IASTFactory astFactory = null; |
| private static final int NO_OFFSET_LIMIT = -1; |
| private int offsetLimit = NO_OFFSET_LIMIT; |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.parser.IScanner#setASTFactory(org.eclipse.cdt.internal.core.parser.ast.IASTFactory) |
| */ |
| public void setASTFactory(IASTFactory f) { |
| astFactory = f; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.parser.IScanner#getLineNumberForOffset(int) |
| */ |
| public int getLineNumberForOffset(int i) |
| { |
| ILineOffsetReconciler reconciler = ParserFactory.createLineOffsetReconciler( backupReader ); |
| return reconciler.getLineNumberForOffset(i); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.parser.IScanner#setOffsetBoundary(int) |
| */ |
| public void setOffsetBoundary(int offset) { |
| offsetLimit = offset; |
| } |
| } |