/*******************************************************************************
 * Copyright (c) 2006, 2016 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;

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

import java.util.List;

import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTBreakStatement;
import org.eclipse.cdt.core.dom.ast.IASTCaseStatement;
import org.eclipse.cdt.core.dom.ast.IASTCastExpression;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression;
import org.eclipse.cdt.core.dom.ast.IASTContinueStatement;
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.IASTDefaultStatement;
import org.eclipse.cdt.core.dom.ast.IASTDoStatement;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator;
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.IASTFieldDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTGotoStatement;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTInitializerList;
import org.eclipse.cdt.core.dom.ast.IASTLabelStatement;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNullStatement;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTProblemHolder;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
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.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCastExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLiteralExpression;
import org.eclipse.cdt.core.dom.lrparser.ISecondaryParser;
import org.eclipse.cdt.core.dom.lrparser.LRParserProperties;
import org.eclipse.cdt.core.dom.parser.IBuiltinBindingsProvider;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.internal.core.dom.parser.ASTInternal;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit;
import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousExpression;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousStatement;

import lpg.lpgjavaruntime.IToken;

/**
 * Parser semantic actions that are common to both C and C++.
 *
 * @author Mike Kucera
 */
@SuppressWarnings("restriction")
public abstract class BuildASTParserAction extends AbstractParserAction {

	/** Abstract factory for creating AST node objects */
	private final INodeFactory nodeFactory;

	/** Abstract factory for creating secondary parsers */
	private final ISecondaryParserFactory parserFactory;

	/**
	 * Returns true if the token is an identifier.
	 */
	protected abstract boolean isIdentifierToken(IToken token);

	protected IASTTranslationUnit tu = null;

	/**
	 * Create a new parser action.
	 * @param tu Root node of the AST, its list of declarations should be empty.
	 * @throws NullPointerException if any of the parameters are null
	 */
	public BuildASTParserAction(ITokenStream parser, ScopedStack<Object> astStack, INodeFactory nodeFactory,
			ISecondaryParserFactory parserFactory) {
		super(parser, astStack);

		if (nodeFactory == null)
			throw new NullPointerException("nodeFactory is null"); //$NON-NLS-1$
		if (parserFactory == null)
			throw new NullPointerException("parserFactory is null"); //$NON-NLS-1$

		this.nodeFactory = nodeFactory;
		this.parserFactory = parserFactory;
	}

	public void initializeTranslationUnit(IScanner scanner, IBuiltinBindingsProvider builtinBindingsProvider,
			IIndex index) {
		tu = nodeFactory.newTranslationUnit(scanner);
		tu.setIndex(index);

		// add built-in names to the scope
		if (builtinBindingsProvider != null) {
			IScope tuScope = tu.getScope();
			IBinding[] bindings = builtinBindingsProvider.getBuiltinBindings(tuScope);
			for (IBinding binding : bindings) {
				ASTInternal.addBinding(tuScope, binding);
			}
		}

		if (tu instanceof ASTTranslationUnit) {
			((ASTTranslationUnit) tu).setLocationResolver(scanner.getLocationResolver());
		}
	}

	public void consumeTranslationUnit() {
		if (tu == null)
			tu = nodeFactory.newTranslationUnit(null);

		// can't close the outermost scope
		for (Object o : astStack.topScope()) {
			tu.addDeclaration((IASTDeclaration) o);
		}
		while (!astStack.isEmpty()) {
			astStack.pop();
		}

		// this is the same way that the DOM parser computes the length
		IASTDeclaration[] declarations = tu.getDeclarations();
		if (declarations.length != 0) {
			IASTNode d = declarations[declarations.length - 1];
			ParserUtil.setOffsetAndLength(tu, 0, offset(d) + length(d));
		}

		resolveAmbiguityNodes(tu);
		tu.freeze();

		astStack.push(tu);
	}

	@Override
	public ASTCompletionNode newCompletionNode(String prefix) {
		return new ASTCompletionNode(prefix, tu);
	}

	/**
	 * Removes ambiguity nodes from the AST by resolving them.
	 *
	 * @see AbstractGNUSourceCodeParser#resolveAmbiguities()
	 */
	private static void resolveAmbiguityNodes(IASTTranslationUnit tu) {
		if (tu instanceof ASTTranslationUnit) {
			((ASTTranslationUnit) tu).resolveAmbiguities();
		}
	}

	/**
	 * Consumes a single identifier token.
	 */
	public void consumeIdentifierName() {
		astStack.push(createName(stream.getRightIToken()));
	}

