/*******************************************************************************
 * Copyright (c) 2006, 2012 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.core.dom.lrparser.action.cpp;

import static org.eclipse.cdt.core.dom.lrparser.action.ParserUtil.*;
import static org.eclipse.cdt.core.parser.util.CollectionUtils.findFirstAndRemove;
import static org.eclipse.cdt.core.parser.util.CollectionUtils.reverseIterable;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import lpg.lpgjavaruntime.IToken;

import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpressionList;
import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointer;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTWhileStatement;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAmbiguousTemplateArgument;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConversionName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLinkageSpecification;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceAlias;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTOperatorName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTPointerToMember;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTReferenceOperator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplatedTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTypenameExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNodeFactory;
import org.eclipse.cdt.core.dom.lrparser.ISecondaryParser;
import org.eclipse.cdt.core.dom.lrparser.LPGTokenAdapter;
import org.eclipse.cdt.core.dom.lrparser.action.BuildASTParserAction;
import org.eclipse.cdt.core.dom.lrparser.action.ITokenMap;
import org.eclipse.cdt.core.dom.lrparser.action.ITokenStream;
import org.eclipse.cdt.core.dom.lrparser.action.ParserUtil;
import org.eclipse.cdt.core.dom.lrparser.action.ScopedStack;
import org.eclipse.cdt.core.dom.lrparser.action.TokenMap;
import org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousExpression;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTAmbiguousDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTAmbiguousExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTAmbiguousStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTAmbiguousTemplateArgument;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTConstructorInitializer;
import org.eclipse.cdt.internal.core.dom.parser.cpp.OverloadableOperator;

/**
 * Semantic actions that build the AST during the parse. 
 * These are the actions that are specific to the C++ parser, the superclass
 * contains actions that can be shared with the C99 parser.
 * 
 * @author Mike Kucera
 */
@SuppressWarnings("restriction")
public class CPPBuildASTParserAction extends BuildASTParserAction {

	/** Allows code in this class to refer to the token kinds in CPPParsersym */
	private final ITokenMap tokenMap;
	
	/** Used to create the AST node objects */
	protected final ICPPNodeFactory nodeFactory;
	
	protected final ICPPSecondaryParserFactory parserFactory;
	
	/** Stack that provides easy access to the current class name, used to disambiguate declarators. */
	protected final LinkedList<IASTName> classNames = new LinkedList<IASTName>();
	
	
	/**
	 * @param parser
	 * @param orderedTerminalSymbols When an instance of this class is created for a parser
	 * that parsers token kinds will be mapped back to the base C99 parser's token kinds.
	 */
	public CPPBuildASTParserAction(ITokenStream parser, ScopedStack<Object> astStack, ICPPNodeFactory nodeFactory, ICPPSecondaryParserFactory parserFactory) {
		super(parser, astStack, nodeFactory, parserFactory);
		
		this.nodeFactory = nodeFactory;
		this.parserFactory = parserFactory;
		this.tokenMap = new TokenMap(CPPParsersym.orderedTerminalSymbols, parser.getOrderedTerminalSymbols());
	}
	
	
	private int baseKind(IToken token) {
		return tokenMap.mapKind(token.getKind());
	}
	
	@Override 
	protected boolean isCompletionToken(IToken token) {
		return baseKind(token) == TK_Completion;
	}
	
	@Override
	protected boolean isIdentifierToken(IToken token) {
		return baseKind(token) == TK_identifier;
	}
	
	@Override
	protected IASTName createName(char[] image) {
		return nodeFactory.newName(image);
	}


	
	public void consumeNewInitializer() {
		IASTExpression expr;
		if(astStack.peek() == null) { // if there is an empty set of parens
			astStack.pop();
			expr = nodeFactory.newExpressionList();
			setOffsetAndLength(expr);
		} else {
			expr = (IASTExpression) astStack.pop(); // may be null
		}
		ICPPASTConstructorInitializer initializer = nodeFactory.newConstructorInitializer(expr);
		setOffsetAndLength(initializer);
		astStack.push(initializer);
	}
	
	
	
	// TODO can the new_array_expressions be removed? it looks like they parse as part of the type id
	/**
	 * new_expression
     *     ::= dcolon_opt 'new' new_placement_opt new_type_id <openscope-ast> new_array_expressions_op new_initializer_opt
     *       | dcolon_opt 'new' new_placement_opt '(' type_id ')' <openscope-ast> new_array_expressions_op new_initializer_opt
	 */
	public void consumeExpressionNew(boolean isNewTypeId) {
		ICPPASTConstructorInitializer initializer = (ICPPASTConstructorInitializer) astStack.pop(); // may be null
		List<Object> arrayExpressions = astStack.closeScope();
		IASTTypeId typeId = (IASTTypeId) astStack.pop();
		IASTExpression placement = (IASTExpression) astStack.pop(); // may be null
		boolean hasDoubleColon = astStack.pop() != null;
		
		ICPPASTNewExpression newExpression = nodeFactory.newNewExpression(new IASTExpression[] {placement}, initializer, typeId);
		newExpression.setIsGlobal(hasDoubleColon);
		newExpression.setIsNewTypeId(isNewTypeId);
		setOffsetAndLength(newExpression);
		
		for(Object expr : arrayExpressions)
			newExpression.addNewTypeIdArrayExpression((IASTExpression)expr);
		
		// handle ambiguities of the form:  (A)(B)
		if(!isNewTypeId && initializer == null &&
				placement instanceof IASTIdExpression && 
				typeId != null && typeId.getDeclSpecifier() instanceof IASTNamedTypeSpecifier) {
			
			IASTName firstName = ((IASTIdExpression)placement).getName();
			IASTName secondName = ((IASTNamedTypeSpecifier)typeId.getDeclSpecifier()).getName();
			
			IASTNamedTypeSpecifier newTypeSpecifier = nodeFactory.newTypedefNameSpecifier(firstName.copy());
			ParserUtil.setOffsetAndLength(newTypeSpecifier, firstName);
			IASTDeclarator newDeclarator = nodeFactory.newDeclarator(nodeFactory.newName());
			ParserUtil.setOffsetAndLength(newDeclarator, endOffset(firstName), 0);
			IASTTypeId newTypeId = nodeFactory.newTypeId(newTypeSpecifier, newDeclarator);
			ParserUtil.setOffsetAndLength(newTypeId, firstName);
			
			IASTIdExpression newInitializer = nodeFactory.newIdExpression(secondName.copy());
			ParserUtil.setOffsetAndLength(newInitializer, secondName);
			
			ICPPASTNewExpression alternate = nodeFactory.newNewExpression(null, newInitializer, newTypeId);
			ParserUtil.setOffsetAndLength(alternate, newExpression);
			newExpression.setIsGlobal(hasDoubleColon);
			newExpression.setIsNewTypeId(isNewTypeId);
			
			IASTAmbiguousExpression ambiguity = createAmbiguousExpression(newExpression, alternate);
			astStack.push(ambiguity);
		}
		else {
			astStack.push(newExpression);
		}
	}
	
	
	/**
	 * new_declarator -- pointer operators are part of the type id, held in an empty declarator
     *     ::= <openscope-ast> new_pointer_operators
	 */
	public void consumeNewDeclarator() {
		IASTName name = nodeFactory.newName();
		IASTDeclarator declarator = nodeFactory.newDeclarator(name);
		
		for(Object pointer : astStack.closeScope())
			declarator.addPointerOperator((IASTPointerOperator)pointer);
		
		setOffsetAndLength(declarator);
		astStack.push(declarator);
	}
	
	
	/**
	 * throw_expression
     *     ::= 'throw'
     *       | 'throw' assignment_expression
	 */
	public void consumeExpressionThrow(boolean hasExpr) {
		IASTExpression operand = hasExpr ? (IASTExpression) astStack.pop() : null;
		IASTUnaryExpression expr = nodeFactory.newUnaryExpression(ICPPASTUnaryExpression.op_throw, operand);
		setOffsetAndLength(expr);
		astStack.push(expr);
	}
	
	
	/**
	 * delete_expression
     *     ::= dcolon_opt 'delete' cast_expression
     *       | dcolon_opt 'delete' '[' ']' cast_expression
	 * @param isVectorized
	 */
	public void consumeExpressionDelete(boolean isVectorized) {
		IASTExpression operand = (IASTExpression) astStack.pop();
		boolean hasDoubleColon = astStack.pop() != null;
		
		ICPPASTDeleteExpression deleteExpr = nodeFactory.newDeleteExpression(operand);
		deleteExpr.setIsGlobal(hasDoubleColon);
		deleteExpr.setIsVectored(isVectorized);
		
		setOffsetAndLength(deleteExpr);
		astStack.push(deleteExpr);
	}
	
	
	/**
	 * 
	 */
	public void consumeExpressionFieldReference(boolean isPointerDereference, boolean hasTemplateKeyword) {
		IASTName name = (IASTName) astStack.pop();
		IASTExpression owner = (IASTExpression) astStack.pop();
		ICPPASTFieldReference expr = nodeFactory.newFieldReference(name, owner);
		expr.setIsPointerDereference(isPointerDereference);
		expr.setIsTemplate(hasTemplateKeyword);
		
		setOffsetAndLength(expr);
		astStack.push(expr);
	}
	
