/*******************************************************************************
* Copyright (c) 2011, 2015 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:
* Markus Schorn - initial API and implementation
* Nathan Ridge - added comments and fixed bug 445177
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import java.util.BitSet;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser.ITemplateIdStrategy;
/**
* This class is used to track alternatives for parsing segments of code that involve '<' tokens.
*
* The '<' token can be either a less-than operator or part of a template-id.
* When parsing, we potentially need to consider both possibilities for each use of '<'.
*
* An instance of this class is used to track alternative parses in a segment of code that includes one or
* more uses of '<' preceded by names. An alternative consists of a choices (template-id or not) for each
* name. At a given point in time, the instance has a notion of a current alternative, and a current
* position within that alternative.
*
* @see also NameOrTemplateIDVariants, which is used together with this class to deal with ambiguities
* involving '<' when parsing in an expression context.
*/
final class TemplateIdStrategy implements ITemplateIdStrategy {
// The current position in the current alternative.
// The next call to shallParseAsTemplateID() will return whether in the current alternative,
// the name at index (fCurrentBranchPoint + 1) should be parsed as a template-id.
private int fCurrentBranchPoint;
// The current alternative, represented as a bitset with one bit for each name.
// A bit corresponding to a name is clear if the name should be parsed as a template-id, and set if
// it should not.
// For the first alternative, this bitset is null, and this is interpreted as all zeros (i.e. every name
// is parsed as a template-id).
private BitSet fSimpleIDs;
// The set of names which are parsed as template-ids in the current alternative.
private IASTName[] fTemplateNames;
public TemplateIdStrategy() {
fCurrentBranchPoint= -1;
fTemplateNames= IASTName.EMPTY_NAME_ARRAY;
}
/**
* Returns whether 'name' should be parsed as a template-id according to the current alternative.
* For each alternative, this is expected to be called once for each name in a segment code for which
* this choice is ambiguous.
* For the first alternative, these calls are used to initially populate the list of names.
* For subsequent alternatives, these calls are expected for the same names in the same order.
*/
@Override
public boolean shallParseAsTemplateID(IASTName name) {
fCurrentBranchPoint++;
// 'fSimpleIDs == null' means we're on the first alternative.
// On the first alternative, everything is parsed as a template-id.
boolean templateID= fSimpleIDs == null || !fSimpleIDs.get(fCurrentBranchPoint);
if (templateID) {
fTemplateNames= ArrayUtil.append(fTemplateNames, name);
}
return templateID;
}
/**
* Advance to the next alternative parse, or return false is there is none.
* After a call to this, the current position is reset to the beginning of the (next) alternative.
* @param previousAlternativeFailedToParse whether this function is being called because the previous
* alternative failed to parse
*/
public boolean setNextAlternative(boolean previousAlternativeFailedToParse) {
// No one has called shallParseAsTemplateID() for the current alternative, so there are no
// ambiguous names. Therefore, there are no more alternatives.
if (fCurrentBranchPoint < 0) {
return false;
}
// Reset the current position, saving the old one, which should point to the last name in the
// bitset for which parsing was attempted during the previous alternative.
int bp = fCurrentBranchPoint;
fCurrentBranchPoint= -1;
// Reset the list of names that were parsed as template-ids, saving the list for the previous
// alternative.
IASTName[] names = getTemplateNames();
// Note that 'names' here contains the list of names for which there is a '0' in the bitset.
int nameLen= names.length;
fTemplateNames= IASTName.EMPTY_NAME_ARRAY;
// If the previous alternative was the first, the bitset is still null. Create it.
if (fSimpleIDs == null) {
fSimpleIDs= new BitSet();
}
// Advance to the next alternative by finding the right-most '0' in the bitset, and setting it to '1',
// and bits to the right of it to '0'. In this way, successive calls to this function will iterate
// over all possible alternatives.
// Note that in searching for the right-most '0', we start at 'bp', not the last element of the
// bitset. The reason is that if 'bp' is not the last element of the bitset, it means that during the
// previous alternative, we failed the parse before getting beyond 'bp'. This means that there is a
// problem with one of the choices up to and including 'bp', so there's no point in trying another
// alternatives that keeps these choices the same.
while (bp >= 0) {
if (!fSimpleIDs.get(bp)) {
// Optimization (bug 363609): if during the previous alternative, a name was parsed as a
// template-id with multiple template arguments, it's not going to be parsed differently in
// a subsequent alternative, so keep it as a template-id.
// Of course, this optimization is only possible if the previous alternative was parsed
// successfully (bug 445177).
// TODO: This optimization is invalid since it triggers bug 497931.
if (previousAlternativeFailedToParse || nameLen == 0 || !hasMultipleArgs(names[--nameLen])) {
fSimpleIDs.clear(bp+1, Integer.MAX_VALUE);
fSimpleIDs.set(bp);
return true;
}
}
bp--;
}
// The bitset was all ones - there are no more alternatives.
return false;
}
private boolean hasMultipleArgs(IASTName templateName) {
IASTNode parent= templateName.getParent();
if (parent instanceof ICPPASTTemplateId) {
return ((ICPPASTTemplateId) parent).getTemplateArguments().length > 1;
}
return false;
}
public IASTName[] getTemplateNames() {
return ArrayUtil.trim(fTemplateNames);
}
}