	/**
	 * block_item ::= declaration | statement
	 *
	 * TODO, be careful where exactly in the grammar this is called, it may be called unnecessarily
	 */
	public void consumeStatementDeclarationWithDisambiguation() {
		IASTDeclaration decl = (IASTDeclaration) astStack.pop();
		IASTDeclarationStatement declarationStatement = nodeFactory.newDeclarationStatement(decl);
		setOffsetAndLength(declarationStatement);

		// attempt to also parse the tokens as an expression
		IASTExpressionStatement expressionStatement = null;
		if (decl instanceof IASTSimpleDeclaration) {
			List<IToken> expressionTokens = stream.getRuleTokens();
			expressionTokens = expressionTokens.subList(0, expressionTokens.size() - 1); // remove the semicolon at the end

			ISecondaryParser<IASTExpression> expressionParser = parserFactory.getExpressionParser(stream, properties);
			IASTExpression expr = runSecondaryParser(expressionParser, expressionTokens);

			if (expr != null) { // the parse may fail
				expressionStatement = nodeFactory.newExpressionStatement(expr);
				setOffsetAndLength(expressionStatement);
			}
		}

		List<IToken> tokens = stream.getRuleTokens();

		IASTNode result;
		if (expressionStatement == null)
			result = declarationStatement;
		else if (expressionStatement.getExpression() instanceof IASTFunctionCallExpression)
			result = expressionStatement;
		else if (tokens.size() == 2 && (isCompletionToken(tokens.get(0)) || isIdentifierToken(tokens.get(0)))) // identifier followed by semicolon
			result = expressionStatement;
		else if (isImplicitInt(decl))
			result = expressionStatement;
		else {
			result = createAmbiguousStatement(declarationStatement, expressionStatement);
			setOffsetAndLength(result);
		}

		astStack.push(result);
	}

	protected abstract IASTAmbiguousStatement createAmbiguousStatement(IASTStatement... statements);

	/**
	 * Wrap a declaration in a DeclarationStatement.
	 */
	public void consumeStatementDeclaration() {
		IASTDeclaration decl = (IASTDeclaration) astStack.pop();
		IASTDeclarationStatement declarationStatement = nodeFactory.newDeclarationStatement(decl);
		setOffsetAndLength(declarationStatement);
		astStack.push(declarationStatement);
	}