	/**
	 * postfix_expression
	 *     ::= simple_type_specifier '(' expression_list_opt ')'
	 */
	public void consumeExpressionSimpleTypeConstructor() {
		IASTExpression expression = (IASTExpression) astStack.pop();
		IToken token = (IToken) astStack.pop();
		////CDT_70_FIX_FROM_50-#3
		//int type = asICPPASTSimpleTypeConstructorExpressionType(token);
		ICPPASTConstructorInitializer init=null;
		if(expression!=null){
			init = new CPPASTConstructorInitializer();
			init.setExpression(expression);
    	
    		((ASTNode)init).setOffsetAndLength(((ASTNode)expression).getOffset(),((ASTNode)expression).getLength());
    	}
    	ICPPASTDeclSpecifier declspec = transformIntoSimpleTypeSpecifier(token);
		ICPPASTSimpleTypeConstructorExpression typeConstructor = nodeFactory.newSimpleTypeConstructorExpression(declspec, init);
		
		setOffsetAndLength(typeConstructor);
		astStack.push(typeConstructor);
	}
	
	
	private int asICPPASTSimpleTypeConstructorExpressionType(IToken token) {
		assert token != null;
		
		switch(baseKind(token)) {
			case TK_char     : return ICPPASTSimpleTypeConstructorExpression.t_char;
			case TK_wchar_t  : return ICPPASTSimpleTypeConstructorExpression.t_wchar_t;
			case TK_bool     : return ICPPASTSimpleTypeConstructorExpression.t_bool;
			case TK_short    : return ICPPASTSimpleTypeConstructorExpression.t_short;
			case TK_int      : return ICPPASTSimpleTypeConstructorExpression.t_int;
			case TK_long     : return ICPPASTSimpleTypeConstructorExpression.t_long;
			case TK_signed   : return ICPPASTSimpleTypeConstructorExpression.t_signed;
			case TK_unsigned : return ICPPASTSimpleTypeConstructorExpression.t_unsigned;
			case TK_float    : return ICPPASTSimpleTypeConstructorExpression.t_float;
			case TK_double   : return ICPPASTSimpleTypeConstructorExpression.t_double;
			case TK_void     : return ICPPASTSimpleTypeConstructorExpression.t_void;
		
			default:
				assert false : "type parsed wrong"; //$NON-NLS-1$
				return ICPPASTSimpleTypeConstructorExpression.t_unspecified;
		}
	}
	
	private ICPPASTDeclSpecifier transformIntoSimpleTypeSpecifier(IToken token){
		ICPPASTSimpleDeclSpecifier declspec= nodeFactory.newSimpleDeclSpecifier();
		switch(baseKind(token)) {
			case TK_char     : declspec.setType(Kind.eChar); break;
			case TK_wchar_t  : declspec.setType(Kind.eWChar); break;
			case TK_bool     : declspec.setType(Kind.eBoolean);break;
			case TK_short    : declspec.setShort(true); break;
			case TK_int      : declspec.setType(Kind.eInt); break;
			case TK_long     : declspec.setLong(true);	break;
			case TK_signed   : declspec.setSigned(true); break;
			case TK_unsigned : declspec.setUnsigned(true); break;
			case TK_float    : declspec.setType(Kind.eFloat); break;
			case TK_double   : declspec.setType(Kind.eDouble); break;
			case TK_void     : declspec.setType(Kind.eVoid); break;
		
			default:
				assert false : "type parsed wrong"; //$NON-NLS-1$
			    declspec.setType(Kind.eUnspecified);
			    break;
		}
		((ASTNode) declspec).setOffset(token.getStartOffset());
		int ruleLength = token.getEndOffset() - token.getStartOffset();
		((ASTNode) declspec).setLength(ruleLength < 0 ? 0 : ruleLength);
		
		return declspec;
		
	
	}
	
	
	/**
	 * postfix_expression
	 *     ::= 'typename' dcolon_opt nested_name_specifier <empty>  identifier_name '(' expression_list_opt ')'
     *       | 'typename' dcolon_opt nested_name_specifier template_opt template_id '(' expression_list_opt ')'
	 */
	@SuppressWarnings("unchecked")
	public void consumeExpressionTypeName() {
		IASTExpression expr = (IASTExpression) astStack.pop();
		IASTName name = (IASTName) astStack.pop();
		boolean isTemplate = astStack.pop() == PLACE_HOLDER;
		LinkedList<IASTName> nestedNames = (LinkedList<IASTName>) astStack.pop();
		IToken dColon = (IToken) astStack.pop();

		nestedNames.addFirst(name);
		
		int startOffset = dColon == null ? offset(nestedNames.getLast()) : offset(dColon);
		int endOffset   = endOffset(name);
		IASTName qualifiedName = createQualifiedName(nestedNames, startOffset, endOffset, dColon != null);
		
		ICPPASTTypenameExpression typenameExpr = nodeFactory.newTypenameExpression(qualifiedName, expr, isTemplate);
		
		setOffsetAndLength(typenameExpr);
		astStack.push(typenameExpr);
	}
	
	
	/**
	 * condition
     *     ::= type_specifier_seq declarator '=' assignment_expression
	 */
	public void consumeConditionDeclaration() {
		IASTExpression expr = (IASTExpression) astStack.pop();
		IASTDeclarator declarator = (IASTDeclarator) astStack.pop();
		IASTDeclSpecifier declSpec = (IASTDeclSpecifier) astStack.pop();
		//CDT_70_FIX_FROM_50-#2
		//IASTInitializerExpression initializer = nodeFactory.newInitializerExpression(expr);
		IASTEqualsInitializer initializer = nodeFactory.newEqualsInitializer(expr);
		ParserUtil.setOffsetAndLength(initializer, offset(expr), length(expr));
		declarator.setInitializer(initializer);
		
		IASTSimpleDeclaration declaration = nodeFactory.newSimpleDeclaration(declSpec);
		declaration.addDeclarator(declarator);
		
		setOffsetAndLength(declaration);
		astStack.push(declaration);
	}
	
	
	/**
	 * template_id
     *     ::= identifier_name '<' <openscope-ast> template_argument_list_opt '>'
     *     
     * operator_function_id
     *     ::= operator_id '<' <openscope-ast> template_argument_list_opt '>'
	 */
	public void consumeTemplateId() {
		List<Object> templateArguments = astStack.closeScope();
		IASTName name = (IASTName) astStack.pop();
		
		ICPPASTTemplateId templateId = nodeFactory.newTemplateId(name);
		
		for(Object arg : templateArguments) {
			if(arg instanceof IASTExpression)
				templateId.addTemplateArgument((IASTExpression)arg);
			else if(arg instanceof IASTTypeId)
				templateId.addTemplateArgument((IASTTypeId)arg);
			else if(arg instanceof ICPPASTAmbiguousTemplateArgument)
				templateId.addTemplateArgument((ICPPASTAmbiguousTemplateArgument)arg);
		}
		
		setOffsetAndLength(templateId);
		astStack.push(templateId);
	}
	

