blob: 571b76f954ebddad7ad72155c1ef068f9e6c669e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 Wind River Systems, Inc. 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:
* UIUC - Photran modifications
* Markus Schorn - initial API and implementation
*******************************************************************************/
/**
* Edited by Matthew Michelotti
*
* Overview of changes
* -may have changed imports
* -Updated constructor of TokenParameterReference to take in precedingWhiteSpace
* -Updated call to the TokenParameterReference constructor in parseExpansion
* to send in preceding white space
*/
package org.eclipse.photran.internal.core.preprocessor.c;
import java.util.ArrayList;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.photran.internal.core.preprocessor.c.Lexer.LexerOptions;
/**
* Utility to parse macro definitions and create the macro objects for the preprocessor.
* @since 5.0
*/
public class MacroDefinitionParser {
private static final int ORIGIN_PREPROCESSOR_DIRECTIVE = OffsetLimitReachedException.ORIGIN_PREPROCESSOR_DIRECTIVE;
/**
* Exception for reporting problems while parsing a macro definition.
*/
@SuppressWarnings("serial")
static class InvalidMacroDefinitionException extends Exception {
public char[] fName;
public int fStartOffset;
public int fEndOffset;
public InvalidMacroDefinitionException(char[] name, int startOffset, int endOffset) {
fName= name;
fStartOffset= startOffset;
fEndOffset= endOffset;
}
}
/**
* Token used for macro parameters found in the replacement list.
*/
static class TokenParameterReference extends TokenWithImage {
private final int fIndex;
public TokenParameterReference(int type, int idx, Object source, int offset, int endOffset,
char[] name, char[] precedingWhiteSpace)
{
super(type, source, offset, endOffset, name, precedingWhiteSpace); //edited by MM to include white-space
fIndex= idx;
}
public int getIndex() {
return fIndex;
}
@Override
public String toString() {
return "[" + fIndex + "]"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
public static char[] getExpansion(char[] expansionImage, int offset, int endOffset) {
TokenList tl= new TokenList();
Lexer lex= new Lexer(expansionImage, offset, endOffset, new LexerOptions(), ILexerLog.NULL, null);
try {
lex.nextToken(); // consume the start token
new MacroDefinitionParser().parseExpansion(lex, ILexerLog.NULL, null, new char[][]{}, tl);
} catch (OffsetLimitReachedException e) {
}
StringBuffer buf= new StringBuffer();
Token t= tl.first();
if (t == null) {
return CharArrayUtils.EMPTY;
}
endOffset= t.getOffset();
for (; t != null; t= (Token) t.getNext()) {
if (endOffset < t.getOffset()) {
buf.append(' ');
}
buf.append(t.getCharImage());
endOffset= t.getEndOffset();
}
final int length= buf.length();
final char[] expansion= new char[length];
buf.getChars(0, length, expansion, 0);
return expansion;
}
private int fHasVarArgs;
private int fExpansionOffset;
private int fExpansionEndOffset;
private Token fNameToken;
MacroDefinitionParser() {
}
/**
* In case the name was successfully parsed, the name token is returned.
* Otherwise the return value is undefined.
*/
public Token getNameToken() {
return fNameToken;
}
/**
* Parses an entire macro definition. Name must be the next token of the lexer.
*/
public ObjectStyleMacro parseMacroDefinition(final Lexer lexer, final ILexerLog log)
throws OffsetLimitReachedException, InvalidMacroDefinitionException {
final Token name = parseName(lexer);
final char[] source= lexer.getInput();
final char[] nameChars= name.getCharImage();
final char[][] paramList= parseParamList(lexer, name);
final TokenList replacement= new TokenList();
parseExpansion(lexer, log, nameChars, paramList, replacement);
if (paramList == null) {
return new ObjectStyleMacro(nameChars, fExpansionOffset, fExpansionEndOffset, replacement, source);
}
return new FunctionStyleMacro(nameChars, paramList, fHasVarArgs, fExpansionOffset, fExpansionEndOffset, replacement, source);
}
/**
* Parses a macro definition without the replacement. Name must be the next token of the lexer.
*/
public PreprocessorMacro parseMacroDefinition(final Lexer lexer, final ILexerLog log, final char[] replacement)
throws InvalidMacroDefinitionException, OffsetLimitReachedException {
final Token name = parseName(lexer);
final char[] nameChars = name.getCharImage();
final char[][] paramList= parseParamList(lexer, name);
final Token replacementToken = lexer.currentToken();
if (replacementToken.getType() != IToken.tEND_OF_INPUT) {
throw new InvalidMacroDefinitionException(nameChars, replacementToken.getOffset(), replacementToken.getEndOffset());
}
if (paramList == null) {
return new ObjectStyleMacro(nameChars, replacement);
}
return new FunctionStyleMacro(nameChars, paramList, fHasVarArgs, replacement);
}
/**
* Parses a macro definition basically checking for var-args.
*/
public static PreprocessorMacro parseMacroDefinition(final char[] name, char[][] paramList, final char[] replacement) {
int hasVarargs= 0;
if (paramList != null) {
final int length = paramList.length;
if (length > 0) {
char[] lastParam= paramList[length-1];
final int lpl = lastParam.length;
switch(lpl) {
case 0: case 1: case 2:
break;
case 3:
if (CharArrayUtils.equals(lastParam, Keywords.cpELLIPSIS)) {
hasVarargs= FunctionStyleMacro.VAARGS;
char[][] copy= new char[length][];
System.arraycopy(paramList, 0, copy, 0, length-1);
copy[length-1]= Keywords.cVA_ARGS;
paramList= copy;
}
break;
default:
if (CharArrayUtils.equals(lastParam, lpl-3, 3, Keywords.cpELLIPSIS)) {
hasVarargs= FunctionStyleMacro.NAMED_VAARGS;
char[][] copy= new char[length][];
System.arraycopy(paramList, 0, copy, 0, length-1);
copy[length-1]= CharArrayUtils.subarray(lastParam, 0, lpl-3);
paramList= copy;
}
break;
}
}
}
if (paramList == null) {
return new ObjectStyleMacro(name, replacement);
}
return new FunctionStyleMacro(name, paramList, hasVarargs, replacement);
}
private Token parseName(final Lexer lexer) throws OffsetLimitReachedException, InvalidMacroDefinitionException {
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);
}
throw new InvalidMacroDefinitionException(name.getCharImage(), name.getOffset(), name.getEndOffset());
}
fNameToken= name;
return name;
}
private char[][] parseParamList(Lexer lex, final Token name) throws OffsetLimitReachedException, InvalidMacroDefinitionException {
final Token lparen= lex.nextToken();
fHasVarArgs= FunctionStyleMacro.NO_VAARGS;
if (lparen.getType() != IToken.tLPAREN || name.getEndOffset() != lparen.getOffset()) {
return null;
}
ArrayList<char[]> paramList= new ArrayList<char[]>();
IToken next= null;
do {
final Token param= lex.nextToken();
switch (param.getType()) {
case IToken.tCOMPLETION:
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, param);
case IToken.tIDENTIFIER:
paramList.add(param.getCharImage());
next= lex.nextToken();
if (next.getType() == IToken.tELLIPSIS) {
fHasVarArgs= FunctionStyleMacro.NAMED_VAARGS;
next= lex.nextToken();
}
break;
case IToken.tELLIPSIS:
fHasVarArgs= FunctionStyleMacro.VAARGS;
paramList.add(Keywords.cVA_ARGS);
next= lex.nextToken();
break;
case IToken.tRPAREN:
if (next == null) {
next= param;
break;
}
throw new InvalidMacroDefinitionException(name.getCharImage(), name.getOffset(), param.getEndOffset());
default:
throw new InvalidMacroDefinitionException(name.getCharImage(), name.getOffset(), param.getEndOffset());
}
}
while (fHasVarArgs==0 && next.getType() == IToken.tCOMMA);
if (next.getType() != IToken.tRPAREN) {
throw new InvalidMacroDefinitionException(name.getCharImage(), name.getOffset(), next.getEndOffset());
}
next= lex.nextToken(); // consume the closing parenthesis
return paramList.toArray(new char[paramList.size()][]);
}
/**
* Parses the expansion for a macro, the current token must be the first token of the expansion
* @since 5.0
*/
public void parseExpansion(final Lexer lexer, final ILexerLog log, final char[] name, final char[][] paramList,
TokenList result) throws OffsetLimitReachedException {
boolean needParam= false;
boolean isFirst= true;
Token needAnotherToken= null;
Token candidate= lexer.currentToken();
fExpansionOffset= fExpansionEndOffset= candidate.getOffset();
loop: while(true) {
switch(candidate.getType()) {
case IToken.tCOMPLETION:
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, candidate);
case IToken.tEND_OF_INPUT:
case Lexer.tNEWLINE:
break loop;
case IToken.tIDENTIFIER:
if (paramList != null) {
// convert the parameters to special tokens
final char[] image = candidate.getCharImage();
int idx= CharArrayUtils.indexOf(image, paramList);
if (idx >= 0) {
//following line edited by MM to include white-space
candidate= new TokenParameterReference(CPreprocessor.tMACRO_PARAMETER, idx, lexer.getSource(), candidate.getOffset(), candidate.getEndOffset(), paramList[idx], candidate.getCharPrecedingWhiteSpace());
needParam= false;
}
else {
if (needParam) {
log.handleProblem(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, name, fExpansionOffset, candidate.getEndOffset());
}
else if (CharArrayUtils.equals(Keywords.cVA_ARGS, image)) {
log.handleProblem(IProblem.PREPROCESSOR_INVALID_VA_ARGS, null, fExpansionOffset, candidate.getEndOffset());
}
needParam= false;
}
}
needAnotherToken= null;
break;
case IToken.tPOUND:
needParam= paramList != null;
needAnotherToken= null;
break;
case IToken.tPOUNDPOUND:
if (needParam || isFirst) {
log.handleProblem(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, name, fExpansionOffset, candidate.getEndOffset());
}
needAnotherToken= candidate;
needParam= false;
break;
default:
if (needParam) {
log.handleProblem(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, name, fExpansionOffset, candidate.getEndOffset());
needParam= false;
}
needAnotherToken= null;
break;
}
isFirst= false;
fExpansionEndOffset= candidate.getEndOffset();
result.append(candidate);
candidate= lexer.nextToken();
}
if (needAnotherToken != null) {
log.handleProblem(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, name, needAnotherToken.getOffset(), needAnotherToken.getEndOffset());
}
}
}