blob: 395e2174423569700266067679992791009ddedf [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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
*******************************************************************************/
package org.eclipse.photran.internal.core.preprocessor.c;
import java.util.ArrayList;
import java.util.LinkedList;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.text.edits.ReplaceEdit;
/**
* Collects information while macro expansion is performed.
*/
public class MacroExpansionTracker {
public class MacroInfo {
private TokenList fMacroCall= new TokenList();
private ArrayList<TokenList> fArguments= new ArrayList<TokenList>();
public MacroInfo(Token identifier) {
fMacroCall.append(identifier);
}
public void setArgument(TokenList tokenList) {
fArguments.add(tokenList);
}
}
private final int fStepToTrack;
private int fStepCount;
private String fPreStep;
private ReplaceEdit fReplacement;
private IMacroBinding fMacroDefinition;
private Lexer fLexer;
private String fReplacementText= ""; //$NON-NLS-1$
private LinkedList<MacroInfo> fMacroStack= new LinkedList<MacroInfo>();
private IToken fReplaceFrom;
private IToken fReplaceTo;
public MacroExpansionTracker(int stepToTrack) {
fStepToTrack= stepToTrack;
}
/**
* Returns whether the requested step has already been encountered.
*/
public boolean isDone() {
return fStepCount > fStepToTrack;
}
/**
* Returns whether we are currently looking at the requested step.
*/
public boolean isRequestedStep() {
return fStepCount == fStepToTrack;
}
/**
* Returns the total amount of steps encountered so far.
*/
public int getStepCount() {
return fStepCount;
}
/**
* Returns the code as it looks like just before performing the step that was tracked.
*/
public String getCodeBeforeStep() {
return fPreStep;
}
/**
* Returns the replacement that represents the change by the step that was tracked.
*/
public ReplaceEdit getReplacement() {
return fReplacement;
}
/**
* Returns the macro that is expanded in the step that was tracked.
*/
public IMacroBinding getExpandedMacro() {
return fMacroDefinition;
}
/**
* Informs the tracker that macro expansion is started.
*/
void start(Lexer lexer) {
fLexer= lexer;
}
/**
* Informs the tracker that the expansion is done.
* @param result the list of tokens after performing partial expansion up to the step that was
* tracked.
* @param endOffset the end offset of the input that was read from the lexer.
*/
void finish(TokenList result, int endOffset) {
final char[] lexInput = fLexer.getInput();
if (!isDone()) {
// special case we compute the entire expansion as one step, the result contains the
// expanded text
StringBuilder replacementText= new StringBuilder();
toString(result, lexInput, replacementText, replacementText, replacementText);
fPreStep= new String(lexInput);
fReplacement= new ReplaceEdit(0, endOffset, replacementText.toString());
}
else {
// the regular case the result contains the text before the step
StringBuilder before= new StringBuilder();
StringBuilder replace= new StringBuilder();
StringBuilder after= new StringBuilder();
toString(result, lexInput, before, replace, after);
int offset= before.length();
// workaround bug 220158
final CharSequence csr= replace;
final CharSequence csa= after;
before.append(csr).append(csa);
before.append(lexInput, endOffset, lexInput.length-endOffset);
fPreStep= before.toString();
fReplacement= new ReplaceEdit(offset, replace.length(), fReplacementText);
}
}
/**
* There was no macro at the beginning of the input.
*/
void fail() {
fPreStep= new String(fLexer.getInput());
fReplacement= new ReplaceEdit(0, 0, ""); //$NON-NLS-1$
}
private void toString(TokenList tokenList, char[] rootInput, StringBuilder before, StringBuilder replace, StringBuilder after) {
StringBuilder buf= before;
Token t= tokenList.first();
if (t == null) {
return ;
}
Token l= null;
Token n;
for (; t != null; l=t, t=n) {
n= (Token) t.getNext();
if (l != null && MacroExpander.hasImplicitSpace(l, t)) {
char[] input= getInputForSource(l.fSource, rootInput);
if (input == null) {
buf.append(' ');
}
else {
final int from = l.getEndOffset();
final int to = t.getOffset();
buf.append(input, from, to-from);
}
}
if (t == fReplaceFrom) {
buf= replace;
}
char[] input= getInputForSource(t.fSource, rootInput);
if (input == null) {
buf.append(t.getCharImage());
}
else {
buf.append(input, t.getOffset(), t.getLength());
}
if (t == fReplaceTo) {
buf= after;
}
}
}
private char[] getInputForSource(Object source, char[] rootInput) {
if (source instanceof MacroExpander) {
return rootInput;
}
if (source instanceof PreprocessorMacro) {
final PreprocessorMacro pm = (PreprocessorMacro) source;
if (!pm.isDynamic()) {
return pm.getExpansionImage();
}
}
return null;
}
/**
* Informs the tracker that a function-style expansion is started.
* @param identifier the identifier token for the macro expansion.
*/
public void startFunctionStyleMacro(Token identifier) {
fMacroStack.add(new MacroInfo(identifier));
}
/**
* All tokens defining a function-style macro expansion are reported.
*/
public void addFunctionStyleMacroExpansionToken(Token t) {
fMacroStack.getLast().fMacroCall.append(t);
}
/**
* The expanded arguments for the function-style macro expansion are reported.
* @param tokenList the expanded argument, or <code>null</code> if it should not
* be expanded.
*/
public void setExpandedMacroArgument(TokenList tokenList) {
fMacroStack.getLast().setArgument(tokenList);
}
/**
* Called for the requested step.
* @param macro the macro expanded in the requested step.
* @param replacement the replacement for the expansion.
* @param result a list to store the macro call with the arguments substituted in.
*/
public void storeFunctionStyleMacroReplacement(PreprocessorMacro macro, TokenList replacement, TokenList result) {
MacroInfo minfo= fMacroStack.getLast();
fMacroDefinition= macro;
fReplaceFrom= minfo.fMacroCall.first();
appendFunctionStyleMacro(result);
fReplaceTo= result.last();
StringBuilder buf= new StringBuilder();
toString(replacement, fLexer.getInput(), buf, buf, buf);
fReplacementText= buf.toString();
}
/**
* Append the current function-style macro with the arguments substituted.
*/
public void appendFunctionStyleMacro(TokenList result) {
MacroInfo minfo= fMacroStack.getLast();
boolean active= true;
int nesting= -1;
int pcount= 0;
Token n;
Token l= null;
for (Token t = minfo.fMacroCall.first(); t != null; l=t, t=n) {
n = (Token) t.getNext();
switch (t.getType()) {
case IToken.tLPAREN:
if (active) {
result.append(t);
}
// the first one sets nesting to zero.
++nesting;
if (nesting == 0) {
if (pcount < minfo.fArguments.size()) {
TokenList p = minfo.fArguments.get(pcount);
if (p != null) {
active = false;
if (n != null && n.getType() != IToken.tCOMMA && n.getType() != IToken.tRPAREN) {
MacroExpander.addSpacemarker(t, n, result);
result.appendAll(p);
}
}
}
}
break;
case IToken.tRPAREN:
if (!active && nesting == 0) {
MacroExpander.addSpacemarker(l, t, result);
active= true;
}
if (active) {
result.append(t);
}
if (nesting > 0) {
nesting--;
}
break;
case IToken.tCOMMA:
if (nesting == 0) {
if (++pcount < minfo.fArguments.size()) {
if (!active) {
MacroExpander.addSpacemarker(l, t, result);
}
result.append(t);
TokenList p = minfo.fArguments.get(pcount);
active = p == null;
if (!active) {
if (n != null && n.getType() != IToken.tCOMMA && n.getType() != IToken.tRPAREN) {
MacroExpander.addSpacemarker(t, n, result);
result.appendAll(p);
}
}
}
} else if (active) {
result.append(t);
}
break;
default:
if (active) {
result.append(t);
}
}
}
}
/**
* Informs the tracker that the function style macro has been expanded.
*/
public void endFunctionStyleMacro() {
fStepCount++;
fMacroStack.removeLast();
}
/**
* Called for the requested step
* @param macro the macro expanded in the requested step.
* @param identifier the token that gets replaced.
* @param replacement the replacement
* @param result a list to store the macro in.
*/
public void storeObjectStyleMacroReplacement(PreprocessorMacro macro, Token identifier, TokenList replacement, TokenList result) {
fMacroDefinition= macro;
fReplaceFrom= fReplaceTo= identifier;
result.append(identifier);
StringBuilder buf= new StringBuilder();
toString(replacement, fLexer.getInput(), buf, buf, buf);
fReplacementText= buf.toString();
}
/**
* Informs the tracker that an object style macro has been expanded.
*/
public void endObjectStyleMacro() {
fStepCount++;
}
}