	/**
	 * Disambiguates template arguments.
	 */
	public void consumeTemplateArgumentTypeId() {
		// TODO is this necessary? It should be able to tell if it looks like an id expression
		ISecondaryParser<IASTExpression> secondaryParser = parserFactory.getExpressionParser(stream, properties);
		IASTExpression result = runSecondaryParser(secondaryParser);
		
		// The grammar rule allows assignment_expression, but the ambiguity
		// only arises with id_expressions.
		if(!(result instanceof IASTIdExpression))
			return;
		
		IASTTypeId typeId = (IASTTypeId) astStack.pop();
		IASTIdExpression expr = (IASTIdExpression) result;
		
		ICPPASTAmbiguousTemplateArgument ambiguityNode = new CPPASTAmbiguousTemplateArgument(typeId, expr);
		//setOffsetAndLength(ambiguityNode);
		
		astStack.push(ambiguityNode);
	}
	
	
	/**
	 * Disambiguates template arguments.
	 * Qualified names parse as an expression, so generate the corresponding
	 * typeId and create an ambiguity node.
	 */
	public void consumeTemplateArgumentExpression() {
		IASTExpression expr = (IASTExpression) astStack.peek();
		
		if(expr instanceof IASTIdExpression) {
			IASTName name = ((IASTIdExpression)expr).getName().copy();
			
			IASTNamedTypeSpecifier declSpec = nodeFactory.newTypedefNameSpecifier(name);
			ParserUtil.setOffsetAndLength(declSpec, name);
			
			IASTDeclarator declarator = nodeFactory.newDeclarator(nodeFactory.newName());
			ParserUtil.setOffsetAndLength(declarator, endOffset(declSpec), 0);
			
			IASTTypeId typeId = nodeFactory.newTypeId(declSpec, declarator);
			setOffsetAndLength(typeId);
			
			ICPPASTAmbiguousTemplateArgument ambiguityNode = new CPPASTAmbiguousTemplateArgument(typeId, expr);
			
			astStack.pop();
			astStack.push(ambiguityNode);
		}
	}
	
	
	/**
	 * operator_id
     *     ::= 'operator' overloadable_operator
	 */
	public void consumeOperatorName() {
		List<IToken> tokens = stream.getRuleTokens();
		tokens = tokens.subList(1, tokens.size());
		OverloadableOperator operator = getOverloadableOperator(tokens);
		
		ICPPASTOperatorName name = nodeFactory.newOperatorName(operator.toCharArray());
		setOffsetAndLength(name);
		astStack.push(name);
	}

	
	private OverloadableOperator getOverloadableOperator(List<IToken> tokens) {
		if(tokens.size() == 1) {
			// TODO this is a hack that I did to save time
			LPGTokenAdapter coreToken = (LPGTokenAdapter) tokens.get(0);
			return OverloadableOperator.valueOf(coreToken.getWrappedToken());
		}
		else if(matchTokens(tokens, tokenMap, TK_new, TK_LeftBracket, TK_RightBracket)) {
			return OverloadableOperator.NEW_ARRAY;
		}
		else if(matchTokens(tokens, tokenMap, TK_delete, TK_LeftBracket, TK_RightBracket)) {
			return OverloadableOperator.DELETE_ARRAY;
		}
		else if(matchTokens(tokens, tokenMap, TK_LeftBracket, TK_RightBracket)) {
			return OverloadableOperator.BRACKET;
		}
		else if(matchTokens(tokens, tokenMap, TK_LeftParen, TK_RightParen)) {
			return OverloadableOperator.PAREN;
		}
		
		return null;
	}
	
	
	/**
	 * conversion_function_id
     *     ::= 'operator' conversion_type_id
	 */
	public void consumeConversionName() {
//	    Representation is computed by the conversion name itself, see bug 258054
//		String rep = createStringRepresentation(parser.getRuleTokens());
//		char[] chars = rep.toCharArray();
		
		IASTTypeId typeId = (IASTTypeId) astStack.pop();
		ICPPASTConversionName name = nodeFactory.newConversionName(typeId);
		setOffsetAndLength(name);
		astStack.push(name);
	}
	
	
	
	
    /**
     * unqualified_id
     *     ::= '~' identifier_token
     */
  	public void consumeDestructorName() {
  		char[] chars = ("~" + stream.getRightIToken()).toCharArray(); //$NON-NLS-1$
  		
  		IASTName name = nodeFactory.newName(chars);
  		setOffsetAndLength(name);
  		astStack.push(name);
  	}
  	
  	
  	/**
  	 * destructor_type_name
     *     ::= '~' template_id_name
  	 */
  	public void consumeDestructorNameTemplateId() {
  		ICPPASTTemplateId templateId = (ICPPASTTemplateId) astStack.peek();
  		
  		IASTName oldName = templateId.getTemplateName();
  		char[] newChars = ("~" + oldName).toCharArray(); //$NON-NLS-1$
  		
  		IASTName newName = nodeFactory.newName(newChars);
  		
  		int offset = offset(stream.getLeftIToken());
  		int length = offset - endOffset(oldName);
  		ParserUtil.setOffsetAndLength(newName, offset, length);
  		
  		templateId.setTemplateName(newName);
  	}
  	
  	
  	
  	/**
  	 * qualified_id
     *     ::= '::' identifier_name
     *       | '::' operator_function_id
     *       | '::' template_id
  	 */

	public void consumeGlobalQualifiedId() {
  		IASTName name = (IASTName) astStack.pop();
  		
  		ICPPASTQualifiedName qualifiedName = nodeFactory.newQualifiedName();
  		qualifiedName.addName(name);
  		qualifiedName.setFullyQualified(true);
  		setOffsetAndLength(qualifiedName);
  		astStack.push(qualifiedName);
  	}
  	
  	
  	/**
	 * selection_statement ::=  switch '(' condition ')' statement
	 */
	public void consumeStatementSwitch() {
		IASTStatement body = (IASTStatement) astStack.pop();
		
		Object condition = astStack.pop();
		
		IASTSwitchStatement stat;
		if(condition instanceof IASTExpression)
			stat = nodeFactory.newSwitchStatement((IASTExpression)condition, body);
		else
			stat = nodeFactory.newSwitchStatement((IASTDeclaration)condition, body);
		
		setOffsetAndLength(stat);
		astStack.push(stat);
	}
	

	public void consumeStatementIf(boolean hasElse) {
		IASTStatement elseClause = hasElse ? (IASTStatement)astStack.pop() : null;		
		IASTStatement thenClause = (IASTStatement) astStack.pop();
		
		Object condition = astStack.pop();
		
		IASTIfStatement ifStatement;
		if(condition instanceof IASTExpression)
			ifStatement = nodeFactory.newIfStatement((IASTExpression)condition, thenClause, elseClause);
		else
			ifStatement = nodeFactory.newIfStatement((IASTDeclaration)condition, thenClause, elseClause);
		
		setOffsetAndLength(ifStatement);
		astStack.push(ifStatement);
	}
	
	
	/**
	 * iteration_statement ::= 'while' '(' condition ')' statement
	 */
	public void consumeStatementWhileLoop() {
		IASTStatement body = (IASTStatement) astStack.pop();
		
		Object condition = astStack.pop();
		
		IASTWhileStatement whileStatement;
		if(condition instanceof IASTExpression)
			whileStatement = nodeFactory.newWhileStatement((IASTExpression)condition, body);
		else
			whileStatement = nodeFactory.newWhileStatement((IASTDeclaration)condition, body);
		
		setOffsetAndLength(whileStatement);
		astStack.push(whileStatement);
	}
	
	
	/**
	 */
	public void consumeStatementForLoop() {
		IASTStatement body = (IASTStatement) astStack.pop();
		IASTExpression expr = (IASTExpression) astStack.pop();
		Object condition = astStack.pop(); // can be an expression or a declaration
		IASTStatement initializer = (IASTStatement) astStack.pop();
		
		// bug 234463, fix for content assist to work in this case
		int TK_EOC = TK_EndOfCompletion; // TODO: change this in the grammar file
		List<IToken> tokens = stream.getRuleTokens();
		if(matchTokens(tokens, tokenMap, 
				TK_for, TK_LeftParen, TK_Completion, TK_EOC, TK_EOC, TK_EOC, TK_EOC)) {
			IASTName name = createName(tokens.get(2));
			IASTIdExpression idExpression = nodeFactory.newIdExpression(name);
			ParserUtil.setOffsetAndLength(idExpression, offset(name), length(name));
			initializer = nodeFactory.newExpressionStatement(idExpression);
			ParserUtil.setOffsetAndLength(initializer, offset(name), length(name));
		}
		
		int TK_SC = TK_SemiColon;
		IASTExpressionStatement expressionStatement = null;
		if(initializer instanceof IASTDeclarationStatement) {
			IASTDeclarationStatement declarationStatement = (IASTDeclarationStatement) initializer;
			List<IToken> expressionTokens = stream.getRuleTokens();
			
			//find the first semicolon
			int end_pos = -1;
			for(int i = 0, n = expressionTokens.size(); i < n; i++) {
				if(tokenMap.mapKind(expressionTokens.get(i).getKind()) == TK_SC) {
					end_pos = i;
					break;
				}
			}			
			
			if (end_pos != -1) {	
				expressionTokens = expressionTokens.subList(2, end_pos);
				
				ISecondaryParser<IASTExpression> expressionParser = parserFactory.getExpressionParser(stream, properties);
				IASTExpression expr1 = runSecondaryParser(expressionParser, expressionTokens);
				
				if(expr1 != null) { // the parse may fail
					expressionStatement = nodeFactory.newExpressionStatement(expr1);
					setOffsetAndLength(expressionStatement);
				}
			}
			
			if(expressionStatement == null) 
				initializer = declarationStatement;
			else {
				initializer = createAmbiguousStatement(declarationStatement, expressionStatement);
				setOffsetAndLength(initializer);
			}
		}	
		
		
		IASTForStatement forStat;
		if(condition instanceof IASTExpression)
			forStat = nodeFactory.newForStatement(initializer, (IASTExpression)condition, expr, body);
		else // its a declaration or its null
			forStat = nodeFactory.newForStatement(initializer, (IASTDeclaration)condition, expr, body);
		
		setOffsetAndLength(forStat);
		astStack.push(forStat);
	}
	
	
	/**
	 * try_block
     *     ::= 'try' compound_statement <openscope-ast> handler_seq
     */
	public void consumeStatementTryBlock(boolean hasCatchBlock) {
		List<Object> handlerSeq = hasCatchBlock ? astStack.closeScope() : Collections.emptyList();
		IASTStatement body = (IASTStatement) astStack.pop();
		
		ICPPASTTryBlockStatement tryStatement = nodeFactory.newTryBlockStatement(body);
		
		for(Object handler : handlerSeq)
			tryStatement.addCatchHandler((ICPPASTCatchHandler)handler);
		
		setOffsetAndLength(tryStatement);
		astStack.push(tryStatement);
	}
	
	
	/**
	 * handler
     *     ::= 'catch' '(' exception_declaration ')' compound_statement
     *       | 'catch' '(' '...' ')' compound_statement
	 */
	 public void consumeStatementCatchHandler(boolean hasEllipsis) {
		 IASTStatement body = (IASTStatement) astStack.pop();
		 IASTDeclaration decl = hasEllipsis ? null : (IASTDeclaration) astStack.pop();
		 
		 ICPPASTCatchHandler catchHandler = nodeFactory.newCatchHandler(decl, body);
		 catchHandler.setIsCatchAll(hasEllipsis);

		 setOffsetAndLength(catchHandler);
	     astStack.push(catchHandler);
	 }
	 
	
	/**
	 * nested_name_specifier
     *     ::= class_or_namespace_name '::' nested_name_specifier_with_template
     *       | class_or_namespace_name '::' 
     *
     * nested_name_specifier_with_template
     *     ::= class_or_namespace_name_with_template '::' nested_name_specifier_with_template
     *       | class_or_namespace_name_with_template '::'
     *       
     *        
     * Creates and updates a list of the nested names on the stack.
     * Important: the names in the list are in *reverse* order,
     * this is because the actions fire in reverse order.
	 */
	@SuppressWarnings("unchecked")
	public void consumeNestedNameSpecifier(final boolean hasNested) {
		LinkedList<IASTName> names;
		if(hasNested)
			names = (LinkedList<IASTName>) astStack.pop();
		else
			names = new LinkedList<IASTName>();
		
		IASTName name = (IASTName) astStack.pop();
		names.add(name);
		
		astStack.push(names);
	}

	
	public void consumeNestedNameSpecifierEmpty() {
		// can't use Collections.EMPTY_LIST because we need a list thats mutable
		astStack.push(new LinkedList<IASTName>());
	}
	
	
	
	
	/**
	 * The template keyword is optional but must be the leftmost token.
	 * 
	 * This just throws away the template keyword.
	 */
	public void consumeNameWithTemplateKeyword() { 
		IASTName name = (IASTName) astStack.pop();
		astStack.pop(); // pop the template keyword
		astStack.push(name);
	}

	
	