	/**
	 * Returns true if the given declaration has unspecified type,
	 * in this case the type defaults to int and is know as "implicit int".
	 *
	 * With implicit int a lot of language constructs can be accidentally parsed
	 * as declarations:
	 *
	 * eg) x = 1;
	 * Should be an assignment statement but can also be parsed as a declaration
	 * of a variable x, of unspecified type, initialized to 1.
	 *
	 * These cases are easy to detect (using this method) and the wrong interpretation
	 * as a declaration is discarded.
	 */
	protected static boolean isImplicitInt(IASTDeclaration declaration) {
		if (declaration instanceof IASTSimpleDeclaration) {
			IASTDeclSpecifier declSpec = ((IASTSimpleDeclaration) declaration).getDeclSpecifier();
			if (declSpec instanceof IASTSimpleDeclSpecifier
					&& ((IASTSimpleDeclSpecifier) declSpec).getType() == IASTSimpleDeclSpecifier.t_unspecified) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @param kind One of the kind flags from IASTLiteralExpression or ICPPASTLiteralExpression
	 * @see IASTLiteralExpression
	 * @see ICPPASTLiteralExpression
	 */
	public void consumeExpressionLiteral(int kind) {
		IToken token = stream.getRightIToken();
		String rep = token.toString();

		// Strip the quotes from string literals, this is just to be consistent
		// with the dom parser (i.e. to make a test pass)
		//		if(kind == IASTLiteralExpression.lk_string_literal &&
		//				rep.startsWith("\"") && rep.endsWith("\"")) {
		//			rep = rep.substring(1, rep.length()-1);
		//		}

		IASTLiteralExpression expr = nodeFactory.newLiteralExpression(kind, rep);
		ParserUtil.setOffsetAndLength(expr, token);
		astStack.push(expr);
	}

	public void consumeExpressionBracketed() {
		IASTExpression operand = (IASTExpression) astStack.pop();
		IASTUnaryExpression expr = nodeFactory.newUnaryExpression(IASTUnaryExpression.op_bracketedPrimary, operand);
		setOffsetAndLength(expr);
		astStack.push(expr);
	}

	public void consumeExpressionID() {
		IASTName name = createName(stream.getLeftIToken());
		IASTIdExpression expr = nodeFactory.newIdExpression(name);
		setOffsetAndLength(expr);
		astStack.push(expr);
	}

	public void consumeExpressionName() {
		IASTName name = (IASTName) astStack.pop();
		IASTIdExpression expr = nodeFactory.newIdExpression(name);
		setOffsetAndLength(expr);
		astStack.push(expr);
	}

	/**
	 * expression ::= <openscope-ast> expression_list_actual
	 */
	public void consumeExpressionList() {
		List<Object> expressions = astStack.closeScope();
		if (expressions.size() == 1) {
			astStack.push(expressions.get(0));
		} else {
			IASTExpressionList exprList = nodeFactory.newExpressionList();

			for (Object o : expressions) {
				exprList.addExpression((IASTExpression) o);
			}

			setOffsetAndLength(exprList);
			astStack.push(exprList);
		}
	}

	/**
	 * postfix_expression ::= postfix_expression '[' expression ']'
	 */
	public void consumeExpressionArraySubscript() {
		IASTExpression subscript = (IASTExpression) astStack.pop();
		IASTExpression arrayExpr = (IASTExpression) astStack.pop();
		IASTArraySubscriptExpression expr = nodeFactory.newArraySubscriptExpression(arrayExpr, subscript);
		setOffsetAndLength(expr);
		astStack.push(expr);
	}

	/**
	 * postfix_expression ::= postfix_expression '(' expression_list_opt ')'
	 */
	public void consumeExpressionFunctionCall() {
		IASTExpression argList = (IASTExpression) astStack.pop(); // may be null
		IASTExpression idExpr = (IASTExpression) astStack.pop();

		IASTFunctionCallExpression expr = nodeFactory.newFunctionCallExpression(idExpr, argList);
		setOffsetAndLength(expr);
		astStack.push(expr);
	}

	/**
	 * @param operator constant for {@link ICPPASTCastExpression}
	 */
	public void consumeExpressionCast(int operator) {
		IASTExpression operand = (IASTExpression) astStack.pop();
		IASTTypeId typeId = (IASTTypeId) astStack.pop();
		IASTCastExpression expr = nodeFactory.newCastExpression(operator, typeId, operand);
		setOffsetAndLength(expr);

		IASTExpression alternateExpr = null;
		if (operator == IASTCastExpression.op_cast) { // don't reparse for dynamic_cast etc as those are not ambiguous
			// try parsing as non-cast to resolve ambiguities
			ISecondaryParser<IASTExpression> secondaryParser = parserFactory.getNoCastExpressionParser(stream,
					properties);
			alternateExpr = runSecondaryParser(secondaryParser);
		}

		if (alternateExpr == null)
			astStack.push(expr);
		else {
			IASTNode ambiguityNode = createAmbiguousExpression(expr, alternateExpr);
			setOffsetAndLength(ambiguityNode);
			astStack.push(ambiguityNode);
		}
	}

	protected abstract IASTAmbiguousExpression createAmbiguousExpression(IASTExpression... expressions);

	/**
	 * Lots of rules, no need to list them.
	 * @param operator From IASTUnaryExpression
	 */
	public void consumeExpressionUnaryOperator(int operator) {
		IASTExpression operand = (IASTExpression) astStack.pop();
		IASTUnaryExpression expr = nodeFactory.newUnaryExpression(operator, operand);
		setOffsetAndLength(expr);
		astStack.push(expr);
	}

	/**
	 * unary_operation ::= 'sizeof' '(' type_name ')'
	 * @see consumeExpressionUnaryOperator For the other use of sizeof
	 */
	public void consumeExpressionTypeId(int operator) {
		IASTTypeId typeId = (IASTTypeId) astStack.pop();
		IASTTypeIdExpression expr = nodeFactory.newTypeIdExpression(operator, typeId);
		setOffsetAndLength(expr);

		// try parsing as an expression to resolve ambiguities
		ISecondaryParser<IASTExpression> secondaryParser = parserFactory.getSizeofExpressionParser(stream, properties);
		IASTExpression alternateExpr = runSecondaryParser(secondaryParser);

		if (alternateExpr == null)
			astStack.push(expr);
		else if (isFunctionType(expr)) // bug 252243
			astStack.push(alternateExpr);
		else {
			IASTNode ambiguityNode = createAmbiguousExpression(expr, alternateExpr);
			setOffsetAndLength(ambiguityNode);
			astStack.push(ambiguityNode);
		}
	}

	private static boolean isFunctionType(IASTExpression expr) {
		if (expr instanceof IASTTypeIdExpression) {
			IASTTypeId typeId = ((IASTTypeIdExpression) expr).getTypeId();
			return typeId.getAbstractDeclarator() instanceof IASTFunctionDeclarator;
		}
		return false;
	}

	/**
	 * Lots of rules, no need to list them all.
	 * @param op Field from IASTBinaryExpression
	 */
	public void consumeExpressionBinaryOperator(int op) {
		IASTExpression expr2 = (IASTExpression) astStack.pop();
		IASTExpression expr1 = (IASTExpression) astStack.pop();
		IASTBinaryExpression binExpr = nodeFactory.newBinaryExpression(op, expr1, expr2);
		setOffsetAndLength(binExpr);
		astStack.push(binExpr);
	}

	/**
	 * conditional_expression ::= logical_OR_expression '?' expression ':' conditional_expression
	 */
	public void consumeExpressionConditional() {
		IASTExpression expr3 = (IASTExpression) astStack.pop();
		IASTExpression expr2 = (IASTExpression) astStack.pop();
		IASTExpression expr1 = (IASTExpression) astStack.pop();
		IASTConditionalExpression condExpr = nodeFactory.newConditionalExpession(expr1, expr2, expr3);
		setOffsetAndLength(condExpr);
		astStack.push(condExpr);
	}

	/**
	 * labeled_statement ::= label_identifier ':' statement
	 * label_identifier ::= identifier
	 */
	public void consumeStatementLabeled() {
		IASTStatement body = (IASTStatement) astStack.pop();
		IASTName label = createName(stream.getLeftIToken());

		IASTLabelStatement stat = nodeFactory.newLabelStatement(label, body);
		setOffsetAndLength(stat);
		astStack.push(stat);
	}

	/**
	 * labeled_statement ::= 'case' constant_expression ':' statement
	 */
	public void consumeStatementCase() {
		IASTStatement body = (IASTStatement) astStack.pop();
		IASTExpression expr = (IASTExpression) astStack.pop();

		IASTCaseStatement caseStatement = nodeFactory.newCaseStatement(expr);
		setOffsetAndLength(caseStatement); // TODO this is wrong, need to adjust length to end of colon

		// this is a hackey fix because case statements are not modeled correctly in the AST
		IASTCompoundStatement compound = nodeFactory.newCompoundStatement();
		setOffsetAndLength(compound);
		compound.addStatement(caseStatement);
		compound.addStatement(body);

		astStack.push(compound);
	}

	/**
	 * labeled_statement ::= 'default' ':' <openscope-ast> statement
	 */
	public void consumeStatementDefault() {
		IASTStatement body = (IASTStatement) astStack.pop();

		IASTDefaultStatement stat = nodeFactory.newDefaultStatement();
		List<IToken> tokens = stream.getRuleTokens();
		IToken defaultToken = tokens.get(0);
		IToken colonToken = tokens.get(1);
		ParserUtil.setOffsetAndLength(stat, offset(defaultToken), offset(colonToken) - offset(defaultToken) + 1);

		IASTCompoundStatement compound = nodeFactory.newCompoundStatement();
		setOffsetAndLength(compound);
		compound.addStatement(stat);
		compound.addStatement(body);

		astStack.push(compound);
	}

	/**
	 * expression_statement ::= ';'
	 */
	public void consumeStatementNull() {
		IASTNullStatement stat = nodeFactory.newNullStatement();
		setOffsetAndLength(stat);
		astStack.push(stat);
	}

	/**
	 * expression_statement ::= expression ';'
	 */
	public void consumeStatementExpression() {
		IASTExpression expr = (IASTExpression) astStack.pop();
		IASTExpressionStatement stat = nodeFactory.newExpressionStatement(expr);
		setOffsetAndLength(stat);
		astStack.push(stat);
	}

	/**
	 * compound_statement ::= <openscope> '{' block_item_list '}'
	 *
	 * block_item_list ::= block_item | block_item_list block_item
	 */
	public void consumeStatementCompoundStatement(boolean hasStatementsInBody) {
		IASTCompoundStatement block = nodeFactory.newCompoundStatement();

		if (hasStatementsInBody) {
			for (Object o : astStack.closeScope()) {
				block.addStatement((IASTStatement) o);
			}
		}

		setOffsetAndLength(block);
		astStack.push(block);
	}

	/**
	 * iteration_statement_matched
	 *     ::= 'do' statement 'while' '(' expression ')' ';'
	 *        | 'do' statement
	 */
	public void consumeStatementDoLoop(boolean hasWhileBlock) {
		IASTExpression condition = hasWhileBlock ? (IASTExpression) astStack.pop() : null;
		IASTStatement body = (IASTStatement) astStack.pop();
		IASTDoStatement stat = nodeFactory.newDoStatement(body, condition);
		setOffsetAndLength(stat);
		astStack.push(stat);
	}

	/**
	 * jump_statement ::= goto goto_identifier ';'
	 */
	public void consumeStatementGoto() {
		IASTName name = createName(stream.getRuleTokens().get(1));
		IASTGotoStatement gotoStat = nodeFactory.newGotoStatement(name);
		setOffsetAndLength(gotoStat);
		astStack.push(gotoStat);
	}

	/**
	 * jump_statement ::= continue ';'
	 */
	public void consumeStatementContinue() {
		IASTContinueStatement stat = nodeFactory.newContinueStatement();
		setOffsetAndLength(stat);
		astStack.push(stat);
	}

	/**
	 * jump_statement ::= break ';'
	 */
	public void consumeStatementBreak() {
		IASTBreakStatement stat = nodeFactory.newBreakStatement();
		setOffsetAndLength(stat);
		astStack.push(stat);
	}

	/**
	 * jump_statement ::= return ';'
	 * jump_statement ::= return expression ';'
	 */
	public void consumeStatementReturn(boolean hasExpr) {
		IASTExpression expr = hasExpr ? (IASTExpression) astStack.pop() : null;
		IASTReturnStatement returnStat = nodeFactory.newReturnStatement(expr);
		setOffsetAndLength(returnStat);
		astStack.push(returnStat);
	}

	/**
	 * type_name ::= specifier_qualifier_list
	 *             | specifier_qualifier_list abstract_declarator
	 */
	public void consumeTypeId(boolean hasDeclarator) {
		IASTDeclarator declarator;
		if (hasDeclarator)
			declarator = (IASTDeclarator) astStack.pop();
		else {
			declarator = nodeFactory.newDeclarator(nodeFactory.newName());
			ParserUtil.setOffsetAndLength(declarator, stream.getRightIToken().getEndOffset(), 0);
		}

		IASTDeclSpecifier declSpecifier = (IASTDeclSpecifier) astStack.pop();
		IASTTypeId typeId = nodeFactory.newTypeId(declSpecifier, declarator);
		setOffsetAndLength(typeId);
		astStack.push(typeId);
	}

	/**
	 * declarator
	 *     ::= <openscope-ast> ptr_operator_seq direct_declarator
	 *
	 * abstract_declarator
	 *     ::= <openscope-ast> ptr_operator_seq
	 *       | <openscope-ast> ptr_operator_seq direct_declarator
	 */
	public void consumeDeclaratorWithPointer(boolean hasDeclarator) {
		IASTDeclarator decl;
		if (hasDeclarator)
			decl = (IASTDeclarator) astStack.pop();
		else
			decl = nodeFactory.newDeclarator(nodeFactory.newName());

		for (Object pointer : astStack.closeScope())
			decl.addPointerOperator((IASTPointerOperator) pointer);

		setOffsetAndLength(decl);
		astStack.push(decl);
	}

	/**
	 * init_declarator
	 *     ::= declarator initializer
	 *
	 * @param hasDeclarator in C++ its possible for a parameter declaration to specifiy
	 *        a default value without also specifying a named declarator
	 */
	public void consumeDeclaratorWithInitializer(boolean hasDeclarator) {
		IASTInitializer initializer = (IASTInitializer) astStack.pop();

		IASTDeclarator declarator;
		if (hasDeclarator) {
			declarator = (IASTDeclarator) astStack.peek();
		} else {
			IASTName emptyName = nodeFactory.newName();
			declarator = nodeFactory.newDeclarator(emptyName);
			setOffsetAndLength(emptyName);
			astStack.push(declarator);
		}

		declarator.setInitializer(initializer);
		setOffsetAndLength(declarator); // adjust the length to include the initializer
	}

	/**
	 * asm_definition
	 *     ::= 'asm' '(' 'stringlit' ')' ';'
	 */
	public void consumeDeclarationASM() {
		String s = stream.getRuleTokens().get(2).toString();
		IASTASMDeclaration asm = nodeFactory.newASMDeclaration(s);

		setOffsetAndLength(asm);
		astStack.push(asm);
	}

	/**
	 * parameter_declaration ::= declaration_specifiers declarator
	 *                         | declaration_specifiers abstract_declarator
	 */
	public void consumeParameterDeclaration() {
		IASTDeclarator declarator = (IASTDeclarator) astStack.pop();
		IASTDeclSpecifier declSpec = (IASTDeclSpecifier) astStack.pop();
		IASTParameterDeclaration declaration = nodeFactory.newParameterDeclaration(declSpec, declarator);
		setOffsetAndLength(declaration);
		astStack.push(declaration);
	}

	/**
	 * parameter_declaration ::= declaration_specifiers
	 */
	public void consumeParameterDeclarationWithoutDeclarator() {
		// offsets need to be calculated differently in this case
		final int endOffset = stream.getRightIToken().getEndOffset();

		IASTName name = nodeFactory.newName();
		ParserUtil.setOffsetAndLength(name, endOffset, 0);

		// it appears that a declarator is always required in the AST here
		IASTDeclarator declarator = nodeFactory.newDeclarator(name);
		ParserUtil.setOffsetAndLength(declarator, endOffset, 0);

		IASTDeclSpecifier declSpec = (IASTDeclSpecifier) astStack.pop();
		IASTParameterDeclaration declaration = nodeFactory.newParameterDeclaration(declSpec, declarator);

		setOffsetAndLength(declaration);
		astStack.push(declaration);
	}

	/**
	 * TODO: do I really want to share declaration rules between the two parsers.
	 * Even if there is potential for reuse it still may be cleaner to leave the
	 * common stuff to just simple expressions and statements.
	 *
	 * For C99:
	 *
	 * declaration ::= declaration_specifiers <openscope> init_declarator_list ';'
	 * declaration ::= declaration_specifiers  ';'
	 *
	 *
	 * For C++:
	 *
	 * simple_declaration
	 *     ::= declaration_specifiers_opt <openscope-ast> init_declarator_list_opt ';'
	 *
	 *
	 * TODO Make both grammars the same here.
	 */
	//	public void consumeDeclarationSimple(boolean hasDeclaratorList) {
	//		if(TRACE_ACTIONS) DebugUtil.printMethodTrace();
	//
	//		List<Object> declarators = (hasDeclaratorList) ? astStack.closeScope() : Collections.emptyList();
	//		IASTDeclSpecifier declSpecifier = (IASTDeclSpecifier) astStack.pop(); // may be null
	//
	//		// do not generate nodes for extra EOC tokens
	//		if(matchTokens(parser.getRuleTokens(), CPPParsersym.TK_EndOfCompletion))
	//			return;
	//
	//		if(declSpecifier == null) { // can happen if implicit int is used
	//			declSpecifier = nodeFactory.newSimpleDeclSpecifier();
	//			setOffsetAndLength(declSpecifier, parser.getLeftIToken().getStartOffset(), 0);
	//		}
	//
	//		IASTSimpleDeclaration declaration = nodeFactory.newSimpleDeclaration(declSpecifier);
	//
	//		for(Object declarator : declarators)
	//			declaration.addDeclarator((IASTDeclarator)declarator);
	//
	//		setOffsetAndLength(declaration);
	//		astStack.push(declaration);
	//
	//		if(TRACE_AST_STACK) System.out.println(astStack);
	//	}

	/**
	 * direct_declarator ::= '(' declarator ')'
	 */
	public void consumeDirectDeclaratorBracketed() {
		IASTDeclarator nested = (IASTDeclarator) astStack.pop();
		IASTDeclarator declarator = nodeFactory.newDeclarator(nodeFactory.newName());
		declarator.setNestedDeclarator(nested);
		setOffsetAndLength(declarator);
		astStack.push(declarator);
	}

	/**
	 * direct_declarator ::= declarator_id_name
	 */
	public void consumeDirectDeclaratorIdentifier() {
		IASTName name = (IASTName) astStack.pop();
		IASTDeclarator declarator = nodeFactory.newDeclarator(name);
		setOffsetAndLength(declarator);
		astStack.push(declarator);
	}

	/**
	 *  array_modifier
	 *      ::= '[' ']'
	 *        | '[' assignment_expression ']'
	 */
	public void consumeDirectDeclaratorArrayModifier(boolean hasAssignmentExpr) {
		IASTExpression expr = hasAssignmentExpr ? (IASTExpression) astStack.pop() : null;
		IASTArrayModifier arrayModifier = nodeFactory.newArrayModifier(expr);
		setOffsetAndLength(arrayModifier);
		astStack.push(arrayModifier);
	}

	/**
	 * When the identifier part of a declarator is parsed it will put a plain IASTDeclarator on the stack.
	 * When the array modifier part is parsed we will need to throw away the plain
	 * declarator and replace it with an array declarator. If its a multidimensional array then
	 * the additional array modifiers will need to be added to the array declarator.
	 * Special care is taken for nested declarators.
	 */
	protected void addArrayModifier(IASTArrayModifier arrayModifier) {
		IASTDeclarator node = (IASTDeclarator) astStack.pop();

		// Its a nested declarator so create an new ArrayDeclarator
		if (node.getNestedDeclarator() != null) { //node.getPropertyInParent() == IASTDeclarator.NESTED_DECLARATOR) {
			IASTArrayDeclarator declarator = nodeFactory.newArrayDeclarator(nodeFactory.newName());
			IASTDeclarator nested = node;
			declarator.setNestedDeclarator(nested);

			int offset = offset(nested);
			int length = endOffset(arrayModifier) - offset;
			ParserUtil.setOffsetAndLength(declarator, offset, length);

			declarator.addArrayModifier(arrayModifier);
			astStack.push(declarator);
		}
		// There is already an array declarator so just add the modifier to it
		else if (node instanceof IASTArrayDeclarator) {
			IASTArrayDeclarator decl = (IASTArrayDeclarator) node;
			((ASTNode) decl).setLength(endOffset(arrayModifier) - offset(decl));

			decl.addArrayModifier(arrayModifier);
			astStack.push(decl);
		}
		// The declarator is an identifier so create a new array declarator
		else {
			IASTName name = node.getName();
			IASTArrayDeclarator decl = nodeFactory.newArrayDeclarator(name);

			int offset = offset(name);
			int length = endOffset(arrayModifier) - offset;
			ParserUtil.setOffsetAndLength(decl, offset, length);

			decl.addArrayModifier(arrayModifier);
			astStack.push(decl);
		}
	}

	/**
	 * Pops a simple declarator from the stack, converts it into
	 * a FunctionDeclator, then pushes it.
	 * TODO: is this the best way of doing this?
	 * TODO, rename this method, its an accidental overload
	 */
	protected void addFunctionModifier(IASTFunctionDeclarator declarator, int endOffset) {
		IASTDeclarator decl = (IASTDeclarator) astStack.pop();

		if (decl.getNestedDeclarator() != null) {
			decl = decl.getNestedDeclarator(); // need to remove one level of nesting for function pointers
			declarator.setNestedDeclarator(decl);
			declarator.setName(nodeFactory.newName());
			int offset = offset(decl);
			ParserUtil.setOffsetAndLength(declarator, offset, endOffset - offset);
			astStack.push(declarator);
		} else {
			IASTName name = decl.getName();
			if (name == null) {
				name = nodeFactory.newName();
			}
			declarator.setName(name);

			IASTPointerOperator[] pointers = decl.getPointerOperators();
			for (IASTPointerOperator pointer : pointers) {
				declarator.addPointerOperator(pointer);
			}

			int offset = offset(name); // TODO
			ParserUtil.setOffsetAndLength(declarator, offset, endOffset - offset);
			astStack.push(declarator);
		}
	}

	// TODO why is this here
	//	/**
	//	 * direct_declarator ::= direct_declarator array_modifier
	//	 * consume the direct_declarator part and add the array modifier
	//	 */
	//	public void consumeDirectDeclaratorArrayDeclarator() {
	//		if(TRACE_ACTIONS) DebugUtil.printMethodTrace();
	//
	//		IASTArrayModifier arrayModifier = (IASTArrayModifier) astStack.pop();
	//		addArrayModifier(arrayModifier);
	//	}

	/**
	 * direct_abstract_declarator
	 *     ::= array_modifier
	 *       | direct_abstract_declarator array_modifier
	 */
	public void consumeDirectDeclaratorArrayDeclarator(boolean hasDeclarator) {
		IASTArrayModifier arrayModifier = (IASTArrayModifier) astStack.pop();

		if (hasDeclarator) {
			addArrayModifier(arrayModifier);
		} else {
			IASTArrayDeclarator decl = nodeFactory.newArrayDeclarator(nodeFactory.newName());
			decl.addArrayModifier(arrayModifier);
			setOffsetAndLength(decl);
			astStack.push(decl);
		}
	}

	/**
	 * enum_specifier ::= 'enum' '{' <openscope> enumerator_list_opt '}'
	 *                  | 'enum' enum_identifier '{' <openscope> enumerator_list_opt '}'
	 */
	public void consumeTypeSpecifierEnumeration(boolean hasIdent) {
		IASTName name = (hasIdent) ? createName(stream.getRuleTokens().get(1)) : nodeFactory.newName();

		IASTEnumerationSpecifier enumSpec = nodeFactory.newEnumerationSpecifier(name);

		for (Object o : astStack.closeScope())
			enumSpec.addEnumerator((IASTEnumerator) o);

		setOffsetAndLength(enumSpec);
		astStack.push(enumSpec);
	}

	/**
	 * enumerator ::= enum_identifier
	 *              | enum_identifier '=' constant_expression
	 */
	public void consumeEnumerator(boolean hasInitializer) {
		IASTName name = createName(stream.getLeftIToken());

		IASTExpression value = null;
		if (hasInitializer)
			value = (IASTExpression) astStack.pop();

		IASTEnumerator enumerator = nodeFactory.newEnumerator(name, value);
		setOffsetAndLength(enumerator);
		astStack.push(enumerator);
	}

	private int initializerListNestingLevel = 0;

	public void initializerListStart() {
		initializerListNestingLevel++;
	}

	public void initializerListEnd() {
		initializerListNestingLevel--;
	}

	/**
	 * initializer ::= assignment_expression
	 */
	public void consumeInitializer() {
		//CDT_70_FIX_FROM_50-#4
		IASTInitializerClause initClause = (IASTInitializerClause) astStack.pop();
		if (initClause instanceof IASTExpression) {
			if (discardInitializer((IASTExpression) initClause)) {
				astStack.push(null);
				return;
			}
		}
		//CDT_70_FIX_FROM_50-#2
		//IASTInitializerExpression initializer = nodeFactory.newInitializerExpression(expr);
		IASTEqualsInitializer initializer = nodeFactory.newEqualsInitializer(initClause);
		setOffsetAndLength(initializer);
		astStack.push(initializer);
	}

	private boolean discardInitializer(IASTExpression expression) {
		return initializerListNestingLevel > 0
				&& Boolean.parseBoolean(
						properties.get(LRParserProperties.SKIP_TRIVIAL_EXPRESSIONS_IN_AGGREGATE_INITIALIZERS))
				&& !ASTQueries.canContainName(expression);
	}

	/**
	 * initializer ::= '{' <openscope> initializer_list '}'
	 *               | '{' <openscope> initializer_list ',' '}'
	 */
	public void consumeInitializerList() {
		IASTInitializerList list = nodeFactory.newInitializerList();

		for (Object o : astStack.closeScope())
			list.addInitializer((IASTInitializer) o);

		setOffsetAndLength(list);
		astStack.push(list);
	}

	/**
	 * struct_declarator
	 *     ::= ':' constant_expression
	 *       | declarator ':' constant_expression
	 */
	public void consumeBitField(boolean hasDeclarator) {
		IASTExpression expr = (IASTExpression) astStack.pop();

		IASTName name;
		if (hasDeclarator) // it should have been parsed into a regular declarator
			name = ((IASTDeclarator) astStack.pop()).getName();
		else
			name = nodeFactory.newName();

		IASTFieldDeclarator fieldDecl = nodeFactory.newFieldDeclarator(name, expr);
		setOffsetAndLength(fieldDecl);
		astStack.push(fieldDecl);
	}

	/**
	 * statement ::= ERROR_TOKEN
	 */
	public void consumeStatementProblem() {
		consumeProblem(nodeFactory.newProblemStatement(null));
	}

	/**
	 * assignment_expression ::= ERROR_TOKEN
	 * constant_expression ::= ERROR_TOKEN
	 */
	public void consumeExpressionProblem() {
		consumeProblem(nodeFactory.newProblemExpression(null));
	}

	/**
	 * external_declaration ::= ERROR_TOKEN
	 */
	public void consumeDeclarationProblem() {
		consumeProblem(nodeFactory.newProblemDeclaration(null));
	}

	private void consumeProblem(IASTProblemHolder problemHolder) {
		IASTProblem problem = nodeFactory.newProblem(IProblem.SYNTAX_ERROR, new char[0], true);
		problemHolder.setProblem(problem);
		setOffsetAndLength(problem);
		setOffsetAndLength((ASTNode) problemHolder);
		astStack.push(problemHolder);
	}

}
