/*******************************************************************************
 * Copyright (c) 2005, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     John Camelon (IBM Rational Software) - Initial API and implementation
 *     Markus Schorn (Wind River Systems)
 *     Ed Swartz (Nokia)
 *     Mike Kucera (IBM) - bug #206952
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.c;

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

import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
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.IASTAttribute;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTCastExpression;
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.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.IASTFieldDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
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.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
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.IASTProblemDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
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.IScope;
import org.eclipse.cdt.core.dom.ast.c.ICASTArrayDesignator;
import org.eclipse.cdt.core.dom.ast.c.ICASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTDesignatedInitializer;
import org.eclipse.cdt.core.dom.ast.c.ICASTDesignator;
import org.eclipse.cdt.core.dom.ast.c.ICASTFieldDesignator;
import org.eclipse.cdt.core.dom.ast.c.ICASTPointer;
import org.eclipse.cdt.core.dom.ast.c.ICASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTTypedefNameSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICNodeFactory;
import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.gnu.c.IGCCASTArrayRangeDesignator;
import org.eclipse.cdt.core.dom.parser.IExtensionToken;
import org.eclipse.cdt.core.dom.parser.c.ICParserExtensionConfiguration;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.parser.EndOfFileException;
import org.eclipse.cdt.core.parser.IGCCToken;
import org.eclipse.cdt.core.parser.IParserLogService;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.ParserMode;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.core.parser.util.CollectionUtils;
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.AbstractGNUSourceCodeParser;
import org.eclipse.cdt.internal.core.dom.parser.BacktrackException;
import org.eclipse.cdt.internal.core.dom.parser.DeclarationOptions;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousExpression;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousStatement;

/**
 * Source parser for gnu-c syntax.
 */
public class GNUCSourceParser extends AbstractGNUSourceCodeParser {
    private static final int DEFAULT_POINTEROPS_LIST_SIZE = 4;
    private static final int DEFAULT_PARAMETERS_LIST_SIZE = 4;

    private final boolean supportGCCStyleDesignators;
	private IIndex index;
    protected IASTTranslationUnit translationUnit;

    private int fPreventKnrCheck= 0;
    
    private final ICNodeFactory nodeFactory;

    public GNUCSourceParser(IScanner scanner, ParserMode parserMode,
            IParserLogService logService, ICParserExtensionConfiguration config) {
    	this(scanner, parserMode, logService, config, null);
    }
    
    public GNUCSourceParser(IScanner scanner, ParserMode parserMode,
            IParserLogService logService, ICParserExtensionConfiguration config,
            IIndex index) {
        super(scanner, logService, parserMode, CNodeFactory.getDefault(),
        		config.supportStatementsInExpressions(), 
        		config.supportTypeofUnaryExpressions(),
        		config.supportAlignOfUnaryExpression(),
        		config.supportKnRC(), 
        		config.supportAttributeSpecifiers(),
                config.supportDeclspecSpecifiers(),
                config.getBuiltinBindingsProvider());
        supportGCCStyleDesignators = config.supportGCCStyleDesignators();
        supportParameterInfoBlock= config.supportParameterInfoBlock();
        supportExtendedSizeofOperator= config.supportExtendedSizeofOperator();
        supportFunctionStyleAsm= config.supportFunctionStyleAssembler();
        this.index= index;
        this.nodeFactory = CNodeFactory.getDefault();
    }

    @Override
	protected IASTInitializer optionalInitializer(IASTDeclarator dtor, DeclarationOptions options) throws EndOfFileException, BacktrackException {
        if (LTcatchEOF(1) == IToken.tASSIGN) {
            final int offset= consume().getOffset();
            IASTInitializerClause initClause = initClause(false);
            IASTEqualsInitializer result= nodeFactory.newEqualsInitializer(initClause);
            return setRange(result, offset, calculateEndOffset(initClause));
        }
        return null;
    }

    private IASTInitializerClause initClause(boolean inAggregate) throws EndOfFileException, BacktrackException {
        final int offset = LA(1).getOffset();
        if (LT(1) != IToken.tLBRACE) {
            IASTExpression assignmentExpression= expression(ExprKind.eAssignment);
            if (inAggregate && skipTrivialExpressionsInAggregateInitializers) {
            	if (!ASTQueries.canContainName(assignmentExpression))
            		return null;
            }
            return assignmentExpression;
        }
        
        // it's an aggregate initializer
        consume(IToken.tLBRACE);
        IASTInitializerList result = nodeFactory.newInitializerList();

        // bug 196468, gcc accepts empty braces.
        if (supportGCCStyleDesignators && LT(1) == IToken.tRBRACE) {
        	int endOffset= consume().getEndOffset();
        	setRange(result, offset, endOffset);
        	return result;
        }

        for (;;) {
        	final int checkOffset= LA(1).getOffset();
        	// required at least one initializer list
        	// get designator list
        	List<? extends ICASTDesignator> designator= designatorList();
        	if (designator == null) {
        		IASTInitializerClause clause= initClause(true);
        		// depending on value of skipTrivialItemsInCompoundInitializers initializer may be null
        		// in any way add the initializer such that the actual size can be tracked.
        		result.addClause(clause);
        	} else {
        		ICASTDesignatedInitializer desigInitializer = nodeFactory.newDesignatedInitializer((IASTInitializerClause) null);
        		setRange(desigInitializer, designator.get(0));
        		for (ICASTDesignator d : designator) {
        			desigInitializer.addDesignator(d);
        		}

        		if (LT(1) != IToken.tEOC) {
        			// Gnu extension: the assign operator is optional
        			if (LT(1) == IToken.tASSIGN)
        				consume(IToken.tASSIGN);

        			IASTInitializerClause clause= initClause(false);
        			desigInitializer.setOperand(clause);
        			adjustLength(desigInitializer, clause);
        		}
        		result.addClause(desigInitializer);
        	}

        	// can end with ", }" or "}"
        	boolean canContinue= LT(1) == IToken.tCOMMA;
        	if (canContinue)
        		consume();
        	
        	switch (LT(1)) {
        	case IToken.tRBRACE:
        		int lastOffset = consume().getEndOffset();
        		setRange(result, offset, lastOffset);
        		return result;

        	case IToken.tEOC:
        		setRange(result, offset, LA(1).getOffset());
        		return result;
        	}
        	
        	if (!canContinue || LA(1).getOffset() == checkOffset) {
        		throwBacktrack(offset, LA(1).getEndOffset() - offset);
        	}
        }
        // consume the closing brace
    }


    private List<? extends ICASTDesignator> designatorList() throws EndOfFileException, BacktrackException {
    	final int lt1= LT(1);
        if (lt1 == IToken.tDOT || lt1 == IToken.tLBRACKET) {
            List<ICASTDesignator> designatorList= null;
            while (true) {
            	switch (LT(1)) {
            	case IToken.tDOT:
                    int offset = consume().getOffset();
                    IASTName n = identifier();
                    ICASTFieldDesignator fieldDesignator = nodeFactory.newFieldDesignator(n);
                	setRange(fieldDesignator, offset, calculateEndOffset(n));
                    if (designatorList == null)
                        designatorList = new ArrayList<ICASTDesignator>(DEFAULT_DESIGNATOR_LIST_SIZE);
                    designatorList.add(fieldDesignator);
                    break;
                    
            	case IToken.tLBRACKET:
                    offset = consume().getOffset();
                    IASTExpression constantExpression = expression();
                    if (supportGCCStyleDesignators && LT(1) == IToken.tELLIPSIS) {
                    	consume(IToken.tELLIPSIS);
                    	IASTExpression constantExpression2 = expression();
                    	int lastOffset = consume(IToken.tRBRACKET).getEndOffset();
                    	IGCCASTArrayRangeDesignator designator = nodeFactory.newArrayRangeDesignatorGCC(constantExpression, constantExpression2);
                    	setRange(designator, offset, lastOffset);
                    	if (designatorList == null)
                    		designatorList = new ArrayList<ICASTDesignator>(DEFAULT_DESIGNATOR_LIST_SIZE);
                    	designatorList.add(designator);
                    } else {
                        int lastOffset = consume(IToken.tRBRACKET).getEndOffset();
                        ICASTArrayDesignator designator = nodeFactory.newArrayDesignator(constantExpression);
                    	setRange(designator, offset, lastOffset);
                        if (designatorList == null)
                            designatorList = new ArrayList<ICASTDesignator>(DEFAULT_DESIGNATOR_LIST_SIZE);
                        designatorList.add(designator);
                    }
                    break;
                    
                default:
                	return designatorList;
                }
            }
        } 
        
		// fix for 84176: if reach identifier and it's not a designator then return empty designator list
		if (supportGCCStyleDesignators && lt1 == IToken.tIDENTIFIER && LT(2) == IToken.tCOLON) {
			int offset= LA(1).getOffset();
			IASTName n = identifier();
			int lastOffset = consume(IToken.tCOLON).getEndOffset();
			ICASTFieldDesignator designator = nodeFactory.newFieldDesignator(n);
			setRange(designator, offset, lastOffset);
			return Collections.singletonList(designator);
		}
		
        return null;
    }
    