	/**
	 * qualified_id
     *     ::= dcolon_opt nested_name_specifier any_name
	 */
	public void consumeQualifiedId(boolean hasTemplateKeyword) {
		IASTName qualifiedName = subRuleQualifiedName(hasTemplateKeyword);
		astStack.push(qualifiedName);
	}
	
	
	private IASTName createQualifiedName(LinkedList<IASTName> nestedNames, int startOffset, int endOffset, boolean startsWithColonColon) {
		return createQualifiedName(nestedNames, startOffset, endOffset, startsWithColonColon, false);
	}
	
	
	/**
	 * Creates a qualified name from a list of names (that must be in reverse order).
	 * 
	 * @param names List of name nodes in reverse order
	 */
	private IASTName createQualifiedName(LinkedList<IASTName> names, int startOffset, int endOffset, boolean startsWithColonColon, boolean endsWithColonColon) {
		if(!endsWithColonColon && !startsWithColonColon && names.size() == 1) 
			return names.getFirst(); // its actually an unqualified name

		ICPPASTQualifiedName qualifiedName = nodeFactory.newQualifiedName();
		qualifiedName.setFullyQualified(startsWithColonColon);
		ParserUtil.setOffsetAndLength(qualifiedName, startOffset, endOffset - startOffset);
		for(IASTName name : reverseIterable(names))
			qualifiedName.addName(name);
	
		// there must be a dummy name in the AST after the last double colon, this happens with pointer to member names
		if(endsWithColonColon) {
			IASTName dummyName = nodeFactory.newName();
			ParserUtil.setOffsetAndLength(dummyName, endOffset, 0);
			qualifiedName.addName(dummyName);
		}
		
		return qualifiedName;
	}

	
	
	/**
	 * Consumes grammar sub-rules of the following form:
	 * 
	 * dcolon_opt nested_name_specifier_opt keyword_opt name
	 * 
	 * Where name is any rule that produces an IASTName node on the stack.
	 * Does not place the resulting node on the stack, returns it instead.
	 */
	@SuppressWarnings("unchecked")
	private IASTName subRuleQualifiedName(boolean hasOptionalKeyword) {
		IASTName lastName = (IASTName) astStack.pop();
		
		if(hasOptionalKeyword) // this is usually a template keyword and can be ignored
			astStack.pop();
		
		LinkedList<IASTName> nestedNames = (LinkedList<IASTName>) astStack.pop();
		IToken dColon = (IToken) astStack.pop();
		
		if(nestedNames.isEmpty() && dColon == null) { // then its not a qualified name
			return lastName;
		}

		nestedNames.addFirst(lastName); // the list of names is in reverse order
		
		int startOffset = dColon == null ? offset(nestedNames.getLast()) : offset(dColon);
		int endOffset = endOffset(lastName);
		
		return createQualifiedName(nestedNames, startOffset, endOffset, dColon != null);
	}
	
	
	
	/**
	 * pseudo_destructor_name
     *     ::= dcolon_opt nested_name_specifier_opt type_name '::' destructor_type_name
     *       | dcolon_opt nested_name_specifier 'template' template_id '::' destructor_type_name
     *       | dcolon_opt nested_name_specifier_opt destructor_type_name
     */
	@SuppressWarnings("unchecked")
	public void consumePsudoDestructorName(boolean hasExtraTypeName) {
		IASTName destructorTypeName = (IASTName) astStack.pop();
		IASTName extraName = hasExtraTypeName ? (IASTName) astStack.pop() : null;
		LinkedList<IASTName> nestedNames = (LinkedList<IASTName>) astStack.pop();
		IToken dColon = (IToken) astStack.pop();
		
		if(hasExtraTypeName)
			nestedNames.addFirst(extraName);
		
		nestedNames.addFirst(destructorTypeName);
		
		int startOffset = dColon == null ? offset(nestedNames.getLast()) : offset(dColon);
		int endOffset = endOffset(destructorTypeName);
		IASTName qualifiedName = createQualifiedName(nestedNames, startOffset, endOffset, dColon != null);
		
		setOffsetAndLength(qualifiedName);
		astStack.push(qualifiedName);
	}
	
	
	/**
	 * namespace_alias_definition
     *     ::= 'namespace' 'identifier' '=' dcolon_opt nested_name_specifier_opt namespace_name ';'
     */
	public void consumeNamespaceAliasDefinition() {
		IASTName qualifiedName = subRuleQualifiedName(false);
		
		IASTName alias = createName(stream.getRuleTokens().get(1));
		ICPPASTNamespaceAlias namespaceAlias = nodeFactory.newNamespaceAlias(alias, qualifiedName);
		
		setOffsetAndLength(namespaceAlias);
		astStack.push(namespaceAlias);
	}
	
	
	/**
	 * using_declaration
     *     ::= 'using' typename_opt dcolon_opt nested_name_specifier_opt unqualified_id ';'
	 */
	public void consumeUsingDeclaration() {
		IASTName qualifiedName = subRuleQualifiedName(false);
		boolean hasTypenameKeyword = astStack.pop() == PLACE_HOLDER;
		
		ICPPASTUsingDeclaration usingDeclaration = nodeFactory.newUsingDeclaration(qualifiedName);
		usingDeclaration.setIsTypename(hasTypenameKeyword);
		
		setOffsetAndLength(usingDeclaration);
		astStack.push(usingDeclaration);
	}
	
	
	/**
	 * using_directive
     *     ::= 'using' 'namespace' dcolon_opt nested_name_specifier_opt namespace_name ';'
	 */
	public void consumeUsingDirective() {
		IASTName qualifiedName = subRuleQualifiedName(false);
		
		ICPPASTUsingDirective usingDirective = nodeFactory.newUsingDirective(qualifiedName);
		setOffsetAndLength(usingDirective);
		astStack.push(usingDirective);
	}
	
	
	/**
	 * linkage_specification
     *     ::= 'extern' 'stringlit' '{' <openscope-ast> declaration_seq_opt '}'
     *       | 'extern' 'stringlit' <openscope-ast> declaration
	 */
	public void consumeLinkageSpecification() {
		String name = stream.getRuleTokens().get(1).toString();
		ICPPASTLinkageSpecification linkageSpec = nodeFactory.newLinkageSpecification(name);
		
		for(Object declaration : astStack.closeScope())
			linkageSpec.addDeclaration((IASTDeclaration)declaration);
			
		setOffsetAndLength(linkageSpec);
		astStack.push(linkageSpec);
	}
	
	
	
