/*******************************************************************************
 * Copyright (c) 2006, 2013 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * 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.endOffset;
import static org.eclipse.cdt.core.dom.lrparser.action.ParserUtil.length;
import static org.eclipse.cdt.core.dom.lrparser.action.ParserUtil.matchTokens;
import static org.eclipse.cdt.core.dom.lrparser.action.ParserUtil.offset;
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.TK_ColonColon;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_Completion;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_EndOfCompletion;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_LeftBracket;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_LeftParen;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_RightBracket;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_RightParen;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_SemiColon;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_auto;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_bool;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_char;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_class;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_const;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_delete;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_double;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_enum;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_explicit;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_extern;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_float;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_for;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_friend;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_identifier;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_inline;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_int;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_long;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_mutable;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_new;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_private;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_protected;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_public;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_register;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_short;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_signed;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_static;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_struct;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_typedef;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_typename;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_union;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_unsigned;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_virtual;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_void;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_volatile;
import static org.eclipse.cdt.internal.core.dom.lrparser.cpp.CPPParsersym.TK_wchar_t;

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

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.ICPPASTNameSpecifier;
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;

import lpg.lpgjavaruntime.IToken;

/**
 * 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<>();

	/**
	 * @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 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<>();
		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<>(); // 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 (ICPPASTNameSpecifier n : qualifiedName.getQualifier())
					newQualifiedName.addNameSpecifier(n);
				newQualifiedName.addName(qualifiedName.getLastName());

				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);
	}
}