1
/*******************************************************************************
2
* Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Markus Schorn - initial API and implementation
10
*******************************************************************************/
11
package org.eclipse.cdt.internal.core.parser.scanner;
13
import java.util.ArrayList;
15
import org.eclipse.cdt.core.parser.IProblem;
16
import org.eclipse.cdt.core.parser.IToken;
17
import org.eclipse.cdt.core.parser.Keywords;
18
import org.eclipse.cdt.core.parser.OffsetLimitReachedException;
19
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
20
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
23
* Utility to parse macro definitions and create the macro objects for the preprocessor.
26
public class MacroDefinitionParser {
27
private static final int ORIGIN_PREPROCESSOR_DIRECTIVE = OffsetLimitReachedException.ORIGIN_PREPROCESSOR_DIRECTIVE;
30
* Exception for reporting problems while parsing a macro definition.
32
static class InvalidMacroDefinitionException extends Exception {
34
public int fStartOffset;
35
public int fEndOffset;
36
public InvalidMacroDefinitionException(char[] name, int startOffset, int endOffset) {
38
fStartOffset= startOffset;
39
fEndOffset= endOffset;
44
* Token used for macro parameters found in the replacement list.
46
static class TokenParameterReference extends TokenWithImage {
47
private final int fIndex;
49
public TokenParameterReference(int type, int idx, Object source, int offset, int endOffset, char[] name) {
50
super(type, source, offset, endOffset, name);
54
public int getIndex() {
59
public String toString() {
60
return "[" + fIndex + "]"; //$NON-NLS-1$ //$NON-NLS-2$
64
public static char[] getExpansion(AbstractCharArray expansionImage, int offset, int endOffset) {
65
TokenList tl= new TokenList();
66
Lexer lex= new Lexer(expansionImage, offset, endOffset, new LexerOptions(), ILexerLog.NULL, null);
68
lex.nextToken(); // consume the start token
69
new MacroDefinitionParser().parseExpansion(lex, ILexerLog.NULL, null, new char[][]{}, tl);
70
} catch (OffsetLimitReachedException e) {
73
StringBuffer buf= new StringBuffer();
76
return CharArrayUtils.EMPTY;
78
endOffset= t.getOffset();
79
for (; t != null; t= (Token) t.getNext()) {
80
if (endOffset < t.getOffset()) {
83
buf.append(t.getCharImage());
84
endOffset= t.getEndOffset();
86
final int length= buf.length();
87
final char[] expansion= new char[length];
88
buf.getChars(0, length, expansion, 0);
92
private int fHasVarArgs;
93
private int fExpansionOffset;
94
private int fExpansionEndOffset;
95
private Token fNameToken;
97
MacroDefinitionParser() {
101
* In case the name was successfully parsed, the name token is returned.
102
* Otherwise the return value is undefined.
104
public Token getNameToken() {
109
* Parses an entire macro definition. Name must be the next token of the lexer.
111
public ObjectStyleMacro parseMacroDefinition(final Lexer lexer, final ILexerLog log)
112
throws OffsetLimitReachedException, InvalidMacroDefinitionException {
113
final Token name = parseName(lexer);
114
final AbstractCharArray source= lexer.getInput();
115
final char[] nameChars= name.getCharImage();
116
final char[][] paramList= parseParamList(lexer, name);
117
final TokenList replacement= new TokenList();
118
parseExpansion(lexer, log, nameChars, paramList, replacement);
119
if (paramList == null) {
120
return new ObjectStyleMacro(nameChars, fExpansionOffset, fExpansionEndOffset, replacement, source);
122
return new FunctionStyleMacro(nameChars, paramList, fHasVarArgs, fExpansionOffset, fExpansionEndOffset, replacement, source);
126
* Parses a macro definition without the replacement. Name must be the next token of the lexer.
128
public PreprocessorMacro parseMacroDefinition(final Lexer lexer, final ILexerLog log, final char[] replacement)
129
throws InvalidMacroDefinitionException, OffsetLimitReachedException {
130
final Token name = parseName(lexer);
132
final char[] nameChars = name.getCharImage();
133
final char[][] paramList= parseParamList(lexer, name);
134
final Token replacementToken = lexer.currentToken();
135
if (replacementToken.getType() != IToken.tEND_OF_INPUT) {
136
throw new InvalidMacroDefinitionException(nameChars, replacementToken.getOffset(), replacementToken.getEndOffset());
139
if (paramList == null) {
140
return new ObjectStyleMacro(nameChars, replacement);
142
return new FunctionStyleMacro(nameChars, paramList, fHasVarArgs, replacement);
146
* Parses a macro definition basically checking for var-args.
148
public static PreprocessorMacro parseMacroDefinition(final char[] name, char[][] paramList, final char[] replacement) {
150
if (paramList != null) {
151
final int length = paramList.length;
153
char[] lastParam= paramList[length-1];
154
final int lpl = lastParam.length;
156
case 0: case 1: case 2:
159
if (CharArrayUtils.equals(lastParam, Keywords.cpELLIPSIS)) {
160
hasVarargs= FunctionStyleMacro.VAARGS;
161
char[][] copy= new char[length][];
162
System.arraycopy(paramList, 0, copy, 0, length-1);
163
copy[length-1]= Keywords.cVA_ARGS;
168
if (CharArrayUtils.equals(lastParam, lpl-3, 3, Keywords.cpELLIPSIS)) {
169
hasVarargs= FunctionStyleMacro.NAMED_VAARGS;
170
char[][] copy= new char[length][];
171
System.arraycopy(paramList, 0, copy, 0, length-1);
172
copy[length-1]= CharArrayUtils.subarray(lastParam, 0, lpl-3);
180
if (paramList == null) {
181
return new ObjectStyleMacro(name, replacement);
183
return new FunctionStyleMacro(name, paramList, hasVarargs, replacement);
186
private Token parseName(final Lexer lexer) throws OffsetLimitReachedException, InvalidMacroDefinitionException {
187
final Token name= lexer.nextToken();
188
final int tt= name.getType();
189
if (tt != IToken.tIDENTIFIER) {
190
if (tt == IToken.tCOMPLETION) {
191
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, name);
193
throw new InvalidMacroDefinitionException(name.getCharImage(), name.getOffset(), name.getEndOffset());
199
private char[][] parseParamList(Lexer lex, final Token name) throws OffsetLimitReachedException, InvalidMacroDefinitionException {
200
final Token lparen= lex.nextToken();
201
fHasVarArgs= FunctionStyleMacro.NO_VAARGS;
202
if (lparen.getType() != IToken.tLPAREN || name.getEndOffset() != lparen.getOffset()) {
205
ArrayList<char[]> paramList= new ArrayList<char[]>();
208
final Token param= lex.nextToken();
209
switch (param.getType()) {
210
case IToken.tCOMPLETION:
211
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, param);
213
case IToken.tIDENTIFIER:
214
paramList.add(param.getCharImage());
215
next= lex.nextToken();
216
if (next.getType() == IToken.tELLIPSIS) {
217
fHasVarArgs= FunctionStyleMacro.NAMED_VAARGS;
218
next= lex.nextToken();
222
case IToken.tELLIPSIS:
223
fHasVarArgs= FunctionStyleMacro.VAARGS;
224
paramList.add(Keywords.cVA_ARGS);
225
next= lex.nextToken();
233
throw new InvalidMacroDefinitionException(name.getCharImage(), name.getOffset(), param.getEndOffset());
235
throw new InvalidMacroDefinitionException(name.getCharImage(), name.getOffset(), param.getEndOffset());
238
while (fHasVarArgs==0 && next.getType() == IToken.tCOMMA);
239
if (next.getType() != IToken.tRPAREN) {
240
throw new InvalidMacroDefinitionException(name.getCharImage(), name.getOffset(), next.getEndOffset());
242
next= lex.nextToken(); // consume the closing parenthesis
244
return paramList.toArray(new char[paramList.size()][]);
248
* Parses the expansion for a macro, the current token must be the first token of the expansion
251
public void parseExpansion(final Lexer lexer, final ILexerLog log, final char[] name, final char[][] paramList,
252
TokenList result) throws OffsetLimitReachedException {
253
boolean needParam= false;
254
boolean isFirst= true;
255
Token needAnotherToken= null;
257
Token candidate= lexer.currentToken();
258
fExpansionOffset= fExpansionEndOffset= candidate.getOffset();
261
switch(candidate.getType()) {
262
case IToken.tCOMPLETION:
263
throw new OffsetLimitReachedException(ORIGIN_PREPROCESSOR_DIRECTIVE, candidate);
264
case IToken.tEND_OF_INPUT:
267
case IToken.tIDENTIFIER:
268
if (paramList != null) {
269
// convert the parameters to special tokens
270
final char[] image = candidate.getCharImage();
271
int idx= CharArrayUtils.indexOf(image, paramList);
273
candidate= new TokenParameterReference(CPreprocessor.tMACRO_PARAMETER, idx, lexer.getSource(), candidate.getOffset(), candidate.getEndOffset(), paramList[idx]);
278
log.handleProblem(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, name, fExpansionOffset, candidate.getEndOffset());
280
else if (CharArrayUtils.equals(Keywords.cVA_ARGS, image)) {
281
log.handleProblem(IProblem.PREPROCESSOR_INVALID_VA_ARGS, null, fExpansionOffset, candidate.getEndOffset());
286
needAnotherToken= null;
289
needParam= paramList != null;
290
needAnotherToken= null;
292
case IToken.tPOUNDPOUND:
293
if (needParam || isFirst) {
294
log.handleProblem(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, name, fExpansionOffset, candidate.getEndOffset());
296
needAnotherToken= candidate;
301
log.handleProblem(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, name, fExpansionOffset, candidate.getEndOffset());
304
needAnotherToken= null;
308
fExpansionEndOffset= candidate.getEndOffset();
309
result.append(candidate);
310
candidate= lexer.nextToken();
312
if (needAnotherToken != null) {
313
log.handleProblem(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, name, needAnotherToken.getOffset(), needAnotherToken.getEndOffset());