	/**
	 * original_namespace_definition
     *     ::= 'namespace' identifier_name '{' <openscope-ast> declaration_seq_opt '}'
     *    
     * extension_namespace_definition
     *     ::= 'namespace' original_namespace_name '{' <openscope-ast> declaration_seq_opt '}'
     *
     * unnamed_namespace_definition
     *     ::= 'namespace' '{' <openscope-ast> declaration_seq_opt '}'
	 */
	public void consumeNamespaceDefinition(boolean hasName) {
		List<Object> declarations = astStack.closeScope();
		IASTName namespaceName = hasName ? (IASTName)astStack.pop() : nodeFactory.newName();
		
		ICPPASTNamespaceDefinition definition = nodeFactory.newNamespaceDefinition(namespaceName);
		
		for(Object declaration : declarations)
			definition.addDeclaration((IASTDeclaration)declaration);
		
		setOffsetAndLength(definition);
		astStack.push(definition);
	}
	
	
	/**
	 * template_declaration
     *     ::= export_opt 'template' '<' <openscope-ast> template_parameter_list '>' declaration
	 */
	public void consumeTemplateDeclaration() {
		IASTDeclaration declaration = (IASTDeclaration) astStack.pop();
		
		// For some reason ambiguous declarators cause bugs when they are a part of a template declaration.
		// But it shouldn't be ambiguous anyway, so just throw away the ambiguity node.
		resolveAmbiguousDeclaratorsToFunction(declaration);
		
		ICPPASTTemplateDeclaration templateDeclaration = nodeFactory.newTemplateDeclaration(declaration);
		
		for(Object param : astStack.closeScope())
			templateDeclaration.addTemplateParamter((ICPPASTTemplateParameter)param);

		boolean hasExportKeyword = astStack.pop() == PLACE_HOLDER;
		templateDeclaration.setExported(hasExportKeyword);
		
		setOffsetAndLength(templateDeclaration);
		astStack.push(templateDeclaration);
	}
	
	
	/**
	 * If we know that a declarator must be a function declarator then we can resolve
	 * the ambiguity without resorting to binding resolution.
	 */
	private static void resolveAmbiguousDeclaratorsToFunction(IASTDeclaration declaration) {
		if(declaration instanceof IASTSimpleDeclaration) {
			for(IASTDeclarator declarator : ((IASTSimpleDeclaration)declaration).getDeclarators()) {
				if(declarator instanceof CPPASTAmbiguousDeclarator) {
					IASTAmbiguityParent owner = (IASTAmbiguityParent) declaration;
					CPPASTAmbiguousDeclarator ambiguity = (CPPASTAmbiguousDeclarator)declarator;
					owner.replace(ambiguity, ambiguity.getDeclarators()[0]);
				}
			}
		}
	}
	
	
    /**
     * explicit_instantiation
     *    ::= 'template' declaration
     */
	public void consumeTemplateExplicitInstantiation() {
		IASTDeclaration declaration = (IASTDeclaration) astStack.pop();
		ICPPASTExplicitTemplateInstantiation instantiation = nodeFactory.newExplicitTemplateInstantiation(declaration);
		
		setOffsetAndLength(instantiation);
		astStack.push(instantiation);
	}
	
	
	/**
	 * explicit_specialization
     *     ::= 'template' '<' '>' declaration
     */
	public void consumeTemplateExplicitSpecialization() {
		IASTDeclaration declaration = (IASTDeclaration) astStack.pop();
		ICPPASTTemplateSpecialization specialization = nodeFactory.newTemplateSpecialization(declaration);
		
		setOffsetAndLength(specialization);
		astStack.push(specialization);
	}
	
	
	/**
	 * Sets a token specifier.
	 * Needs to be overrideable for new decl spec keywords.
	 * 
	 * @param token Allows subclasses to override this method and use any
	 * object to determine how to set a specifier.
	 */
	public void setSpecifier(ICPPASTDeclSpecifier node, Object specifier) {
		if(!(specifier instanceof IToken))
			return;
		
		IToken token = (IToken)specifier;
		int kind = baseKind(token);
		switch(kind){
			case TK_typedef:  node.setStorageClass(IASTDeclSpecifier.sc_typedef);    return;
			case TK_extern:   node.setStorageClass(IASTDeclSpecifier.sc_extern);     return;
			case TK_static:   node.setStorageClass(IASTDeclSpecifier.sc_static);     return;
			case TK_auto:     node.setStorageClass(IASTDeclSpecifier.sc_auto);       return;
			case TK_register: node.setStorageClass(IASTDeclSpecifier.sc_register);   return;
			case TK_mutable:  node.setStorageClass(ICPPASTDeclSpecifier.sc_mutable); return;
			
			case TK_inline:   node.setInline(true);   return;
			case TK_const:    node.setConst(true);    return;
			case TK_friend:   node.setFriend(true);   return;
			case TK_virtual:  node.setVirtual(true);  return;
			case TK_volatile: node.setVolatile(true); return;
			case TK_explicit: node.setExplicit(true); return;
		}
		
		if(node instanceof ICPPASTSimpleDeclSpecifier) {
			ICPPASTSimpleDeclSpecifier n = (ICPPASTSimpleDeclSpecifier) node;
			switch(kind) {
				case TK_void:     n.setType(IASTSimpleDeclSpecifier.t_void);       return;
				case TK_char:     n.setType(IASTSimpleDeclSpecifier.t_char);       return;
				case TK_int:      n.setType(IASTSimpleDeclSpecifier.t_int);        return;
				case TK_float:    n.setType(IASTSimpleDeclSpecifier.t_float);      return;
				case TK_double:   n.setType(IASTSimpleDeclSpecifier.t_double);     return;
				case TK_bool:     n.setType(ICPPASTSimpleDeclSpecifier.t_bool);    return;
				case TK_wchar_t:  n.setType(ICPPASTSimpleDeclSpecifier.t_wchar_t); return;
				
				case TK_signed:   n.setSigned(true);   return;
				case TK_unsigned: n.setUnsigned(true); return;
				//if it is a longlong, donot set long, CDT_70_FIX_FROM_50-#8
				case TK_long:     if(!n.isLongLong()) n.setLong(true);     return;
				case TK_short:    n.setShort(true);    return;
			}
		}
	}
	
	
	public void consumeDeclarationSpecifiersSimple() {
		ICPPASTDeclSpecifier declSpec = nodeFactory.newSimpleDeclSpecifier();
		
		for(Object token : astStack.closeScope())
			setSpecifier(declSpec, token);
		
		setOffsetAndLength(declSpec);
		astStack.push(declSpec);
	}

	
	/**
	 * TODO: maybe move this into the superclass
	 */
	public void consumeDeclarationSpecifiersComposite() {
		List<Object> topScope = astStack.closeScope();
		
		// There's already a composite or elaborated or enum type specifier somewhere on the stack, find it.
		ICPPASTDeclSpecifier declSpec = findFirstAndRemove(topScope, ICPPASTDeclSpecifier.class);
		
		// now apply the rest of the specifiers
		for(Object token : topScope)
			setSpecifier(declSpec, token);
		
		setOffsetAndLength(declSpec);
		astStack.push(declSpec);
	}
	
	
	
	
//	/**
//	 * declaration_specifiers ::=  <openscope> type_name_declaration_specifiers
//	 */
	public void consumeDeclarationSpecifiersTypeName() {
		List<Object> topScope = astStack.closeScope();
		// There's a name somewhere on the stack, find it		
		IASTName typeName = findFirstAndRemove(topScope, IASTName.class);
		
		// TODO what does the second argument mean?
		ICPPASTNamedTypeSpecifier declSpec = nodeFactory.newTypedefNameSpecifier(typeName);
		
		// now apply the rest of the specifiers
		for(Object token : topScope) {
			setSpecifier(declSpec, token);
		}
		
		// the only way there could be a typename token
		for(IToken token : stream.getRuleTokens()) {
			if(baseKind(token) == TK_typename) {
				declSpec.setIsTypename(true);
				break;
			}
		}

		setOffsetAndLength(declSpec);
		astStack.push(declSpec);
	}
	
	
	/**
	 * elaborated_type_specifier
     *     ::= class_keyword dcolon_opt nested_name_specifier_opt identifier_name
     *       | class_keyword dcolon_opt nested_name_specifier_opt template_opt template_id_name
     *       | 'enum' dcolon_opt nested_name_specifier_opt identifier_name      
	 */
	public void consumeTypeSpecifierElaborated(boolean hasOptionalTemplateKeyword) {
		IASTName name = subRuleQualifiedName(hasOptionalTemplateKeyword);
		int kind = getElaboratedTypeSpecifier(stream.getLeftIToken());
		
		IASTElaboratedTypeSpecifier typeSpecifier = nodeFactory.newElaboratedTypeSpecifier(kind, name);
		
		setOffsetAndLength(typeSpecifier);
		astStack.push(typeSpecifier);
	}
	
	
	private int getElaboratedTypeSpecifier(IToken token) {
		int kind = baseKind(token);
		switch(kind) {
			default: assert false : "wrong token kind: " + kind; //$NON-NLS-1$
			case TK_struct: return IASTElaboratedTypeSpecifier.k_struct;
			case TK_union:  return IASTElaboratedTypeSpecifier.k_union;
			case TK_enum:   return IASTElaboratedTypeSpecifier.k_enum;
			case TK_class:  return ICPPASTElaboratedTypeSpecifier.k_class;   
		}
	}
	
	
	
