| /******************************************************************************* |
| * 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++; |
| } |
| } |