	@Override
	protected IASTDeclaration declaration(final DeclarationOptions declOption) throws EndOfFileException, BacktrackException {
        switch (LT(1)) {
        case IToken.t_asm:
            return asmDeclaration();
        case IToken.tSEMI:
        	IToken semi= consume();
        	IASTDeclSpecifier declspec= nodeFactory.newSimpleDeclSpecifier();
        	IASTSimpleDeclaration decl= nodeFactory.newSimpleDeclaration(declspec);
        	decl.setDeclSpecifier(declspec);
        	((ASTNode) declspec).setOffsetAndLength(semi.getOffset(), 0);
        	((ASTNode) decl).setOffsetAndLength(semi.getOffset(), semi.getLength());
        	return decl;
        }

        return simpleDeclaration(declOption);
    }

	private IASTDeclaration simpleDeclaration(final DeclarationOptions declOption) throws BacktrackException, EndOfFileException {
        if (LT(1) == IToken.tLBRACE)
            throwBacktrack(LA(1));
        
        final int firstOffset= LA(1).getOffset();
        int endOffset= firstOffset;
        boolean insertSemi= false;

        IASTDeclSpecifier declSpec= null;
        IASTDeclarator dtor= null;
        IASTDeclSpecifier altDeclSpec= null;
        IASTDeclarator altDtor= null;
        IToken markBeforDtor= null;
        try {
        	Decl decl= declSpecifierSequence_initDeclarator(declOption, true);
        	markBeforDtor= decl.fDtorToken1;
        	declSpec= decl.fDeclSpec1;
        	dtor= decl.fDtor1;
        	altDeclSpec= decl.fDeclSpec2;
        	altDtor= decl.fDtor2;
        } catch (FoundAggregateInitializer lie) {
        	declSpec= lie.fDeclSpec;
        	// scalability: don't keep references to tokens, initializer may be large
        	declarationMark= null;
        	dtor= addInitializer(lie, declOption);
        } catch (BacktrackException e) {
        	IASTNode node= e.getNodeBeforeProblem();
        	if (node instanceof IASTDeclSpecifier) {
        		IASTSimpleDeclaration d= nodeFactory.newSimpleDeclaration((IASTDeclSpecifier) node);
        		setRange(d, node);
        		throwBacktrack(e.getProblem(), d);
        	}
        	throw e;
        }
        
        IASTDeclarator[] declarators= IASTDeclarator.EMPTY_DECLARATOR_ARRAY;
        if (dtor != null) {
        	declarators= new IASTDeclarator[]{dtor};
        	while (LTcatchEOF(1) == IToken.tCOMMA) {
        		consume();
        		try {
        			dtor= initDeclarator(declSpec, declOption);
        		} catch (FoundAggregateInitializer e) {
        	        // scalability: don't keep references to tokens, initializer may be large
        			declarationMark= null;
        			markBeforDtor= null;
        			dtor= addInitializer(e, declOption);
        		}
        		declarators= ArrayUtil.append( IASTDeclarator.class, declarators, dtor);
        	}
        	declarators= ArrayUtil.removeNulls( IASTDeclarator.class, declarators );
        }
        
        final int lt1= LTcatchEOF(1);
        switch (lt1) {
        case IToken.tEOC:
        	endOffset= figureEndOffset(declSpec, declarators);
        	break;
        case IToken.tSEMI:
        	endOffset= consume().getEndOffset();
        	break;
        case IToken.tLBRACE:
        	return functionDefinition(firstOffset, declSpec, declarators);

        default:
    		insertSemi= true;
        	if (declOption == DeclarationOptions.LOCAL) {
            	endOffset= figureEndOffset(declSpec, declarators);
        		if (firstOffset != endOffset) {
        			break;
        		}
        	} else {
        		if (markBeforDtor != null) {
        			endOffset= calculateEndOffset(declSpec);
        			if (firstOffset != endOffset && !isOnSameLine(endOffset, markBeforDtor.getOffset())) {
        				backup(markBeforDtor);
        				declarators= IASTDeclarator.EMPTY_DECLARATOR_ARRAY;
        				break;
        			}
        		}
        		endOffset= figureEndOffset(declSpec, declarators);
        		if (lt1 == 0) {
        			break;
        		}
        		if (firstOffset != endOffset) {
        			if (!isOnSameLine(endOffset, LA(1).getOffset())) {
        				break;
        			}
        			if (declarators.length == 1 && declarators[0] instanceof IASTFunctionDeclarator) {
        				break;
        			}
        		}
        	}
        	throwBacktrack(LA(1));
        }

        // no function body
        IASTSimpleDeclaration simpleDeclaration = nodeFactory.newSimpleDeclaration(declSpec);
        for (IASTDeclarator declarator : declarators)
            simpleDeclaration.addDeclarator(declarator);
        
    	setRange(simpleDeclaration, firstOffset, endOffset);
		if (altDeclSpec != null && altDtor != null) {
			simpleDeclaration = new CASTAmbiguousSimpleDeclaration(simpleDeclaration, altDeclSpec, altDtor);
			setRange(simpleDeclaration, firstOffset, endOffset);
		}
        
        if (insertSemi) {
    		IASTProblem problem= createProblem(IProblem.MISSING_SEMICOLON, endOffset-1, 1);
    		throwBacktrack(problem, simpleDeclaration);
        }
        return simpleDeclaration;
    }

	private IASTDeclaration functionDefinition(int firstOffset, IASTDeclSpecifier declSpec,
			IASTDeclarator[] declarators) throws BacktrackException, EndOfFileException {
		if (declarators.length != 1)
		    throwBacktrack(firstOffset, LA(1).getEndOffset());

		final IASTDeclarator outerDtor= declarators[0];
		final IASTDeclarator fdtor= ASTQueries.findTypeRelevantDeclarator(outerDtor);
		if (!(fdtor instanceof IASTFunctionDeclarator))
			throwBacktrack(firstOffset, LA(1).getEndOffset() - firstOffset);

		IASTFunctionDefinition funcDefinition = nodeFactory.newFunctionDefinition(declSpec, (IASTFunctionDeclarator) fdtor, null);
		
		try {
			IASTStatement s= handleFunctionBody();
			funcDefinition.setBody(s);
			((ASTNode) funcDefinition).setOffsetAndLength(firstOffset, calculateEndOffset(s) - firstOffset);

			return funcDefinition;
		} catch (BacktrackException bt) {
			final IASTNode n= bt.getNodeBeforeProblem();
			if (n instanceof IASTCompoundStatement) {
				funcDefinition.setBody((IASTCompoundStatement) n);
				((ASTNode) funcDefinition).setOffsetAndLength(firstOffset, calculateEndOffset(n) - firstOffset);
				throwBacktrack(bt.getProblem(), funcDefinition);
			}
			throw bt;
		}
	}


	@Override
	protected void setupTranslationUnit() throws DOMException {
		translationUnit = nodeFactory.newTranslationUnit(scanner);
		translationUnit.setIndex(index);

		// add built-in names to the scope
		if (builtinBindingsProvider != null) {
			IScope tuScope = translationUnit.getScope();
			
			IBinding[] bindings = builtinBindingsProvider.getBuiltinBindings(tuScope);
			for (IBinding binding : bindings) {
				ASTInternal.addBinding(tuScope, binding);
			}
		}
	}
	
	@Override
	protected IASTExpression expression() throws BacktrackException, EndOfFileException {
		return expression(ExprKind.eExpression);
	}

	@Override
	protected IASTExpression constantExpression() throws BacktrackException, EndOfFileException {
    	return expression(ExprKind.eConstant);
    }

    private IASTExpression expression(final ExprKind kind) throws EndOfFileException, BacktrackException {
    	final boolean allowComma= kind == ExprKind.eExpression;
    	boolean allowAssignment= kind != ExprKind.eConstant;
		int lt1;
		int conditionCount= 0;
		BinaryOperator lastOperator= null;
		IASTExpression lastExpression= castExpression(CastExprCtx.eDirectlyInBExpr, null);
		loop: while (true) {
			lt1= LT(1);
			switch(lt1) {
	        case IToken.tQUESTION:
				conditionCount++;
				// <logical-or> ? <expression> : <assignment-expression>
				// Precedence: 25 is lower than precedence of logical or; 0 is lower than precedence of expression
				lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 25, 0);  
				if (LT(2) == IToken.tCOLON) {
					// Gnu extension: The expression after '?' can be omitted.
					consume();				// Consume operator
					lastExpression= null; 	// Next cast expression is just null
					continue;
				} 
				allowAssignment= true;  // assignment expressions will be subsumed by the conditional expression
				break;
				
			case IToken.tCOLON:
				if (--conditionCount < 0) 
					break loop;
				
				// <logical-or> ? <expression> : <assignment-expression>
				// Precedence: 0 is lower than precedence of expression; 15 is lower than precedence of assignment; 
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 0, 15);  
				allowAssignment= true;  // assignment expressions will be subsumed by the conditional expression
	        	break;

			case IToken.tCOMMA:
				if (!allowComma && conditionCount == 0)
					break loop;
				// Lowest precedence except inside the conditional expression
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 10, 11);
	        	break;