	/**
	 * simple_declaration
     *     ::= declaration_specifiers_opt <openscope-ast> init_declarator_list_opt ';'
	 */
	public void consumeDeclarationSimple(boolean hasDeclaratorList) {
		List<Object> declarators = hasDeclaratorList ? astStack.closeScope() : new ArrayList<Object>();
		ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) astStack.pop(); // may be null
		
		List<IToken> ruleTokens = stream.getRuleTokens();
		IToken nameToken = null;
		
		
		// do not generate nodes for extra EOC tokens
		if(matchTokens(ruleTokens, tokenMap, TK_EndOfCompletion)) {
			return;
		}
		
		// In the case that a single completion token is parsed then it needs
		// to be interpreted as a named type specifier for content assist to work.
		else if(matchTokens(ruleTokens, tokenMap, TK_Completion, TK_EndOfCompletion)) {
			IASTName name = createName(stream.getLeftIToken());
			declSpec = nodeFactory.newTypedefNameSpecifier(name);
			ParserUtil.setOffsetAndLength(declSpec, offset(name), length(name));
			declarators = new ArrayList<Object>(); // throw away the bogus declarator
		}
		
		// can happen if implicit int is used
		else if(declSpec == null) { 
			declSpec = nodeFactory.newSimpleDeclSpecifier();
			ParserUtil.setOffsetAndLength(declSpec, stream.getLeftIToken().getStartOffset(), 0);
		}
		
		
		else if(declarators.size() == 1 && disambiguateToConstructor(declSpec, (IASTDeclarator)declarators.get(0))) { // puts results of disambiguation onto stack
			declSpec = (ICPPASTDeclSpecifier) astStack.pop(); 
			declarators = Arrays.asList(astStack.pop());
		}
		
		// bug 80171, check for situation similar to: static var;
		// this will get parsed wrong, the following is a hack to rebuild the AST as it should have been parsed
		// exclude syntax "friend xxx"
		else if(declarators.isEmpty() && 
		   declSpec instanceof ICPPASTNamedTypeSpecifier && ! declSpec.isFriend() &&
		   ruleTokens.size() >= 2 &&
		   baseKind(nameToken = ruleTokens.get(ruleTokens.size() - 2)) == TK_identifier) {
			
			declSpec = nodeFactory.newSimpleDeclSpecifier();
			for(IToken t : ruleTokens.subList(0, ruleTokens.size()-1))
				setSpecifier(declSpec, t);
			
			int offset = offset(stream.getLeftIToken());
			int length = endOffset(ruleTokens.get(ruleTokens.size()-2)) - offset;
			ParserUtil.setOffsetAndLength(declSpec, offset, length);
			
			IASTName name = createName(nameToken);
			IASTDeclarator declarator = nodeFactory.newDeclarator(name);
			ParserUtil.setOffsetAndLength(declarator, nameToken);
			declarators.add(declarator);
		}

		IASTSimpleDeclaration declaration = nodeFactory.newSimpleDeclaration(declSpec);
		setOffsetAndLength(declaration);
		for(Object declarator : declarators)
			declaration.addDeclarator((IASTDeclarator)declarator);
				
		// simple ambiguity resolutions
//		if(declSpecifier.isFriend())
//			resolveAmbiguousDeclaratorsToFunction(declaration);
//		
//		if(declSpecifier instanceof IASTSimpleDeclSpecifier) {
//			IASTSimpleDeclSpecifier simple = (IASTSimpleDeclSpecifier) declSpecifier;
//			if(simple.getType() == IASTSimpleDeclSpecifier.t_void && declaration.getDeclarators()[0].getPointerOperators().length == 0)
//				resolveAmbiguousDeclaratorsToFunction(declaration);
//			
//		}
		