	        case IToken.tASSIGN:
	        case IToken.tSTARASSIGN:
	        case IToken.tDIVASSIGN:
	        case IToken.tMODASSIGN:
	        case IToken.tPLUSASSIGN:
	        case IToken.tMINUSASSIGN:
	        case IToken.tSHIFTRASSIGN:
	        case IToken.tSHIFTLASSIGN:
	        case IToken.tAMPERASSIGN:
	        case IToken.tXORASSIGN:
	        case IToken.tBITORASSIGN:
				if (!allowAssignment && conditionCount == 0)
					break loop;
	        	// Assignments group right to left
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 21, 20); 
	        	break;
	        	
	        case IToken.tOR:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 30, 31); 
	        	break;
	        case IToken.tAND:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 40, 41);
	        	break;
	        case IToken.tBITOR:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 50, 51);
	        	break;
	        case IToken.tXOR:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 60, 61);
	        	break;
	        case IToken.tAMPER:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 70, 71);
	        	break;
	        case IToken.tEQUAL:
            case IToken.tNOTEQUAL:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 80, 81);
	        	break;
            case IToken.tGT:
            case IToken.tLT:
            case IToken.tLTEQUAL:
            case IToken.tGTEQUAL:
            case IGCCToken.tMAX:
            case IGCCToken.tMIN:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 90, 91);
	        	break;
            case IToken.tSHIFTL:
            case IToken.tSHIFTR:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 100, 101);
	        	break;
            case IToken.tPLUS:
            case IToken.tMINUS:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 110, 111);
	        	break;
            case IToken.tSTAR:
            case IToken.tDIV:
            case IToken.tMOD:
	        	lastOperator= new BinaryOperator(lastOperator, lastExpression, lt1, 120, 121);
	        	break;
            default:
            	break loop;
			}
	         
			consume(); 											// consume operator
			lastExpression= castExpression(CastExprCtx.eDirectlyInBExpr, null); 	// next cast expression
		}
		
    	// Check for incomplete conditional expression
    	if (lt1 != IToken.tEOC && conditionCount > 0)
    		throwBacktrack(LA(1));
    	
    	return buildExpression(lastOperator, lastExpression);
	}
    
    @Override
	protected IASTExpression buildBinaryExpression(int operator, IASTExpression expr1, IASTInitializerClause expr2, int lastOffset) {
        IASTBinaryExpression result = nodeFactory.newBinaryExpression(operator, expr1, (IASTExpression) expr2);
        int o = ((ASTNode) expr1).getOffset();
        ((ASTNode) result).setOffsetAndLength(o, lastOffset - o);
        return result;
    }
    
    @Override
	protected IASTExpression unaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException {
        switch (LT(1)) {
        case IToken.tSTAR:
            return unaryExpression(IASTUnaryExpression.op_star, ctx, strat);
        case IToken.tAMPER:
            return unaryExpression(IASTUnaryExpression.op_amper, ctx, strat);
        case IToken.tPLUS:
            return unaryExpression(IASTUnaryExpression.op_plus, ctx, strat);
        case IToken.tMINUS:
            return unaryExpression(IASTUnaryExpression.op_minus, ctx, strat);
        case IToken.tNOT:
            return unaryExpression(IASTUnaryExpression.op_not, ctx, strat);
        case IToken.tBITCOMPLEMENT:
            return unaryExpression(IASTUnaryExpression.op_tilde, ctx, strat);
        case IToken.tINCR:
            return unaryExpression(IASTUnaryExpression.op_prefixIncr, ctx, strat);
        case IToken.tDECR:
            return unaryExpression(IASTUnaryExpression.op_prefixDecr, ctx, strat);
        case IToken.t_sizeof:
        	return parseTypeidInParenthesisOrUnaryExpression(false, consume().getOffset(), 
        			IASTTypeIdExpression.op_sizeof, IASTUnaryExpression.op_sizeof, ctx, strat);
        case IGCCToken.t___alignof__:
        	return parseTypeidInParenthesisOrUnaryExpression(false, consume().getOffset(), 
        			IASTTypeIdExpression.op_alignof, IASTUnaryExpression.op_alignOf, ctx, strat);
        default:
            return postfixExpression(ctx, strat);
        }
    }

    private IASTExpression postfixExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException {
        IASTExpression firstExpression = null;
        switch (LT(1)) {
        case IToken.tLPAREN:
            // ( type-name ) { initializer-list }
            // ( type-name ) { initializer-list , }
        	IToken m = mark();
        	try {
        		int offset = consume().getOffset();
        		IASTTypeId t= typeId(DeclarationOptions.TYPEID);
        		consume(IToken.tRPAREN);
        		if (LT(1) == IToken.tLBRACE) {
        			IASTInitializer i = (IASTInitializerList) initClause(false);
        			firstExpression= nodeFactory.newTypeIdInitializerExpression(t, i);
        			setRange(firstExpression, offset, calculateEndOffset(i));
        			break;        
        		}
        	} catch (BacktrackException bt) {
        	}
        	backup(m); 
        	firstExpression= primaryExpression(ctx, strat);
        	break;
        	
        default:
            firstExpression = primaryExpression(ctx, strat);
        	break;
        }

        IASTExpression secondExpression = null;
        for (;;) {
            switch (LT(1)) {
            case IToken.tLBRACKET:
                // array access
                consume();
                secondExpression = expression();
                int last;
				switch (LT(1)) {
				case IToken.tRBRACKET:
					last = consume().getEndOffset();
					break;
				case IToken.tEOC:
					last = Integer.MAX_VALUE;
					break;
				default:
					throw backtrack;
				}
				
                IASTArraySubscriptExpression s = nodeFactory.newArraySubscriptExpression(firstExpression, secondExpression);
                ((ASTNode) s).setOffsetAndLength(((ASTNode) firstExpression).getOffset(),
                		last - ((ASTNode) firstExpression).getOffset());
                firstExpression = s;
                break;
            case IToken.tLPAREN:
                // function call
                int endOffset;
                List<IASTExpression> argList= null;
                consume(IToken.tLPAREN);
                boolean isFirst= true;
                while (true) {
                	final int lt1= LT(1);
                	if (lt1 == IToken.tRPAREN) {
                		endOffset= consume().getEndOffset();
                		break;
                	} else if (lt1 == IToken.tEOC) {
                		endOffset= LA(1).getEndOffset();
                		break;
                	} 
                	if (isFirst) {
                		isFirst= false;
                	} else {
                		consume(IToken.tCOMMA);
                	}
                	
                	IASTExpression expr= expression(ExprKind.eAssignment);
                	if (argList == null) {
                		argList= new ArrayList<IASTExpression>();
                	}
                	argList.add(expr);
                }
                
				final IASTExpression[] args;
				if (argList == null) { 
					args= IASTExpression.EMPTY_EXPRESSION_ARRAY;
				} else {
					args= argList.toArray(new IASTExpression[argList.size()]);
				}
				IASTFunctionCallExpression f = nodeFactory.newFunctionCallExpression(firstExpression, args);
                firstExpression = setRange(f, firstExpression, endOffset);
                break;
            case IToken.tINCR:
                int offset = consume().getEndOffset();
                firstExpression = buildUnaryExpression(
                        IASTUnaryExpression.op_postFixIncr, firstExpression,
                        ((ASTNode) firstExpression).getOffset(), offset);
                break;
            case IToken.tDECR:
                offset = consume().getEndOffset();
                firstExpression = buildUnaryExpression(
                        IASTUnaryExpression.op_postFixDecr, firstExpression,
                        ((ASTNode) firstExpression).getOffset(), offset);
                break;
            case IToken.tDOT:
                // member access
                IToken dot = consume();
                IASTName name = identifier();
                if (name == null)
                	throwBacktrack(((ASTNode) firstExpression).getOffset(), 
                			((ASTNode) firstExpression).getLength() + dot.getLength());
                IASTFieldReference result = nodeFactory.newFieldReference(name, firstExpression);
                result.setIsPointerDereference(false);
                ((ASTNode) result).setOffsetAndLength(
                        ((ASTNode) firstExpression).getOffset(),
                        calculateEndOffset(name) - ((ASTNode) firstExpression).getOffset());
                firstExpression = result;
                break;
            case IToken.tARROW:
                // member access
                IToken arrow = consume();
                name = identifier();
                if (name == null)
                	throwBacktrack(((ASTNode) firstExpression).getOffset(), 
                			((ASTNode) firstExpression).getLength() + arrow.getLength());
                result = nodeFactory.newFieldReference(name, firstExpression);
                result.setIsPointerDereference(true);
                ((ASTNode) result).setOffsetAndLength(
                        ((ASTNode) firstExpression).getOffset(),
                        calculateEndOffset(name) - ((ASTNode) firstExpression).getOffset());
                firstExpression = result;
                break;
            default:
                return firstExpression;
            }
        }
    }

    @Override
	protected IASTExpression primaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException {
        IToken t = null;
        IASTLiteralExpression literalExpression = null;
        switch (LT(1)) {
        // TO DO: we need more literals...
        case IToken.tINTEGER:
            t = consume();
            literalExpression = nodeFactory.newLiteralExpression(IASTLiteralExpression.lk_integer_constant, t.getImage());
            ((ASTNode) literalExpression).setOffsetAndLength(t.getOffset(), t.getEndOffset() - t.getOffset());
            return literalExpression;
        case IToken.tFLOATINGPT:
            t = consume();
            literalExpression = nodeFactory.newLiteralExpression(IASTLiteralExpression.lk_float_constant, t.getImage());
            ((ASTNode) literalExpression).setOffsetAndLength(t.getOffset(), t.getEndOffset() - t.getOffset());
            return literalExpression;
        case IToken.tSTRING:
        case IToken.tLSTRING:
        case IToken.tUTF16STRING:
        case IToken.tUTF32STRING:
            t = consume();
            literalExpression = nodeFactory.newLiteralExpression(IASTLiteralExpression.lk_string_literal, t.getImage());
            ((ASTNode) literalExpression).setOffsetAndLength(t.getOffset(), t.getEndOffset() - t.getOffset());
            return literalExpression;
        case IToken.tCHAR:
        case IToken.tLCHAR:
        case IToken.tUTF16CHAR:
        case IToken.tUTF32CHAR:
            t = consume();
            literalExpression = nodeFactory.newLiteralExpression(IASTLiteralExpression.lk_char_constant, t.getImage());
            ((ASTNode) literalExpression).setOffsetAndLength(t.getOffset(), t.getLength());
            return literalExpression;
        case IToken.tLPAREN:
        	if (supportStatementsInExpressions && LT(2) == IToken.tLBRACE) {
        		return compoundStatementExpression();
        	}
            t = consume();
            IASTExpression lhs = expression(ExprKind.eExpression); // instead of expression(), to keep the stack smaller
            int finalOffset = 0;
            switch (LT(1)) {
            case IToken.tRPAREN:
            case IToken.tEOC:
                finalOffset = consume().getEndOffset();
                break;
            default:
                throwBacktrack(LA(1));
            }
            return buildUnaryExpression(IASTUnaryExpression.op_bracketedPrimary, lhs, t.getOffset(), finalOffset);
        case IToken.tIDENTIFIER:
        case IToken.tCOMPLETION:
        case IToken.tEOC:
            int startingOffset = LA(1).getOffset();
            IASTName name = identifier();
            IASTIdExpression idExpression = nodeFactory.newIdExpression(name);
            ((ASTNode) idExpression).setOffsetAndLength((ASTNode) name);
            return idExpression;
        default:
            IToken la = LA(1);
            startingOffset = la.getOffset();
            throwBacktrack(startingOffset, la.getLength());
            return null;
        }

    }


    @Override
	protected IASTTypeId typeId(DeclarationOptions option) throws EndOfFileException, BacktrackException {
    	if (!canBeTypeSpecifier()) {
    		return null;
    	}
    	final int offset = mark().getOffset();
        IASTDeclSpecifier declSpecifier = null;
        IASTDeclarator declarator = null;

    	fPreventKnrCheck++;
        try {
        	Decl decl= declSpecifierSequence_initDeclarator(option, false);
        	declSpecifier= decl.fDeclSpec1;
        	declarator= decl.fDtor1;
        } catch (FoundAggregateInitializer lie) {
        	// type-ids have not compound initializers
        	throwBacktrack(lie.fDeclarator);
        } finally {
        	fPreventKnrCheck--;
        }

        IASTTypeId result = nodeFactory.newTypeId(declSpecifier, declarator);
        setRange(result, offset, figureEndOffset(declSpecifier, declarator));
        return result;
    }

    /**
     * Parse a Pointer Operator.
     * 
     * ptrOperator : "*" (cvQualifier)* | "&" | ::? nestedNameSpecifier "*"
     * (cvQualifier)*
     * 
     * @throws BacktrackException to request a backtrack
     */
    protected void consumePointerOperators(List<IASTPointerOperator> pointerOps)
            throws EndOfFileException, BacktrackException {
        for (;;) {
        	// __attribute__ in-between pointers
            __attribute_decl_seq(supportAttributeSpecifiers, false);

            IToken mark = mark();
            IToken last = null;

            boolean isConst = false, isVolatile = false, isRestrict = false;

            if (LT(1) != IToken.tSTAR) {
                backup(mark);
                break;
            }

            last = consume();
            int startOffset = mark.getOffset();
            for (;;) {
                IToken t = LA(1);
                switch (LT(1)) {
                case IToken.t_const:
                    last = consume();
                    isConst = true;
                    break;
                case IToken.t_volatile:
                    last = consume();
                    isVolatile = true;
                    break;
                case IToken.t_restrict:
                    last = consume();
                    isRestrict = true;
                    break;
                }

                if (t == LA(1))
                    break;
            }

            ICASTPointer po = nodeFactory.newPointer();
            ((ASTNode) po).setOffsetAndLength(startOffset, last.getEndOffset() - startOffset);
            po.setConst(isConst);
            po.setVolatile(isVolatile);
            po.setRestrict(isRestrict);
            pointerOps.add(po);
        }
    }


	private final static int INLINE=0x1, CONST=0x2, RESTRICT=0x4, VOLATILE=0x8, 
	    SHORT=0x10,	UNSIGNED= 0x20, SIGNED=0x40, COMPLEX=0x80, IMAGINARY=0x100;

    @Override
	protected Decl declSpecifierSeq(final DeclarationOptions declOption) throws BacktrackException, EndOfFileException {
        int storageClass= IASTDeclSpecifier.sc_unspecified;
        int simpleType= IASTSimpleDeclSpecifier.t_unspecified;
        int options= 0;
        int isLong= 0;

        IToken returnToken= null;
    	IASTDeclSpecifier result= null;
    	IASTDeclSpecifier altResult= null;
    	try {
    		IASTName identifier= null;
    		IASTExpression typeofExpression= null;
    		IASTProblem problem= null;
        
    		boolean encounteredRawType= false;
    		boolean encounteredTypename= false;

    		final int offset= LA(1).getOffset();
    		int endOffset= offset;

    		declSpecifiers: for (;;) {
    			final int lt1= LTcatchEOF(1);
    			switch (lt1) {
    			case 0: // eof
    				break declSpecifiers;
    				// storage class specifiers
    			case IToken.t_auto:
    				storageClass = IASTDeclSpecifier.sc_auto;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_register:
    				storageClass = IASTDeclSpecifier.sc_register;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_static:
    				storageClass = IASTDeclSpecifier.sc_static;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_extern:
    				storageClass = IASTDeclSpecifier.sc_extern;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_typedef:
    				storageClass = IASTDeclSpecifier.sc_typedef;
    				endOffset= consume().getEndOffset();
    				break;

    				// Function Specifier
    			case IToken.t_inline:
    				options |= INLINE;
    				endOffset= consume().getEndOffset();
    				break;

    				// Type Qualifiers
    			case IToken.t_const:
    				options |= CONST;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_volatile:
    				options |= VOLATILE;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_restrict:
    				options |= RESTRICT;
    				endOffset= consume().getEndOffset();
    				break;

    				// Type Specifiers
    			case IToken.t_void:
    				if (encounteredTypename)
    					break declSpecifiers;
    				simpleType = IASTSimpleDeclSpecifier.t_void;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_char:
    				if (encounteredTypename)
    					break declSpecifiers;
    				simpleType = IASTSimpleDeclSpecifier.t_char;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_short:
    				if (encounteredTypename)
    					break declSpecifiers;
    				options |= SHORT;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_int:
    				if (encounteredTypename)
    					break declSpecifiers;
    				simpleType = IASTSimpleDeclSpecifier.t_int;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_long:
    				if (encounteredTypename)
    					break declSpecifiers;
    				isLong++;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_float:
    				if (encounteredTypename)
    					break declSpecifiers;
    				simpleType = IASTSimpleDeclSpecifier.t_float;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_double:
    				if (encounteredTypename)
    					break declSpecifiers;
    				simpleType = IASTSimpleDeclSpecifier.t_double;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_signed:
    				if (encounteredTypename)
    					break declSpecifiers;
    				options |= SIGNED;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t_unsigned:
    				if (encounteredTypename)
    					break declSpecifiers;
    				options |= UNSIGNED;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t__Bool:
    				if (encounteredTypename)
    					break declSpecifiers;
    				simpleType = IASTSimpleDeclSpecifier.t_bool;
    				encounteredRawType= true;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t__Complex:
    				if (encounteredTypename)
    					break declSpecifiers;
    				options |= COMPLEX;
    				endOffset= consume().getEndOffset();
    				break;
    			case IToken.t__Imaginary:
    				if (encounteredTypename)
    					break declSpecifiers;
    				options |= IMAGINARY;
    				endOffset= consume().getEndOffset();
    				break;

    			case IToken.tIDENTIFIER:
    			case IToken.tCOMPLETION:
    			case IToken.tEOC:
    				if (encounteredTypename || encounteredRawType) 
    					break declSpecifiers;

    				if ((endOffset != offset || declOption.fAllowEmptySpecifier) && LT(1) != IToken.tCOMPLETION) {
    					altResult= buildSimpleDeclSpec(storageClass, simpleType, options, isLong, typeofExpression, offset, endOffset);
    					returnToken= mark();
    				}

    				identifier = identifier();
    				endOffset= calculateEndOffset(identifier);
    				encounteredTypename= true;
    				break;
    			case IToken.t_struct:
    			case IToken.t_union:
    				if (encounteredTypename || encounteredRawType)
    					break declSpecifiers;
    				try {
    					result= structOrUnionSpecifier();
    				} catch (BacktrackException bt) {
    					result= elaboratedTypeSpecifier();
    				}
    				endOffset= calculateEndOffset(result);
    				encounteredTypename= true;
    				break;
    			case IToken.t_enum:
    				if (encounteredTypename || encounteredRawType)
    					break declSpecifiers;
    				try {
    					result= enumSpecifier();
    				} catch (BacktrackException bt) {
    					if (bt.getNodeBeforeProblem() instanceof IASTDeclSpecifier) {
    						result= (IASTDeclSpecifier) bt.getNodeBeforeProblem();
    						problem = bt.getProblem();
    						break declSpecifiers;
    					} else {
    						result= elaboratedTypeSpecifier();
    					}
    				}
    				endOffset= calculateEndOffset(result);
    				encounteredTypename= true;
    				break;

    			case IGCCToken.t__attribute__: // if __attribute__ is after the declSpec
	    			if (!supportAttributeSpecifiers)
	    				throwBacktrack(LA(1));
	    			__attribute_decl_seq(true, false);
	    			break;
    			case IGCCToken.t__declspec: // __declspec precedes the identifier
	    			if (identifier != null || !supportDeclspecSpecifiers)
	    				throwBacktrack(LA(1));
	    			__attribute_decl_seq(false, true);
	    			break;

    			case IGCCToken.t_typeof:
    				if (encounteredRawType || encounteredTypename)
    					throwBacktrack(LA(1));

    				simpleType= IASTSimpleDeclSpecifier.t_typeof;
    				consume(IGCCToken.t_typeof);
    				typeofExpression = parseTypeidInParenthesisOrUnaryExpression(false, LA(1).getOffset(), 
    						IASTTypeIdExpression.op_typeof, -1, CastExprCtx.eNotInBExpr, null);

    				encounteredTypename= true;
    				endOffset= calculateEndOffset(typeofExpression);
    				break;

    			default:
    				if (lt1 >= IExtensionToken.t__otherDeclSpecModifierFirst && lt1 <= IExtensionToken.t__otherDeclSpecModifierLast) {
    					handleOtherDeclSpecModifier();
    					endOffset= LA(1).getOffset();
    					break;
    				}
    				break declSpecifiers;
    			}
            
    			if (encounteredRawType && encounteredTypename)
    				throwBacktrack(LA(1));
    		}

    		// check for empty specification
    		if (!encounteredRawType && !encounteredTypename && LT(1) != IToken.tEOC && !declOption.fAllowEmptySpecifier) {
    			if (offset == endOffset) {
    				throwBacktrack(LA(1));
    			}
    		}
        
    		if (result != null) {
    			configureDeclSpec(result, storageClass, options);
    			if ((options & RESTRICT) != 0) {
    				if (result instanceof ICASTCompositeTypeSpecifier) {
    					((ICASTCompositeTypeSpecifier) result).setRestrict(true);
    				} else if (result instanceof CASTEnumerationSpecifier) {
    					((CASTEnumerationSpecifier) result).setRestrict(true);
    				} else if (result instanceof CASTElaboratedTypeSpecifier) {
    					((CASTElaboratedTypeSpecifier) result).setRestrict(true);
    				}
    			}
    			setRange(result, offset, endOffset);
    			if (problem != null)
    				throwBacktrack(problem, result);
    		} else if (identifier != null) { 
    			result= buildNamedTypeSpecifier(identifier, storageClass, options, offset, endOffset);
    		} else {
    			result= buildSimpleDeclSpec(storageClass, simpleType, options, isLong, typeofExpression, offset, endOffset);
    		}
        } catch (BacktrackException e) {
        	if (returnToken != null) {
        		backup(returnToken);
        		result= altResult;
        		altResult= null;
        		returnToken= null;
        	} else {
        		throw e;
        	}
        }
        Decl target= new Decl();
        target.fDeclSpec1= result;
        target.fDeclSpec2= altResult;
        target.fDtorToken1= returnToken;
        return target;
    }

	private ICASTTypedefNameSpecifier buildNamedTypeSpecifier(IASTName name, int storageClass,
			int options, int offset, int endOffset) {
		ICASTTypedefNameSpecifier declSpec = nodeFactory.newTypedefNameSpecifier(name);
		configureDeclSpec(declSpec, storageClass, options);
		declSpec.setRestrict((options & RESTRICT) != 0);
        ((ASTNode) declSpec).setOffsetAndLength(offset, endOffset - offset);
        return declSpec;
	}

	private ICASTSimpleDeclSpecifier buildSimpleDeclSpec(int storageClass, int simpleType,
			int options, int isLong, IASTExpression typeofExpression, int offset, int endOffset) {
		ICASTSimpleDeclSpecifier declSpec= nodeFactory.newSimpleDeclSpecifier();
		
    	configureDeclSpec(declSpec, storageClass, options);
		declSpec.setType(simpleType);
		declSpec.setLong(isLong == 1);
		declSpec.setLongLong(isLong > 1);
		declSpec.setRestrict((options & RESTRICT) != 0);
		declSpec.setUnsigned((options & UNSIGNED) != 0);
		declSpec.setSigned((options & SIGNED) != 0);
		declSpec.setShort((options & SHORT) != 0);
		declSpec.setComplex((options & COMPLEX) != 0);
		declSpec.setImaginary((options & IMAGINARY) != 0);
		if (typeofExpression != null) {
			declSpec.setDeclTypeExpression(typeofExpression);
			typeofExpression.setParent(declSpec);
		}

		((ASTNode) declSpec).setOffsetAndLength(offset, endOffset - offset);
		return declSpec;
	}

	private void configureDeclSpec(IASTDeclSpecifier declSpec, int storageClass, int options) {
		declSpec.setStorageClass(storageClass);
		declSpec.setConst((options & CONST) != 0);
		declSpec.setVolatile((options & VOLATILE) != 0);
		declSpec.setInline((options & INLINE) != 0);
	}

    /**
     * Parse a class/struct/union definition.
     * 
     * classSpecifier : classKey name (baseClause)? "{" (memberSpecification)*
     * "}"
     * 
     * @throws BacktrackException to request a backtrack
     */
    protected ICASTCompositeTypeSpecifier structOrUnionSpecifier() throws BacktrackException, EndOfFileException {
        int classKind = 0;
        IToken mark= mark();
        final int offset= mark.getOffset();
        
        // class key
        switch (LT(1)) {
        case IToken.t_struct:
            consume();
            classKind = IASTCompositeTypeSpecifier.k_struct;
            break;
        case IToken.t_union:
            consume();
            classKind = IASTCompositeTypeSpecifier.k_union;
            break;
        default:
            throwBacktrack(LA(1));
        	return null; // line never reached, hint for the parser.
        }

        // if __attribute__ or __declspec occurs after struct/union/class and before the identifier
        __attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers);
        
        // class name
        IASTName name = null;
        if (LT(1) == IToken.tIDENTIFIER) {
            name = identifier();
        }

        // if __attribute__ or __declspec occurs after struct/union/class identifier and before the { or ;        
        __attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers);
        
        if (LT(1) != IToken.tLBRACE) {
            IToken errorPoint = LA(1);
            backup(mark);
            throwBacktrack(errorPoint);
        }

        if (name == null) {
        	name= nodeFactory.newName();
        }
        ICASTCompositeTypeSpecifier result = nodeFactory.newCompositeTypeSpecifier(classKind, name);
        declarationListInBraces(result, offset, DeclarationOptions.C_MEMBER);
        return result;
    }

    protected IASTElaboratedTypeSpecifier elaboratedTypeSpecifier() throws BacktrackException, EndOfFileException {
        // this is an elaborated class specifier
        IToken t = consume();
        int eck = 0;

        switch (t.getType()) {
        case IToken.t_struct:
            eck = IASTElaboratedTypeSpecifier.k_struct;
            break;
        case IToken.t_union:
            eck = IASTElaboratedTypeSpecifier.k_union;
            break;
        case IToken.t_enum:
            eck = IASTElaboratedTypeSpecifier.k_enum;
            break;
        default:
            backup(t);
            throwBacktrack(t.getOffset(), t.getLength());
        }
        
        // if __attribute__ or __declspec occurs after struct/union/class and before the identifier
        __attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers);

        IASTName name = identifier();
        IASTElaboratedTypeSpecifier result = nodeFactory.newElaboratedTypeSpecifier(eck, name);
        ((ASTNode) result).setOffsetAndLength(t.getOffset(), calculateEndOffset(name) - t.getOffset());
        return result;
    }

    @Override
	protected IASTDeclarator initDeclarator(IASTDeclSpecifier declspec, final DeclarationOptions option) 
    		throws EndOfFileException, BacktrackException, FoundAggregateInitializer {
        IASTDeclarator d = declarator(declspec, option);

		final int lt1= LTcatchEOF(1);
		if (lt1 == IToken.tLBRACE) {
			if (!(ASTQueries.findTypeRelevantDeclarator(d) instanceof IASTFunctionDeclarator)) {
				throwBacktrack(LA(1));
			}
		}
		
        if (lt1 == IToken.tASSIGN && LT(2) == IToken.tLBRACE) 
        	throw new FoundAggregateInitializer(declspec, d);
       
        IASTInitializer i = optionalInitializer(d, option);
        if (i != null) {
            d.setInitializer(i);
            ((ASTNode) d).setLength(calculateEndOffset(i) - ((ASTNode) d).getOffset());
        }
        return d;
    }
    
    protected IASTDeclarator declarator(IASTDeclSpecifier declSpec, DeclarationOptions option) throws EndOfFileException, BacktrackException {
        final int startingOffset = LA(1).getOffset();
        int endOffset = startingOffset;

        List<IASTPointerOperator> pointerOps = new ArrayList<IASTPointerOperator>(DEFAULT_POINTEROPS_LIST_SIZE);
        consumePointerOperators(pointerOps);
        if (!pointerOps.isEmpty()) {
        	endOffset = calculateEndOffset(pointerOps.get(pointerOps.size() - 1));
        }
            
        // Accept __attribute__ or __declspec between pointer operators and declarator.
        List<IASTAttribute> attributes =
        		__attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers);
        
        // Look for identifier or nested declarator
        final int lt1= LT(1);
        if (lt1 == IToken.tIDENTIFIER) {
        	if (option.fRequireAbstract)
        		throwBacktrack(LA(1));

        	final IASTName declaratorName = identifier();
        	endOffset= calculateEndOffset(declaratorName);
        	return declarator(pointerOps, attributes, declaratorName, null, startingOffset,
        			endOffset, option);
        } 
        
        if (lt1 == IToken.tLPAREN) {
        	IASTDeclarator cand1= null;
        	IToken cand1End= null;
        	// try an abstract function declarator 
        	if (option.fAllowAbstract) {
        		final IToken mark= mark();
        		try {
        			cand1= declarator(pointerOps, attributes, nodeFactory.newName(), null,
        					startingOffset, endOffset, option);
            		if (option.fRequireAbstract) 
            			return cand1;

            		cand1End= LA(1);
        		} catch (BacktrackException e) {
        		}
        		backup(mark);
        	}
        	// try a nested declarator
        	try {
        		consume();
        		if (LT(1) == IToken.tRPAREN)
        			throwBacktrack(LA(1));
        		
        		final IASTDeclarator nested= declarator(declSpec, option);
        		endOffset= consume(IToken.tRPAREN).getEndOffset();
        		final IASTDeclarator cand2= declarator(pointerOps, attributes, null, nested,
        				startingOffset, endOffset, option);
        		if (cand1 == null || cand1End == null)
        			return cand2;
        		final IToken cand2End= LA(1);
        		if (cand1End == cand2End) {
        			CASTAmbiguousDeclarator result= new CASTAmbiguousDeclarator(cand1, cand2);
                    ((ASTNode) result).setOffsetAndLength((ASTNode) cand1);
                    return result;
        		}
        		// use the longer variant
        		if (cand1End.getOffset() < cand2End.getOffset()) 
        			return cand2;
        		
        	} catch (BacktrackException e) {
        		if (cand1 == null)
        			throw e;
        	}
    		backup(cand1End);
    		return cand1;
        }
        
        // try abstract declarator
        if (!option.fAllowAbstract) {
        	throwBacktrack(LA(1));
        }
        return declarator(pointerOps, attributes, nodeFactory.newName(), null, startingOffset,
        		endOffset, option);
    }
        
	private IASTDeclarator declarator(final List<IASTPointerOperator> pointerOps,
			List<IASTAttribute> attributes, final IASTName declaratorName,
			final IASTDeclarator nestedDeclarator, final int startingOffset, int endOffset, 
			final DeclarationOptions option) throws EndOfFileException, BacktrackException {
        IASTDeclarator result= null;
        int lt1;
        loop: while (true) {
        	lt1= LTcatchEOF(1);
        	switch (lt1) {
        	case IToken.tLPAREN:
        		result= functionDeclarator(isAbstract(declaratorName, nestedDeclarator) 
        				? DeclarationOptions.PARAMETER : DeclarationOptions.C_PARAMETER_NON_ABSTRACT);
        		setDeclaratorID(result, declaratorName, nestedDeclarator);
        		break loop;
        		
        	case IToken.tLBRACKET:
        		result= arrayDeclarator();
        		setDeclaratorID(result, declaratorName, nestedDeclarator);
        		break loop;
        		
        	case IToken.tCOLON:
        		if (!option.fAllowBitField)
        			throwBacktrack(LA(1));
        		
        		result= bitFieldDeclarator();
        		setDeclaratorID(result, declaratorName, nestedDeclarator);
        		break loop;
        		
        	case IGCCToken.t__attribute__: // if __attribute__ is after a declarator
        		if (!supportAttributeSpecifiers)
        			throwBacktrack(LA(1));
        		attributes = CollectionUtils.merge(attributes,
        				__attribute_decl_seq(true, supportDeclspecSpecifiers));
        		break;
        	case IGCCToken.t__declspec:
        		if (!supportDeclspecSpecifiers)
        			throwBacktrack(LA(1));
        		attributes = CollectionUtils.merge(attributes,
        				__attribute_decl_seq(supportAttributeSpecifiers, true));
        		break;
        	default:
        		break loop;
        	}
        }
        if (lt1 != 0) {
    		attributes = CollectionUtils.merge(attributes,
    				__attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers));
        }

        if (result == null) {
        	result= nodeFactory.newDeclarator(null);
        	setDeclaratorID(result, declaratorName, nestedDeclarator);
        } else {
        	endOffset= calculateEndOffset(result);
        }

        if (lt1 != 0 && LT(1) == IToken.t_asm) { // asm labels bug 226121
    		consume();
    		endOffset= asmExpression(null).getEndOffset();

    		__attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers);
    	}

        for (IASTPointerOperator po : pointerOps) {
			result.addPointerOperator(po);
		}

        if (attributes != null) {
        	for (IASTAttribute attribute : attributes) {
        		result.addAttribute(attribute);
        	}
        }

        ((ASTNode) result).setOffsetAndLength(startingOffset, endOffset - startingOffset);
        return result;
    }

	private boolean isAbstract(IASTName declaratorName, IASTDeclarator nestedDeclarator) {
		nestedDeclarator= ASTQueries.findInnermostDeclarator(nestedDeclarator);
		if (nestedDeclarator != null) {
			declaratorName= nestedDeclarator.getName();
		}
		return declaratorName == null || declaratorName.toCharArray().length == 0;
	}

	private void setDeclaratorID(IASTDeclarator declarator, IASTName declaratorName, IASTDeclarator nestedDeclarator) {
		if (nestedDeclarator != null) { 
			declarator.setNestedDeclarator(nestedDeclarator);
			declarator.setName(nodeFactory.newName());
		} else {
			declarator.setName(declaratorName);
		}
	}

	private IASTDeclarator functionDeclarator(DeclarationOptions paramOption) throws EndOfFileException, BacktrackException {
		IToken last = consume(IToken.tLPAREN);
		int startOffset= last.getOffset();
		
		// check for K&R C parameters (0 means it's not K&R C)
		if (fPreventKnrCheck == 0 && supportKnRC) {
			fPreventKnrCheck++;
			try {
				final int numKnRCParms = countKnRCParms();
				if (numKnRCParms > 0) { // KnR C parameters were found
					IASTName[] parmNames = new IASTName[numKnRCParms];
					IASTDeclaration[] parmDeclarations = new IASTDeclaration[numKnRCParms];

					boolean seenParameter= false;
					for (int i = 0; i <= parmNames.length; i++) {
						switch (LT(1)) {
						case IToken.tCOMMA:
							last = consume();
							parmNames[i] = identifier();
							seenParameter = true;
							break;
						case IToken.tIDENTIFIER:
							if (seenParameter)
								throwBacktrack(startOffset, last.getEndOffset() - startOffset);

							parmNames[i] = identifier();
							seenParameter = true;
							break;
						case IToken.tRPAREN:
							last = consume();
							break;
						default:
							break;
						}
					}

					// now that the parameter names are parsed, parse the parameter declarations
					// count for parameter declarations <= count for parameter names.
					int endOffset= last.getEndOffset();
					for (int i = 0; i < numKnRCParms && LT(1) != IToken.tLBRACE; i++) {
						try {
							IASTDeclaration decl= simpleDeclaration(DeclarationOptions.LOCAL);
							IASTSimpleDeclaration ok= checkKnrParameterDeclaration(decl, parmNames);
							if (ok != null) {
								parmDeclarations[i]= ok;
								endOffset= calculateEndOffset(ok);
							} else {
								final ASTNode node = (ASTNode) decl;
								parmDeclarations[i] = createKnRCProblemDeclaration(node.getOffset(), node.getLength());
								endOffset= calculateEndOffset(node);
							}
						} catch (BacktrackException b) {
							parmDeclarations[i] = createKnRCProblemDeclaration(b.getOffset(), b.getLength());
							endOffset= b.getOffset() + b.getLength();
						}
					}

					parmDeclarations = ArrayUtil.removeNulls( IASTDeclaration.class, parmDeclarations );
		            ICASTKnRFunctionDeclarator functionDecltor = nodeFactory.newKnRFunctionDeclarator(parmNames, parmDeclarations);
		            ((ASTNode) functionDecltor).setOffsetAndLength(startOffset, endOffset-startOffset);
		            return functionDecltor;
				}
			} finally {
				fPreventKnrCheck--;
			}
		}

		boolean seenParameter= false;
		boolean encounteredVarArgs= false;
		List<IASTParameterDeclaration> parameters= null;
		int endOffset= last.getEndOffset();
		
		paramLoop: while (true) {
			switch (LT(1)) {
			case IToken.tRPAREN:
			case IToken.tEOC:
				endOffset= consume().getEndOffset();
				break paramLoop;
			case IToken.tELLIPSIS:
				endOffset= consume().getEndOffset();
				encounteredVarArgs = true;
				break;
			case IToken.tCOMMA:
				endOffset= consume().getEndOffset();
				seenParameter = false;
				break;
			default:
				if (seenParameter)
					throwBacktrack(startOffset, endOffset - startOffset);
			
				IASTParameterDeclaration pd = parameterDeclaration(paramOption);
				endOffset = calculateEndOffset(pd);
				if (parameters == null)
					parameters = new ArrayList<IASTParameterDeclaration>(DEFAULT_PARAMETERS_LIST_SIZE);
				parameters.add(pd);
				seenParameter = true;
				break;
			}
		}
		IASTStandardFunctionDeclarator fc = nodeFactory.newFunctionDeclarator(null);
		fc.setVarArgs(encounteredVarArgs);
		if (parameters != null) {
			for (IASTParameterDeclaration pd : parameters) {
				fc.addParameterDeclaration(pd);
			}
		}
        ((ASTNode) fc).setOffsetAndLength(startOffset, endOffset-startOffset);
        return fc;
	}

	private IASTSimpleDeclaration checkKnrParameterDeclaration(IASTDeclaration decl, final IASTName[] parmNames) {
		if (!(decl instanceof IASTSimpleDeclaration)) 
			return null;
		
		IASTSimpleDeclaration declaration= ((IASTSimpleDeclaration) decl);
		IASTDeclarator[] decltors = declaration.getDeclarators();
		for (IASTDeclarator decltor : decltors) {
			boolean decltorOk = false;
			final char[] nchars = ASTQueries.findInnermostDeclarator(decltor).getName().toCharArray();
			for (IASTName parmName : parmNames) {
				if (CharArrayUtils.equals(nchars, parmName.toCharArray())) {
					decltorOk= true;
					break;
				}
			}
			if (!decltorOk)
				return null;
		}
		return declaration;
	}

	/**
	 * Parse an array declarator starting at the square bracket.
	 */
	private IASTArrayDeclarator arrayDeclarator() throws EndOfFileException, BacktrackException {
		ArrayList<IASTArrayModifier> arrayMods = new ArrayList<IASTArrayModifier>(DEFAULT_POINTEROPS_LIST_SIZE);
		int start= LA(1).getOffset();
		consumeArrayModifiers(arrayMods);
		if (arrayMods.isEmpty())
			throwBacktrack(LA(1));
		
		final int endOffset = calculateEndOffset(arrayMods.get(arrayMods.size() - 1));
		final IASTArrayDeclarator d = nodeFactory.newArrayDeclarator(null);
		for (IASTArrayModifier m : arrayMods) {
            d.addArrayModifier(m);
        }
		
		((ASTNode) d).setOffsetAndLength(start, endOffset-start);
		return d;
	}
	
	
	/**
	 * Parses for a bit field declarator starting with the colon
	 */
	private IASTFieldDeclarator bitFieldDeclarator() throws EndOfFileException, BacktrackException {
		int start= consume(IToken.tCOLON).getOffset();
		
		final IASTExpression bitField = constantExpression();
		final int endOffset = calculateEndOffset(bitField);
		
        IASTFieldDeclarator d = nodeFactory.newFieldDeclarator(null, bitField);
        d.setBitFieldSize(bitField);

        ((ASTNode) d).setOffsetAndLength(start, endOffset-start);
		return d;
	}
	
	
    @Override
	protected IASTName identifier() throws EndOfFileException, BacktrackException {
    	final IToken t= LA(1);
    	IASTName n;
    	switch (t.getType()) {
    	case IToken.tIDENTIFIER:
    		consume();
            n = nodeFactory.newName(t.getCharImage());
            break;
            
    	case IToken.tCOMPLETION:
    	case IToken.tEOC:
    		consume();
            n = nodeFactory.newName(t.getCharImage());
            createCompletionNode(t).addName(n);
    		return n;
    		
    	default:
    		throw backtrack;
    	}
    	
        setRange(n, t.getOffset(), t.getEndOffset());
        return n;
    }

	protected void consumeArrayModifiers(List<IASTArrayModifier> arrayMods) throws EndOfFileException, BacktrackException {
        while (LT(1) == IToken.tLBRACKET) {
            // eat the '['
            int startOffset = consume().getOffset();

            boolean isStatic = false;
            boolean isConst = false;
            boolean isRestrict = false;
            boolean isVolatile = false;
            boolean isVarSized = false;

            outerLoop: do {
                switch (LT(1)) {
                case IToken.t_static:
                    isStatic = true;
                    consume();
                    break;
                case IToken.t_const:
                    isConst = true;
                    consume();
                    break;
                case IToken.t_volatile:
                    isVolatile = true;
                    consume();
                    break;
                case IToken.t_restrict:
                    isRestrict = true;
                    consume();
                    break;
                case IToken.tSTAR:
                    isVarSized = true;
                    consume();
                    break outerLoop;
                default:
                    break outerLoop;
                }
            } while (true);

            IASTExpression exp = null;

            if (LT(1) != IToken.tRBRACKET) {
                if (!(isStatic || isRestrict || isConst || isVolatile))
                    exp = expression(ExprKind.eAssignment);
                else
                    exp = constantExpression();
            }
            int lastOffset;
			switch (LT(1)) {
			case IToken.tRBRACKET:
				lastOffset = consume().getEndOffset();
				break;
			case IToken.tEOC:
				lastOffset = Integer.MAX_VALUE;
				break;
			default:
				throw backtrack;
			}

			ICASTArrayModifier arrayMod = nodeFactory.newArrayModifier(exp);
			arrayMod.setStatic(isStatic);
			arrayMod.setConst(isConst);
			arrayMod.setVolatile(isVolatile);
			arrayMod.setRestrict(isRestrict);
			arrayMod.setVariableSized(isVarSized);
            ((ASTNode) arrayMod).setOffsetAndLength(startOffset, lastOffset - startOffset);
            arrayMods.add(arrayMod);
        }
    }


    protected IASTParameterDeclaration parameterDeclaration(DeclarationOptions option) throws BacktrackException, EndOfFileException {
        final IToken current = LA(1);
        int startingOffset = current.getOffset();
        if (current.getType() == IToken.tLBRACKET && supportParameterInfoBlock) {
        	skipBrackets(IToken.tLBRACKET, IToken.tRBRACKET, 0);
        }

        IASTDeclSpecifier declSpec = null;
        IASTDeclarator declarator = null;
        IASTDeclSpecifier altDeclSpec = null;
        IASTDeclarator altDeclarator = null;
        
        try {
        	fPreventKnrCheck++;
        	Decl decl= declSpecifierSequence_initDeclarator(option, false);
        	declSpec= decl.fDeclSpec1;
        	declarator= decl.fDtor1;
        	altDeclSpec= decl.fDeclSpec2;
        	altDeclarator= decl.fDtor2;
        } catch (FoundAggregateInitializer lie) {
        	declSpec= lie.fDeclSpec;
        	declarator= lie.fDeclarator;
        } finally {
        	fPreventKnrCheck--;
        }

        final int length = figureEndOffset(declSpec, declarator) - startingOffset;
        IASTParameterDeclaration result = nodeFactory.newParameterDeclaration(declSpec, declarator);
		((ASTNode) result).setOffsetAndLength(startingOffset, length);
        if (altDeclarator != null && altDeclSpec != null) {
            IASTParameterDeclaration alt = nodeFactory.newParameterDeclaration(altDeclSpec, altDeclarator);
    		((ASTNode) alt).setOffsetAndLength(startingOffset, length);
            // order is important, prefer variant with declspec over the one without
            result= new CASTAmbiguousParameterDeclaration(result, alt);
            ((ASTNode) result).setOffsetAndLength((ASTNode) alt);
        }
        return result;
    }


    @Override
	protected IASTTranslationUnit getTranslationUnit() {
        return translationUnit;
    }


    @Override
	protected IASTStatement statement() throws EndOfFileException, BacktrackException {
        switch (LT(1)) {
        // labeled statements
        case IToken.t_case:
            return parseCaseStatement();
        case IToken.t_default:
            return parseDefaultStatement();
        // compound statement
        case IToken.tLBRACE:
            return parseCompoundStatement();
        // selection statement
        case IToken.t_if:
            return parseIfStatement();
        case IToken.t_switch:
            return parseSwitchStatement();
        // iteration statements
        case IToken.t_while:
            return parseWhileStatement();
        case IToken.t_do:
            return parseDoStatement();
        case IToken.t_for:
            return parseForStatement();
        // jump statement
        case IToken.t_break:
            return parseBreakStatement();
        case IToken.t_continue:
            return parseContinueStatement();
        case IToken.t_return:
            return parseReturnStatement();
        case IToken.t_goto:
            return parseGotoStatement();
        case IToken.tSEMI:
            return parseNullStatement();
        default:
            // can be many things:
            // label
            if (LT(1) == IToken.tIDENTIFIER && LT(2) == IToken.tCOLON) {
                return parseLabelStatement();
            }

            return parseDeclarationOrExpressionStatement();
        }
    }

    @Override
	protected void nullifyTranslationUnit() {
        translationUnit = null;
    }


    private int countKnRCParms() {
        IToken mark = null;
        int parmCount = 0;
        boolean previousWasIdentifier = false;

        try {
            mark = mark();

            // starts at the beginning of the parameter list
            for (;;) {
                if (LT(1) == IToken.tCOMMA) {
                    consume();
                    previousWasIdentifier = false;
                } else if (LT(1) == IToken.tIDENTIFIER) {
                    consume();
                    if (previousWasIdentifier) {
                        backup(mark);
                        return 0; // i.e. KnR C won't have int f(typedef x)
                                    // char
                        // x; {}
                    }
                    previousWasIdentifier = true;
                    parmCount++;
                } else if (LT(1) == IToken.tRPAREN) {
                	if (!previousWasIdentifier) { 
                		// if the first token encountered is tRPAREN then it's not K&R C
                		// the first token when counting K&R C parms is always an identifier
                		backup(mark);
                		return 0;
                	}
                    consume();
                    break;
                } else {
                    backup(mark);
                    return 0; // i.e. KnR C won't have int f(char) char x; {}
                }
            }

            // if the next token is a tSEMI then the declaration was a regular
            // declaration statement i.e. int f(type_def);
            final int lt1= LT(1);
            if (lt1 == IToken.tSEMI || lt1 == IToken.tLBRACE) {
                backup(mark);
                return 0;
            }

            // look ahead for the start of the function body, if end of file is
            // found then return 0 parameters found (implies not KnR C)
            int previous=-1;
            while (LT(1) != IToken.tLBRACE) {
            	// fix for 100104: check if the parameter declaration is a valid one
            	try {
            		simpleDeclaration(DeclarationOptions.LOCAL);
				} catch (BacktrackException e) {
					backup(mark);
					return 0;
				}            	
            	
               	final IToken t = LA(1);
               	if (t.getType() == IToken.tEOC)
               		break;
               	
               	final int next = t.hashCode();
               	if (next == previous) { // infinite loop detected
               		break;
               	}
               	previous = next;
            }

            backup(mark);
            return parmCount;
        } catch (EndOfFileException eof) {
            if (mark != null)
                backup(mark);

            return 0;
        }
    }

    private IASTProblemDeclaration createKnRCProblemDeclaration(int offset, int length) throws EndOfFileException {
        IASTProblem p = createProblem(IProblem.SYNTAX_ERROR, offset, length);
        IASTProblemDeclaration pd = nodeFactory.newProblemDeclaration(p);
        ((ASTNode) pd).setOffsetAndLength((ASTNode) p);

        // consume until LBRACE is found (to leave off at the function body and
        // continue from there)
        IToken previous=null;
        IToken next=null;
        while (LT(1) != IToken.tLBRACE) {
           	next = consume();
           	if (next == previous || next.getType() == IToken.tEOC) { // infinite loop detected
           		break;
           	}
           	previous = next;
        }

        return pd;
    }

    @Override
	protected ASTVisitor createAmbiguityNodeVisitor() {
        return new CASTAmbiguityResolver();
    }

    @Override
	protected IASTAmbiguousStatement createAmbiguousStatement() {
        return new CASTAmbiguousStatement();
    }

    @Override
	protected IASTAmbiguousExpression createAmbiguousExpression() {
        return new CASTAmbiguousExpression();
    }
    
	@Override
	protected IASTAmbiguousExpression createAmbiguousBinaryVsCastExpression(IASTBinaryExpression binary, IASTCastExpression castExpr) {
		return new CASTAmbiguousBinaryVsCastExpression(binary, castExpr);
	}

	@Override
	protected IASTAmbiguousExpression createAmbiguousCastVsFunctionCallExpression(IASTCastExpression castExpr, IASTFunctionCallExpression funcCall) {
		return new CASTAmbiguousCastVsFunctionCallExpression(castExpr, funcCall);
	}

    protected IASTStatement parseIfStatement() throws EndOfFileException, BacktrackException {
        IASTIfStatement result = null;
        IASTIfStatement if_statement = null;
        int start = LA(1).getOffset();
        if_loop: while (true) {
            int so = consume(IToken.t_if).getOffset();
            consume(IToken.tLPAREN);
            // condition
            IASTExpression condition= condition(true);
            if (LT(1) == IToken.tEOC) {
            	// Completing in the condition
            	IASTIfStatement new_if = nodeFactory.newIfStatement(condition, null, null);

            	if (if_statement != null) {
            		if_statement.setElseClause(new_if);
            	}
            	return result != null ? result : new_if; 
            }
            consume(IToken.tRPAREN);
    
            IASTStatement thenClause = statement();
            IASTIfStatement new_if_statement = nodeFactory.newIfStatement(null, null, null);
            ((ASTNode) new_if_statement).setOffset(so);
            if( condition != null ) // shouldn't be possible but failure in condition() makes it so
            {
                new_if_statement.setConditionExpression(condition);
            }
            if (thenClause != null) {
                new_if_statement.setThenClause(thenClause);
                ((ASTNode) new_if_statement).setLength(calculateEndOffset(thenClause)
                                - ((ASTNode) new_if_statement).getOffset());
            }
            if (LT(1) == IToken.t_else) {
                consume();
                if (LT(1) == IToken.t_if) {
                    // an else if, don't recurse, just loop and do another if
    
                    if (if_statement != null) {
                        if_statement.setElseClause(new_if_statement);
                        ((ASTNode) if_statement).setLength(calculateEndOffset(new_if_statement)
                        		- ((ASTNode) if_statement).getOffset());
                    }
                    if (result == null && if_statement != null)
                        result = if_statement;
                    if (result == null)
                        result = new_if_statement;
    
                    if_statement = new_if_statement;
                    continue if_loop;
                }
                IASTStatement elseStatement = statement();
                new_if_statement.setElseClause(elseStatement);
                if (if_statement != null) {
                    if_statement.setElseClause(new_if_statement);
                    ((ASTNode) if_statement).setLength(calculateEndOffset(new_if_statement)
                                - ((ASTNode) if_statement).getOffset());
                } else {
                    if (result == null)
                        result = new_if_statement;
                    if_statement = new_if_statement;
                }
            } else {
            	if( thenClause != null )
                    ((ASTNode) new_if_statement).setLength(calculateEndOffset(thenClause) - start);
                if (if_statement != null) {
                    if_statement.setElseClause(new_if_statement);
                    ((ASTNode) new_if_statement).setLength(calculateEndOffset(new_if_statement) - start);
                }
                if (result == null && if_statement != null)
                    result = if_statement;
                if (result == null)
                    result = new_if_statement;
    
                if_statement = new_if_statement;
            }
            break if_loop;
        }
    
        reconcileLengths(result);
        return result;
    }


    protected IASTStatement parseSwitchStatement() throws EndOfFileException, BacktrackException {
        int startOffset;
        startOffset = consume().getOffset();
        consume(IToken.tLPAREN);
        IASTExpression switch_condition = condition(true);
        switch (LT(1)) {
        case IToken.tRPAREN:
            consume();
            break;
        case IToken.tEOC:
            break;
        default:
            throwBacktrack(LA(1));
        }

        IASTStatement switch_body = parseSwitchBody();
        IASTSwitchStatement switch_statement = nodeFactory.newSwitchStatement(switch_condition, switch_body);
        ((ASTNode) switch_statement).setOffsetAndLength(startOffset,
        		(switch_body != null ? calculateEndOffset(switch_body) : LA(1).getEndOffset()) - startOffset);
        return switch_statement;
    }

    protected IASTStatement parseForStatement() throws EndOfFileException, BacktrackException {
        int startOffset;
        startOffset = consume().getOffset();
        consume(IToken.tLPAREN);
        IASTStatement init = forInitStatement();
        IASTExpression for_condition = null;
        switch (LT(1)) {
        case IToken.tSEMI:
        case IToken.tEOC:
            break;
        default:
            for_condition = condition(false);
        }
        switch (LT(1)) {
        case IToken.tSEMI:
            consume();
            break;
        case IToken.tEOC:
            break;
        default:
            throw backtrack;
        }
        IASTExpression iterationExpression = null;
        switch (LT(1)) {
        case IToken.tRPAREN:
        case IToken.tEOC:
            break;
        default:
            iterationExpression = expression();
        }
        switch (LT(1)) {
        case IToken.tRPAREN:
            consume();
            break;
        case IToken.tEOC:
            break;
        default:
            throw backtrack;
        }
        
        IASTForStatement for_statement = nodeFactory.newForStatement(init, for_condition, iterationExpression, null);
        if (LT(1) != IToken.tEOC) {
        	IASTStatement for_body = statement();
            ((ASTNode) for_statement).setOffsetAndLength(startOffset, calculateEndOffset(for_body) - startOffset);
            for_statement.setBody(for_body);
        }
        return for_statement;
    }
}