		astStack.push(declaration);
	}
	
	

	private boolean disambiguateToConstructor(IASTDeclSpecifier declSpec, IASTDeclarator declarator) {
		if(!(declSpec instanceof IASTNamedTypeSpecifier))
			return false;
			
		IASTNamedTypeSpecifier namedTypeSpecifier = (IASTNamedTypeSpecifier) declSpec;
		IASTName name = namedTypeSpecifier.getName();
		IASTDeclarator nested = declarator.getNestedDeclarator();
		
		ICPPASTSimpleDeclSpecifier simpleDeclSpec = nodeFactory.newSimpleDeclSpecifier(); // empty
		ParserUtil.setOffsetAndLength(simpleDeclSpec, stream.getLeftIToken().getStartOffset(), 0);
		
		if(!classNames.isEmpty() && nested != null && ParserUtil.isSameName(name, classNames.getLast())) {

			IASTName paramTypeName = nested.getName();  // reuse the parameter name node
			IASTNamedTypeSpecifier paramName = nodeFactory.newTypedefNameSpecifier(paramTypeName);
			ParserUtil.setOffsetAndLength(paramName, paramTypeName);
			
			IASTDeclarator paramDeclarator = nodeFactory.newDeclarator(nodeFactory.newName());
			ParserUtil.setOffsetAndLength(paramDeclarator, offset(paramName) + length(paramName), 0);
			
			ICPPASTParameterDeclaration parameter = nodeFactory.newParameterDeclaration(paramName, paramDeclarator);
			ParserUtil.setOffsetAndLength(parameter, paramName);
			
			ICPPASTFunctionDeclarator constructorDeclarator = nodeFactory.newFunctionDeclarator(name); // reuse the name node
			constructorDeclarator.addParameterDeclaration(parameter);
			ParserUtil.setOffsetAndLength(constructorDeclarator, offset(simpleDeclSpec), endOffset(paramDeclarator) - offset(simpleDeclSpec) + 1);
			
			astStack.push(constructorDeclarator);
			astStack.push(simpleDeclSpec);
			return true;
		}
		
		if(declarator instanceof IASTFunctionDeclarator && declarator.getName() instanceof ICPPASTQualifiedName) {
			ICPPASTQualifiedName qualifiedName = (ICPPASTQualifiedName) declarator.getName();
			//IASTName lastName = qualifiedName.getLastName();
			
			if(qualifiedName.isFullyQualified()) {
				
				ICPPASTQualifiedName newQualifiedName = nodeFactory.newQualifiedName();
				newQualifiedName.addName(name);
				for(IASTName n : qualifiedName.getNames())
					newQualifiedName.addName(n);
				
				ParserUtil.setOffsetAndLength(newQualifiedName, offset(name), endOffset(qualifiedName.getLastName()) - offset(name));
				
				
				declarator.setName(newQualifiedName);
				ParserUtil.setOffsetAndLength(declarator, offset(name), length(declarator) + offset(declarator) - offset(name));
				
				astStack.push(declarator);
				astStack.push(simpleDeclSpec);
				return true;
			}
		}
		
		return false;
	}
	

		
	public void consumeInitDeclaratorComplete() {
		// Don't do disambiguation when parsing for content assist,
		// trust me this makes things work out a lot better.
		if(completionNode != null)
			return;
		
		IASTDeclarator declarator = (IASTDeclarator) astStack.peek();
		if(!(declarator instanceof IASTFunctionDeclarator))
			return;
		
		ISecondaryParser<IASTDeclarator> secondaryParser = parserFactory.getNoFunctionDeclaratorParser(stream, properties); 
		IASTDeclarator notFunctionDeclarator = runSecondaryParser(secondaryParser);
	
		if(notFunctionDeclarator == null)
			return;
		
		astStack.pop();

		IASTNode ambiguityNode = new CPPASTAmbiguousDeclarator(declarator, notFunctionDeclarator);

		setOffsetAndLength(ambiguityNode);
		astStack.push(ambiguityNode); 
	}
	
	
	
	/**
	 * visibility_label
     *     ::= access_specifier_keyword ':'
	 */
	public void consumeVisibilityLabel() {
		IToken specifier = (IToken)astStack.pop();
		int visibility = getAccessSpecifier(specifier);
		ICPPASTVisibilityLabel visibilityLabel = nodeFactory.newVisibilityLabel(visibility);
		setOffsetAndLength(visibilityLabel);
		astStack.push(visibilityLabel);
	}
	
	
	private int getAccessSpecifier(IToken token) {
		int kind = baseKind(token);
		switch(kind) {
			default: assert false : "wrong token kind: " + kind; //$NON-NLS-1$
			case TK_private:   return ICPPASTVisibilityLabel.v_private;
			case TK_public:    return ICPPASTVisibilityLabel.v_public;    
			case TK_protected: return ICPPASTVisibilityLabel.v_protected;
		}
	}
	
	
	/**
	 * base_specifier
     *     ::= dcolon_opt nested_name_specifier_opt class_name
     *       | 'virtual' access_specifier_keyword_opt dcolon_opt nested_name_specifier_opt class_name
     *       | access_specifier_keyword 'virtual' dcolon_opt nested_name_specifier_opt class_name
     *       | access_specifier_keyword dcolon_opt nested_name_specifier_opt class_name
	 */
	public void consumeBaseSpecifier(boolean hasAccessSpecifier, boolean isVirtual) {
		IASTName name = subRuleQualifiedName(false);
		
		int visibility = 0; // this is the default value that the DOM parser uses
		if(hasAccessSpecifier) {
			IToken accessSpecifierToken = (IToken) astStack.pop();
			if(accessSpecifierToken != null)
				visibility = getAccessSpecifier(accessSpecifierToken);
		}
		
		ICPPASTBaseSpecifier baseSpecifier = nodeFactory.newBaseSpecifier(name, visibility, isVirtual);
		setOffsetAndLength(baseSpecifier);
		astStack.push(baseSpecifier);
	}
		
	
	/**
	 * class_specifier
     *     ::= class_head '{' <openscope-ast> member_declaration_list_opt '}'
     */
	public void consumeClassSpecifier() {
		List<Object> declarations = astStack.closeScope();
		
		// the class specifier is created by the rule for class_head
		IASTCompositeTypeSpecifier classSpecifier = (IASTCompositeTypeSpecifier) astStack.peek();
		
		for(Object declaration : declarations)
			classSpecifier.addMemberDeclaration((IASTDeclaration)declaration);
		
		setOffsetAndLength(classSpecifier);
		
		classNames.removeLast(); // pop the stack of class names
	}
	
	
	/**
     * class_head
     *     ::= class_keyword identifier_name_opt <openscope-ast> base_clause_opt
     *       | class_keyword template_id <openscope-ast> base_clause_opt
     *       | class_keyword nested_name_specifier identifier_name <openscope-ast> base_clause_opt
     *       | class_keyword nested_name_specifier template_id <openscope-ast> base_clause_opt
	 */
	@SuppressWarnings("unchecked")
	public void consumeClassHead(boolean hasNestedNameSpecifier) {
		int key = getCompositeTypeSpecifier(stream.getLeftIToken());
		List<Object> baseSpecifiers = astStack.closeScope();
		// may be null, but if it is then hasNestedNameSpecifier == false
		IASTName className = (IASTName) astStack.pop();
		
		if(hasNestedNameSpecifier) {
			LinkedList<IASTName> nestedNames = (LinkedList<IASTName>) astStack.pop();
			nestedNames.addFirst(className);
			int startOffset = offset(nestedNames.getLast());
			int endOffset = endOffset(className);
			className = createQualifiedName(nestedNames, startOffset, endOffset, false);
		}

		if(className == null)
			className = nodeFactory.newName();
		
		ICPPASTCompositeTypeSpecifier classSpecifier = nodeFactory.newCompositeTypeSpecifier(key, className);
		
		for(Object base : baseSpecifiers)
			classSpecifier.addBaseSpecifier((ICPPASTBaseSpecifier)base);
		
		// the offset and length are set in consumeClassSpecifier()
		astStack.push(classSpecifier);
		classNames.add(className); // push
	}
	
	
	private int getCompositeTypeSpecifier(IToken token) {
		final int kind = baseKind(token);
		switch(kind) {
			default: assert false : "wrong token kind: " + kind; //$NON-NLS-1$
			case TK_struct: return IASTCompositeTypeSpecifier.k_struct;
			case TK_union:  return IASTCompositeTypeSpecifier.k_union;
			case TK_class:  return ICPPASTCompositeTypeSpecifier.k_class;   
		}
	}
	
	
	/**
	 * ptr_operator
     *     ::= '*' <openscope-ast> cv_qualifier_seq_opt
     */
    public void consumePointer() {
    	IASTPointer pointer = nodeFactory.newPointer();
    	List<Object> tokens = astStack.closeScope();
    	addCVQualifiersToPointer(pointer, tokens);
		setOffsetAndLength(pointer);
		astStack.push(pointer);
    }
    
    
    protected void addCVQualifiersToPointer(IASTPointer pointer, List<Object> tokens) {
    	for(Object t : tokens) {
    		switch(baseKind((IToken) t)) {
				case TK_const:    pointer.setConst(true);    break;
				case TK_volatile: pointer.setVolatile(true); break;
			}
		}
    }
    
    /**
	 * ptr_operator
     *     ::= '&'
     */ 
    public void consumeReferenceOperator() {
        ICPPASTReferenceOperator referenceOperator = nodeFactory.newReferenceOperator();
        setOffsetAndLength(referenceOperator);
		astStack.push(referenceOperator);
    }
    
    
    /**
	 * ptr_operator
     *     ::= dcolon_opt nested_name_specifier '*' <openscope-ast> cv_qualifier_seq_opt
     */
     @SuppressWarnings("unchecked")
	public void consumePointerToMember() {
    	 List<Object> qualifiers = astStack.closeScope();
    	 LinkedList<IASTName> nestedNames = (LinkedList<IASTName>) astStack.pop();
    	 IToken dColon = (IToken) astStack.pop();
    	 
    	 int startOffset = dColon == null ? offset(nestedNames.getLast()) : offset(dColon);
    	 int endOffset   = endOffset(nestedNames.getFirst()); // temporary
    	 
    	 // find the last double colon by searching for it
    	 for(IToken t : reverseIterable(stream.getRuleTokens())) {
    		 if(baseKind(t) == TK_ColonColon) {
    			 endOffset = endOffset(t);
    			 break;
    		 }
    	 }
    	 
    	 IASTName name = createQualifiedName(nestedNames, startOffset, endOffset, dColon != null, true);
    	 
     	 ICPPASTPointerToMember pointer = nodeFactory.newPointerToMember(name);
     	 addCVQualifiersToPointer(pointer, qualifiers);
     	 setOffsetAndLength(pointer);
		 astStack.push(pointer);
     }

     
     
     /**
      * initializer
      *     ::= '(' expression_list ')'
      */
     public void consumeInitializerConstructor() {
    	 //CDT_70_FIX_FROM_50-#5
     	 Object o = astStack.pop();
    	 IASTInitializerClause[] initClauseList =null;
    	 if(o instanceof IASTExpressionList){
    		 initClauseList = ((IASTExpressionList) o).getExpressions();
    	 }else if(o instanceof IASTInitializerClause){
    		 initClauseList = new IASTInitializerClause[]{(IASTInitializerClause)o};
    	 }
    	 
    	 ICPPASTConstructorInitializer initializer = nodeFactory.newConstructorInitializer(initClauseList);
    	 setOffsetAndLength(initializer);
		 astStack.push(initializer);
     }
     
     
    /**
 	 * function_direct_declarator
     *     ::= basic_direct_declarator '(' <openscope-ast> parameter_declaration_clause ')' 
     *         <openscope-ast> cv_qualifier_seq_opt <openscope-ast> exception_specification_opt
 	 */
 	public void consumeDirectDeclaratorFunctionDeclarator(boolean hasDeclarator) {
 		IASTName name = nodeFactory.newName();
 		ICPPASTFunctionDeclarator declarator = nodeFactory.newFunctionDeclarator(name);
 		
 		List<Object> typeIds = astStack.closeScope();
 		if(typeIds.size() == 1 && typeIds.get(0) == PLACE_HOLDER) { // fix for bug 86943
 			declarator.setEmptyExceptionSpecification(); 
 		}
 		else {
	 		for(Object typeId : typeIds) {
	 			declarator.addExceptionSpecificationTypeId((IASTTypeId) typeId);
	 		}
 		}
 		
 		for(Object token : astStack.closeScope()) {
 			int kind = baseKind((IToken)token);
 			switch(kind) {
 				default: assert false : "wrong token kind: " + kind; //$NON-NLS-1$
 				case TK_const:    declarator.setConst(true); break;
 				case TK_volatile: declarator.setVolatile(true); break;
 			}
 		}
 		
 		boolean isVarArgs = astStack.pop() == PLACE_HOLDER;
 		declarator.setVarArgs(isVarArgs);
 			
 		for(Object o : astStack.closeScope()) {
 			declarator.addParameterDeclaration((IASTParameterDeclaration)o);
 		}
 		
 		if(hasDeclarator) {
 			int endOffset = endOffset(stream.getRightIToken());
 			addFunctionModifier(declarator, endOffset);
 		}
 		else {
 			setOffsetAndLength(declarator);
			astStack.push(declarator);
 		}
 	}
 
 	
 	/**
 	 * Consume an empty bracketed abstract declarator.
 	 */
 	public void consumeAbstractDeclaratorEmpty() {
 		IASTName name = nodeFactory.newName();
 		ParserUtil.setOffsetAndLength(name, offset(stream.getLeftIToken())+1, 0);
 		IASTDeclarator declarator = nodeFactory.newDeclarator(name);
 		setOffsetAndLength(declarator);
 		astStack.push(declarator);
 	}
 	
 	
 	/**
 	 * mem_initializer
     *     ::= mem_initializer_id '(' expression_list_opt ')'
 	 */
 	public void consumeConstructorChainInitializer() {
		Object o = astStack.pop();
		IASTName name = (IASTName) astStack.pop();
		IASTInitializerClause[] initClauseList =null;
		if(o instanceof IASTExpressionList){
			initClauseList = ((IASTExpressionList) o).getExpressions();
		}else if(o instanceof IASTInitializerClause){
			initClauseList = new IASTInitializerClause[]{(IASTInitializerClause)o};
		}
		ICPPASTConstructorInitializer init = nodeFactory.newConstructorInitializer(initClauseList);
		int rule_start_offset = stream.getLeftIToken().getStartOffset();
		int initClauseList_offset = ParserUtil.offset(initClauseList[0]);
		List<IToken> ruleTokens = stream.getRuleTokens();
		int start_offset = -1;
		
		for (int i = initClauseList_offset, n = rule_start_offset; i >= n; i--){
			if(tokenMap.mapKind(ruleTokens.get(i).getKind()) == TK_LeftParen) {
				start_offset = ruleTokens.get(i).getStartOffset();
				break;
			}
		}
		int ruleLength = stream.getRightIToken().getEndOffset() - start_offset;
		ParserUtil.setOffsetAndLength(init, start_offset, ruleLength < 0 ? 0 : ruleLength);
 		
 		ICPPASTConstructorChainInitializer initializer = nodeFactory.newConstructorChainInitializer(name, init);
 		setOffsetAndLength(initializer);
		astStack.push(initializer);
 	}
 	
 	
 	
 	/**
 	 * function_definition
     *     ::= declaration_specifiers_opt function_direct_declarator 
     *         <openscope-ast> ctor_initializer_list_opt function_body
     *         
     *       | declaration_specifiers_opt function_direct_declarator 
     *         'try' <openscope-ast> ctor_initializer_list_opt function_body <openscope-ast> handler_seq
     *         
 	 */
 	public void consumeFunctionDefinition(boolean isTryBlockDeclarator) {
 		List<Object> handlers = isTryBlockDeclarator ? astStack.closeScope() : Collections.emptyList();
 		IASTCompoundStatement body = (IASTCompoundStatement) astStack.pop();
 		List<Object> initializers = astStack.closeScope();
 		Object o = astStack.pop();
 		IASTFunctionDeclarator declarator = (IASTFunctionDeclarator) o;
 		Object o2 = astStack.pop();
 		IASTDeclSpecifier declSpec = (IASTDeclSpecifier) o2; // may be null
 		
 		if(declSpec == null) { // can happen if implicit int is used
 			declSpec = nodeFactory.newSimpleDeclSpecifier();
 			ParserUtil.setOffsetAndLength(declSpec, stream.getLeftIToken().getStartOffset(), 0);
		}
 		else if(disambiguateToConstructor(declSpec, declarator)) {
 			declSpec = (IASTDeclSpecifier) astStack.pop(); 
			declarator = (IASTFunctionDeclarator) astStack.pop();
 		}
 		
 		ICPPASTFunctionDefinition definition;
 		if (isTryBlockDeclarator) {
 			ICPPASTFunctionWithTryBlock tryblock= nodeFactory.newFunctionTryBlock(declSpec, declarator, body);
 			for(Object handler : handlers)
 				tryblock.addCatchHandler((ICPPASTCatchHandler)handler);
 			definition = tryblock;
 		} else {
 			definition = nodeFactory.newFunctionDefinition(declSpec, declarator, body);
 		}
 		
 		
 		if(initializers != null && !initializers.isEmpty()) {
 			for(Object initializer : initializers)
 	 			definition.addMemberInitializer((ICPPASTConstructorChainInitializer)initializer);
 		}
 		
 		setOffsetAndLength(definition);
		astStack.push(definition);
 	}
 	
 	
 	/**
 	 * member_declaration
 	 *     ::= dcolon_opt nested_name_specifier template_opt unqualified_id_name ';'
 	 */
 	public void consumeMemberDeclarationQualifiedId() {
 		IASTName qualifiedId = subRuleQualifiedName(true);
 		
 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=92793
 		ICPPASTUsingDeclaration declaration = nodeFactory.newUsingDeclaration(qualifiedId);
 		setOffsetAndLength(declaration);
 		
 		astStack.push(declaration);
 	}
 	
 	
 	/**
 	 * member_declarator
     *     ::= declarator constant_initializer
 	 */
 	
    public void consumeMemberDeclaratorWithInitializer() {
    	
    	//CDT_70_FIX_FROM_50-#2
    	//IASTInitializerExpression initializer = (IASTInitializerExpression) astStack.pop();
    	IASTEqualsInitializer initializer = (IASTEqualsInitializer) astStack.pop();
    	IASTDeclarator declarator = (IASTDeclarator) astStack.peek();
    	setOffsetAndLength(declarator);
    	
    	if(declarator instanceof ICPPASTFunctionDeclarator) {
    		IASTExpression expr = (IASTExpression)initializer.getInitializerClause();
    		if(expr instanceof IASTLiteralExpression && "0".equals(expr.toString())) { //$NON-NLS-1$
    			((ICPPASTFunctionDeclarator)declarator).setPureVirtual(true);
    			return;
    		}
    	}
    	
    	declarator.setInitializer(initializer);
    }

    
    /**
     * type_parameter
     *     ::= 'class' identifier_name_opt -- simple type template parameter     
     *       | 'class' identifier_name_opt '=' type_id
     *       | 'typename' identifier_name_opt
     *       | 'typename' identifier_name_opt '=' type_id
     */
    public void consumeSimpleTypeTemplateParameter(boolean hasTypeId) {
    	IASTTypeId typeId = hasTypeId ? (IASTTypeId)astStack.pop() : null;
    	
    	IASTName name = (IASTName)astStack.pop();
    	if(name == null)
    		name = nodeFactory.newName();
    	
    	int type = getTemplateParameterType(stream.getLeftIToken()); 
    	
    	ICPPASTSimpleTypeTemplateParameter templateParameter = nodeFactory.newSimpleTypeTemplateParameter(type, name, typeId);
    	
    	setOffsetAndLength(templateParameter);
		astStack.push(templateParameter);
    }
    
    
    private int getTemplateParameterType(IToken token) {
    	int kind = baseKind(token);
    	switch(kind) {
    		default: assert false : "wrong token kind: " + kind; //$NON-NLS-1$
    		case TK_class:    return ICPPASTSimpleTypeTemplateParameter.st_class;
    		case TK_typename: return ICPPASTSimpleTypeTemplateParameter.st_typename;
    	}
    }
    
    
    /**
     * Simple type template parameters using the 'class' keyword are being parsed
     * wrong due to an ambiguity between type_parameter and parameter_declaration.
     * 
     * eg) template <class T>
     * 
     * The 'class T' part is being parsed as an elaborated type specifier instead
     * of a simple type template parameter.
     * 
     * This method detects the incorrect parse, throws away the incorrect AST fragment,
     * and replaces it with the correct AST fragment.
     * 
     * Yes its a hack.
     */
    public void consumeTemplateParamterDeclaration() {
    	ISecondaryParser<ICPPASTTemplateParameter> typeParameterParser = parserFactory.getTemplateTypeParameterParser(stream, properties);
    	IASTNode alternate = runSecondaryParser(typeParameterParser);
    	
		if(alternate == null)
			return;
		
		astStack.pop(); // throw away the incorrect AST
		astStack.push(alternate);  // replace it with the correct AST
    }

    
    
    /**
     * type_parameter
     *     ::= 'template' '<' <openscope-ast> template_parameter_list '>' 'class' identifier_name_opt
     *       | 'template' '<' <openscope-ast> template_parameter_list '>' 'class' identifier_name_opt '=' id_expression
     * @param hasIdExpr
     */
    public void consumeTemplatedTypeTemplateParameter(boolean hasIdExpr) {
    	IASTExpression idExpression = hasIdExpr ? (IASTExpression)astStack.pop() : null;
    	IASTName name = (IASTName) astStack.pop();
    	
    	ICPPASTTemplatedTypeTemplateParameter templateParameter = nodeFactory.newTemplatedTypeTemplateParameter(name, idExpression);
    	
    	for(Object param : astStack.closeScope())
    		templateParameter.addTemplateParamter((ICPPASTTemplateParameter)param);
    	
    	setOffsetAndLength(templateParameter);
		astStack.push(templateParameter);
    }


	@Override
	protected IASTAmbiguousExpression createAmbiguousExpression(IASTExpression... expressions) {
		return new CPPASTAmbiguousExpression(expressions);
	}


	@Override
	protected IASTAmbiguousStatement createAmbiguousStatement(IASTStatement... statements) {
		return new CPPASTAmbiguousStatement(statements);
	}
}