/*******************************************************************************
 * Copyright (c) 2005, 2015 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:
 *     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)
 *     Thomas Corbat (IFS)
 *     Anders Dahlberg (Ericsson) - bug 84144
 *     Alexander Nyßen (itemis AG) - bug 475908
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser;

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

import org.eclipse.cdt.core.dom.ast.ASTCompletionNode;
import org.eclipse.cdt.core.dom.ast.ASTGenericVisitor;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTAlignmentSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTAttribute;
import org.eclipse.cdt.core.dom.ast.IASTAttributeList;
import org.eclipse.cdt.core.dom.ast.IASTAttributeOwner;
import org.eclipse.cdt.core.dom.ast.IASTAttributeSpecifier;
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.IASTCompletionNode;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
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.IASTDeclarationListOwner;
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.IASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator;
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.IASTFileLocation;
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.IASTIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
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.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNullStatement;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTProblemDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTProblemExpression;
import org.eclipse.cdt.core.dom.ast.IASTProblemStatement;
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.IASTToken;
import org.eclipse.cdt.core.dom.ast.IASTTokenList;
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.IASTWhileStatement;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.gnu.IGNUASTCompoundStatementExpression;
import org.eclipse.cdt.core.dom.parser.IBuiltinBindingsProvider;
import org.eclipse.cdt.core.dom.parser.IExtensionToken;
import org.eclipse.cdt.core.dom.parser.ISourceCodeParser;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.AbstractParserLogService;
import org.eclipse.cdt.core.parser.EndOfFileException;
import org.eclipse.cdt.core.parser.IGCCToken;
import org.eclipse.cdt.core.parser.IInactiveCodeToken;
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.OffsetLimitReachedException;
import org.eclipse.cdt.core.parser.ParseError;
import org.eclipse.cdt.core.parser.ParserMode;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver;

/**
 * Base class for the c- and c++ parser.
 */
public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
	// See TemplateIdStrategy for an explanation of what this does.
	public interface ITemplateIdStrategy {
		boolean shallParseAsTemplateID(IASTName name);
	}

	protected static class FoundAggregateInitializer extends Exception {
		public final IASTDeclarator fDeclarator;
		public final IASTDeclSpecifier fDeclSpec;

		public FoundAggregateInitializer(IASTDeclSpecifier declSpec, IASTDeclarator d) {
			fDeclSpec = declSpec;
			fDeclarator = d;
		}
	}

	protected static class Decl {
		public Decl() {
		}

		public IASTDeclSpecifier fDeclSpec1;
		public IASTDeclSpecifier fDeclSpec2;

		public IASTDeclarator fDtor1;
		public IASTDeclarator fDtor2;
		public IToken fDtorToken1;

		public boolean isAtStartOfStructuredBinding = false;

		public Decl set(IASTDeclSpecifier declspec, IASTDeclarator dtor, IToken dtorToken) {
			fDeclSpec1 = declspec;
			fDtor1 = dtor;
			fDtorToken1 = dtorToken;
			fDeclSpec2 = null;
			fDtor2 = null;
			return this;
		}

		public Decl set(IASTDeclSpecifier declspec1, IASTDeclarator dtor1, IASTDeclSpecifier declspec2,
				IASTDeclarator dtor2) {
			fDeclSpec1 = declspec1;
			fDtor1 = dtor1;
			fDtorToken1 = null;
			fDeclSpec2 = declspec2;
			fDtor2 = dtor2;
			return this;
		}
	}

	private static final ASTVisitor MARK_INACTIVE = new ASTGenericVisitor(true) {
		{
			shouldVisitAmbiguousNodes = true;
		}

		@Override
		protected int genericVisit(IASTNode node) {
			((ASTNode) node).setInactive();
			return PROCESS_CONTINUE;
		}

		@Override
		public int visit(ASTAmbiguousNode node) {
			node.setInactive();
			IASTNode[] alternatives = node.getNodes();
			for (IASTNode alt : alternatives) {
				if (!alt.accept(this))
					return PROCESS_ABORT;
			}
			return PROCESS_CONTINUE;
		}
	};

	/**
	 * Information about the context in which a cast-expression is parsed:
	 * in a binary expression, in a binary expression in a template-id, or elsewhere.
	 */
	protected static enum CastExprCtx {
		eDirectlyInBExpr, eInBExpr, eNotInBExpr
	}

	protected static enum ExprKind {
		eExpression, eAssignment, eConstant
	}

	protected static final int DEFAULT_DESIGNATOR_LIST_SIZE = 4;

	protected final AbstractParserLogService log;
	protected final IScanner scanner;
	protected final ParserMode mode;

	protected final boolean supportStatementsInExpressions;
	protected final boolean supportTypeOfUnaries;
	protected final boolean supportAlignOfUnaries;
	protected final boolean supportKnRC;
	protected final boolean supportAttributeSpecifiers;
	protected final boolean supportDeclspecSpecifiers;
	protected boolean supportParameterInfoBlock;
	protected boolean supportFunctionStyleAsm;
	protected boolean supportExtendedSizeofOperator;
	protected final IBuiltinBindingsProvider builtinBindingsProvider;

	protected boolean functionCallCanBeLValue;
	protected int maximumTrivialExpressionsInAggregateInitializers = Integer.MAX_VALUE;

	/**
	 *  Marks the beginning of the current declaration. It is important to clear the mark whenever we
	 *  enter a nested declaration, in order to avoid holding on to all the tokens.
	 */
	protected IToken declarationMark;
	protected IToken nextToken;
	protected IToken lastToken;
	protected IToken lastTokenFromScanner;

	protected boolean isCancelled = false;
	protected boolean parsePassed = true;
	protected int backtrackCount = 0;
	protected BacktrackException backtrack = new BacktrackException();

	protected ASTCompletionNode completionNode;

	private final INodeFactory nodeFactory;
	private boolean fActiveCode = true;

	protected AbstractGNUSourceCodeParser(IScanner scanner, IParserLogService logService, ParserMode parserMode,
			INodeFactory nodeFactory, boolean supportStatementsInExpressions, boolean supportTypeOfUnaries,
			boolean supportAlignOfUnaries, boolean supportKnRC, boolean supportAttributeSpecifiers,
			boolean supportDeclspecSpecifiers, IBuiltinBindingsProvider builtinBindingsProvider) {
		this.scanner = scanner;
		this.log = wrapLogService(logService);
		this.mode = parserMode;
		this.supportStatementsInExpressions = supportStatementsInExpressions;
		this.supportTypeOfUnaries = supportTypeOfUnaries;
		this.supportAlignOfUnaries = supportAlignOfUnaries;
		this.supportKnRC = supportKnRC;
		this.supportAttributeSpecifiers = supportAttributeSpecifiers;
		this.supportDeclspecSpecifiers = supportDeclspecSpecifiers;
		this.builtinBindingsProvider = builtinBindingsProvider;
		this.nodeFactory = nodeFactory;
	}

	/**
	 * Instructs the parser not to create AST nodes for expressions within aggregate initializers
	 * when they do not contain names beyond the given limit.
	 */
	public void setMaximumTrivialExpressionsInAggregateInitializers(int limit) {
		maximumTrivialExpressionsInAggregateInitializers = limit;
	}

	private AbstractParserLogService wrapLogService(IParserLogService logService) {
		if (logService instanceof AbstractParserLogService) {
			return (AbstractParserLogService) logService;
		}
		return new ParserLogServiceWrapper(logService);
	}

	protected final void throwBacktrack(int offset, int length) throws BacktrackException {
		++backtrackCount;
		backtrack.initialize(offset, length < 0 ? 0 : length);
		throw backtrack;
	}

	protected INodeFactory getNodeFactory() {
		return nodeFactory;
	}

	@Override
	public IASTCompletionNode getCompletionNode() {
		return completionNode;
	}

	// Use to create the completion node
	protected ASTCompletionNode createCompletionNode(IToken token) {
		// the preprocessor may deliver tokens for literals or header-names.
		if (completionNode == null && token != null && token.getType() == IToken.tCOMPLETION) {
			completionNode = new ASTCompletionNode(token, getTranslationUnit());
		}
		return completionNode;
	}

	/**
	 * Fetches the next token from the scanner.
	 */
	private final IToken fetchToken(boolean skipInactive) throws EndOfFileException {
		try {
			IToken t = scanner.nextToken();
			if (skipInactive) {
				while (t.getType() == IToken.tINACTIVE_CODE_START) {
					scanner.skipInactiveCode();
					t = scanner.nextToken();
				}
			}
			if (lastTokenFromScanner != null)
				lastTokenFromScanner.setNext(t);
			lastTokenFromScanner = t;
			return t;
		} catch (OffsetLimitReachedException olre) {
			if (mode != ParserMode.COMPLETION_PARSE)
				throw new EndOfFileException(olre.getEndOffset());
			IToken completionToken = olre.getFinalToken();
			createCompletionNode(completionToken);
			if (olre.getOriginator() == OffsetLimitReachedException.ORIGIN_INACTIVE_CODE) {
				// If completion is invoked inside inactive code, there is no AST from which
				// to give the completion node a completion name, so we invent a name.
				// The invented name is not hooked up to the AST, but does have an offset
				// and length, so it can provide an accurate point of reference in
				// declaredBefore().
				IASTName completionName = nodeFactory.newInactiveCompletionName(completionToken.getCharImage(),
						getTranslationUnit());
				((ASTNode) completionName).setOffsetAndLength(completionToken.getOffset(), completionToken.getLength());
				completionNode.addName(completionName);

				// Consume the completion token so we don't try to parse an AST fragment from it.
				consume();
			}
			throw olre;
		}
	}

	private final IToken nextToken(boolean skipInactive) throws EndOfFileException {
		final IToken t = nextToken;
		if (t != null)
			return t;

		final IToken tn = fetchToken(skipInactive);
		nextToken = tn;
		return tn;
	}

	private final IToken lookaheadToken(int i, boolean skipInactive) throws EndOfFileException {
		assert i >= 0;
		if (isCancelled) {
			throw new ParseError(ParseError.ParseErrorKind.TIMEOUT_OR_CANCELLED);
		}
		IToken t = nextToken(skipInactive);
		for (; i > 1; --i) {
			t = t.getNext();
			if (t == null)
				t = fetchToken(skipInactive);
		}
		return t;
	}

	/**
	 * Returns the next token without advancing. Same as {@code LA(1)}.
	 */
	protected final IToken LA() throws EndOfFileException {
		IToken t = nextToken(true);
		checkForEOI(t);
		return t;
	}

	/**
	 * Returns one of the next tokens. With {@code i == 1}, the next token is returned.
	 * @param i number of tokens to look ahead, must be greater than 0.
	 */
	protected final IToken LA(int i) throws EndOfFileException {
		IToken t = lookaheadToken(i, true);
		checkForEOI(t);
		return t;
	}

	/**
	 * Consumes and returns the next token available.
	 */
	protected final IToken consume() throws EndOfFileException {
		IToken t = nextToken(true);
		checkForEOI(t);

		nextToken = t.getNext();
		return t;
	}

	/**
	 * Tests whether we are looking at a change from active to inactive code at this point. If so, the change
	 * is accepted.
	 *
	 * @param nesting
	 *            the nesting level of the code branch we have to stay within
	 * @return <code>false</code> if an inactive code branch was rejected because of its nesting level,
	 *         <code>true</code>, otherwise.
	 */
	protected final boolean acceptInactiveCodeBoundary(int nesting) {
		try {
			while (true) {
				IToken t = nextToken(false);
				switch (t.getType()) {
				case IToken.tINACTIVE_CODE_START:
				case IToken.tINACTIVE_CODE_SEPARATOR:
					IInactiveCodeToken it = (IInactiveCodeToken) t;
					if (it.getNewNesting() < nesting
							|| (it.getNewNesting() == nesting && it.getOldNesting() == nesting)) {
						return false;
					}
					fActiveCode = false;
					nextToken = t.getNext(); // consume the token
					continue;
				case IToken.tINACTIVE_CODE_END:
					it = (IInactiveCodeToken) t;
					if (it.getNewNesting() < nesting
							|| (it.getNewNesting() == nesting && it.getOldNesting() == nesting)) {
						return false;
					}
					fActiveCode = true;
					nextToken = t.getNext(); // consume the token
					continue;
				default:
					return true;
				}
			}
		} catch (EndOfFileException e) {
		}
		return true;
	}

	protected final void skipInactiveCode() throws OffsetLimitReachedException {
		IToken t = nextToken;
		if (fActiveCode && (t == null || t.getType() != IToken.tINACTIVE_CODE_START))
			return;
		try {
			fActiveCode = true;
			while (t != null && t.getType() != IToken.tINACTIVE_CODE_END) {
				t = t.getNext();
			}

			if (t != null) {
				nextToken = t.getNext();
			} else {
				nextToken = null;
				scanner.skipInactiveCode();
			}
		} catch (OffsetLimitReachedException olre) {
			if (mode == ParserMode.COMPLETION_PARSE) {
				createCompletionNode(olre.getFinalToken());
				throw olre;
			}
		}
	}

	protected final boolean isActiveCode() {
		return fActiveCode;
	}

	protected final int getCodeBranchNesting() {
		return scanner.getCodeBranchNesting();
	}

	/**
	 * Returns the next token, which can be used to reset the input back to
	 * this point in the stream.
	 */
	protected final IToken mark() throws EndOfFileException {
		return LA();
	}

	/**
	 * Roll back to a previous point, reseting the queue of tokens.
	 * @param mark a token previously obtained via {@link #mark()}.
	 */
	protected final void backup(IToken mark) {
		nextToken = mark;
	}

	private final void checkForEOI(IToken t) throws EndOfFileException {
		final int lt = t.getType();
		if (lt == IToken.tINACTIVE_CODE_SEPARATOR || lt == IToken.tINACTIVE_CODE_END)
			throw new EndOfFileException(t.getOffset(), true);
	}

	/**
	 * Same as {@link #LA(int)}, but returns <code>null</code> when eof is reached.
	 */
	protected final IToken LAcatchEOF(int i) {
		try {
			return LA(i);
		} catch (EndOfFileException e) {
			return null;
		}
	}

	/**
	 * Look ahead in the token list and return the token type.
	 * @param i number of tokens to look ahead, must be greater or equal to 0.
	 * @return The type of that token
	 */
	protected final int LT(int i) throws EndOfFileException {
		return LA(i).getType();
	}

	/**
	 * Same as {@link #LT(int)}, but returns <code>0</code> when eof is reached.
	 */
	protected final int LTcatchEOF(int i) {
		try {
			return LT(i);
		} catch (EndOfFileException e) {
			return 0;
		}
	}

	/**
	 * The next token is consumed. Afterwards its type is checked and a {@link BacktrackException}
	 * is thrown if the type does not match the expected one.
	 * @param type the expected type of the next token.
	 */
	protected final IToken consume(int type) throws EndOfFileException, BacktrackException {
		final IToken result = consume();
		if (result.getType() != type)
			throwBacktrack(result);
		return result;
	}

	/**
	 * The next token is consumed. Afterwards its type is checked and a {@link BacktrackException}
	 * is thrown if the type neither matches <code>type1</code> nor <code>type2</code>.
	 */
	protected final IToken consume(int type1, int type2) throws EndOfFileException, BacktrackException {
		final IToken result = consume();
		final int lt1 = result.getType();
		if (lt1 != type1 && lt1 != type2)
			throwBacktrack(result);
		return result;
	}

	/**
	 * Consume the next token available only if the type is as specified. In case we reached
	 * the end of completion, no token is consumed and the eoc-token returned.
	 *
	 * @param type
	 *            The type of token that you are expecting.
	 * @return the token that was consumed and removed from our buffer.
	 * @throws BacktrackException
	 *             if LT(1) != type
	 */
	protected IToken consumeOrEOC(int type) throws EndOfFileException, BacktrackException {
		final IToken la1 = LA(1);
		final int lt1 = la1.getType();
		if (lt1 != type) {
			if (lt1 == IToken.tEOC)
				return la1;
			throwBacktrack(la1);
		}
		return consume();
	}

	protected final boolean isOnSameLine(int offset1, int offset2) {
		ILocationResolver lr = getTranslationUnit().getAdapter(ILocationResolver.class);
		IASTFileLocation floc = lr.getMappedFileLocation(offset1, offset2 - offset1 + 1);
		return floc.getFileName().equals(lr.getContainingFilePath(offset1))
				&& floc.getStartingLineNumber() == floc.getEndingLineNumber();
	}

	protected final int calculateEndOffset(IASTNode n) {
		ASTNode node = (ASTNode) n;
		return node.getOffset() + node.getLength();
	}

	protected final <T extends IASTNode> T setRange(T n, IASTNode from) {
		((ASTNode) n).setOffsetAndLength((ASTNode) from);
		return n;
	}

	protected final <T extends IASTNode> T setRange(T n, IASTNode from, int endOffset) {
		final int offset = ((ASTNode) from).getOffset();
		((ASTNode) n).setOffsetAndLength(offset, endOffset - offset);
		return n;
	}

	protected final <T extends IASTNode> T setRange(T n, int offset, int endOffset) {
		((ASTNode) n).setOffsetAndLength(offset, endOffset - offset);
		return n;
	}

	protected final void adjustLength(IASTNode n, IASTNode endNode) {
		final int endOffset = calculateEndOffset(endNode);
		adjustEndOffset(n, endOffset);
	}

	protected final <T extends IASTNode> T adjustEndOffset(T n, final int endOffset) {
		final ASTNode node = (ASTNode) n;
		node.setLength(endOffset - node.getOffset());
		return n;
	}

	protected final int getEndOffset() {
		if (lastTokenFromScanner == null)
			return 0;
		return lastTokenFromScanner.getEndOffset();
	}

	/**
	 * This is the single entry point for setting parsePassed to false
	 */
	protected void failParse() {
		parsePassed = false;
	}

	@Override
	public synchronized void cancel() {
		isCancelled = true;
	}

	/**
	 * Parses an identifier.
	 *
	 * @throws BacktrackException request a backtrack
	 */
	protected abstract IASTName identifier() throws EndOfFileException, BacktrackException;

	/**
	 * @return Returns the backtrackCount.
	 */
	public final int getBacktrackCount() {
		return backtrackCount;
	}

	protected IASTProblem createProblem(BacktrackException bt) {
		IASTProblem result = bt.getProblem();
		if (result == null) {
			result = createProblem(IProblem.SYNTAX_ERROR, bt.getOffset(), bt.getLength());
		}
		return result;
	}

	protected final IASTProblem createProblem(int signal, int offset, int length) {
		IASTProblem result = nodeFactory.newProblem(signal, CharArrayUtils.EMPTY, true);
		((ASTNode) result).setOffsetAndLength(offset, length);
		return result;
	}

	protected void logThrowable(String methodName, Throwable e) {
		if (e != null) {
			if (log.isTracing()) {
				String message = String.format("Parser: Unexpected throwable in %s:%s::%s. w/%s", //$NON-NLS-1$
						methodName, e.getClass().getName(), e.getMessage(), scanner);
				log.traceLog(message);
			}
			log.traceException(e);
		}
	}

	@Override
	public String toString() {
		return scanner.toString();
	}

	/**
	 * @param methodName
	 * @param e
	 */
	protected void logException(String methodName, Exception e) {
		if (!(e instanceof EndOfFileException) && e != null) {
			if (log.isTracing()) {
				String message = String.format("Parser: Unexpected exception in %s:%s::%s. w/%s", //$NON-NLS-1$
						methodName, e.getClass().getName(), e.getMessage(), scanner);
				log.traceLog(message);
			}
			log.traceException(e);
		}
	}

	protected final void throwBacktrack(IASTProblem problem, IASTNode node) throws BacktrackException {
		++backtrackCount;
		backtrack.initialize(problem, node);
		throw backtrack;
	}

	protected final void throwBacktrack(IASTProblem problem) throws BacktrackException {
		++backtrackCount;
		backtrack.initialize(problem);
		throw backtrack;
	}

	protected final void throwBacktrack(IASTNode node) throws BacktrackException {
		final ASTNode n = (ASTNode) node;
		throwBacktrack(n.getOffset(), n.getLength());
	}

	@Override
	public IASTTranslationUnit parse() {
		long t0 = log.isTracing() ? System.currentTimeMillis() : 0;
		translationUnit();
		long t1 = log.isTracing() ? System.currentTimeMillis() : 0;
		resolveAmbiguities();
		IASTTranslationUnit ast = getTranslationUnit();
		if (log.isTracing()) {
			ITranslationUnit tu = ast.getOriginatingTranslationUnit();
			String name = tu == null ? "<unknown>" : tu.getElementName(); //$NON-NLS-1$
			String message = String.format("Parsed %s: %d ms%s. Ambiguity resolution: %d ms", //$NON-NLS-1$
					name, t1 - t0, parsePassed ? "" : " - parse failure", System.currentTimeMillis() - t1); //$NON-NLS-1$//$NON-NLS-2$
			log.traceLog(message);
		}
		nullifyTranslationUnit();
		ast.freeze(); // Make the AST immutable.
		return ast;
	}

	protected void resolveAmbiguities() {
		final IASTTranslationUnit translationUnit = getTranslationUnit();
		if (translationUnit instanceof ASTTranslationUnit) {
			((ASTTranslationUnit) translationUnit).resolveAmbiguities();
		}
	}

	protected abstract ASTVisitor createAmbiguityNodeVisitor();

	protected abstract void nullifyTranslationUnit();

	protected IToken skipOverCompoundStatement(boolean hasSkippedNodes) throws BacktrackException, EndOfFileException {
		// speed up the parser by skipping the body, simply look for matching brace and return
		if (hasSkippedNodes)
			getTranslationUnit().setHasNodesOmitted(true);
		final boolean isActive = isActiveCode();
		final int codeBranchNesting = getCodeBranchNesting();

		consume(IToken.tLBRACE);
		IToken result = null;
		int depth = 1;
		while (depth > 0) {
			if (!isActive) {
				IToken t = lookaheadToken(1, false);
				final int lt = t.getType();
				if (lt == IToken.tINACTIVE_CODE_SEPARATOR || lt == IToken.tINACTIVE_CODE_END
						|| lt == IToken.tINACTIVE_CODE_START) {
					if (!acceptInactiveCodeBoundary(codeBranchNesting))
						throw new EndOfFileException(t.getOffset(), true);
				}
			}
			result = consume();
			switch (result.getType()) {
			case IToken.tRBRACE:
				--depth;
				break;
			case IToken.tLBRACE:
				++depth;
				break;
			case IToken.tEOC:
				throw new EndOfFileException(result.getOffset());
			}
		}
		return result;
	}

	protected IASTProblemDeclaration skipProblemDeclaration(int offset) {
		return skipProblemDeclaration(offset, null);
	}

	protected IASTProblemDeclaration skipProblemDeclaration(int offset, IASTProblem origProblem) {
		failParse();
		declarationMark = null;
		int endOffset = skipToSemiOrClosingBrace(offset, false);
		IASTProblem problem = createProblem(IProblem.SYNTAX_ERROR, offset, endOffset - offset);
		if (origProblem != null && origProblem.getID() != IProblem.SYNTAX_ERROR) {
			problem.setOriginalProblem(origProblem);
		}
		return buildProblemDeclaration(problem);
	}

	protected IASTProblemStatement skipProblemStatement(int offset) {
		failParse();
		declarationMark = null;
		int endOffset = skipToSemiOrClosingBrace(offset, false);
		IASTProblem problem = createProblem(IProblem.SYNTAX_ERROR, offset, endOffset - offset);
		return buildProblemStatement(problem);
	}

	private IASTProblem skipProblemEnumerator(int offset) {
		failParse();
		final int endOffset = skipToSemiOrClosingBrace(offset, true);
		return createProblem(IProblem.SYNTAX_ERROR, offset, endOffset - offset);
	}

	private int skipToSemiOrClosingBrace(int offset, boolean eatBrace) {
		failParse();
		declarationMark = null;
		int depth = 0;
		int endOffset;
		loop: try {
			endOffset = LA(1).getOffset();
			while (true) {
				switch (LT(1)) {
				case IToken.tEOC:
					endOffset = getEndOffset();
					break loop;
				case IToken.tSEMI:
					if (depth == 0) {
						endOffset = consume().getEndOffset();
						break loop;
					}
					break;
				case IToken.tLBRACE:
					++depth;
					break;
				case IToken.tRBRACE:
					if (--depth <= 0) {
						if (depth == 0 || offset == endOffset || eatBrace) {
							endOffset = consume().getEndOffset(); // consume closing brace
						}
						if (LTcatchEOF(1) == IToken.tSEMI) {
							endOffset = consume().getEndOffset();
						}
						break loop;
					}
					break;
				}
				endOffset = consume().getEndOffset();
			}
		} catch (EndOfFileException e) {
			endOffset = getEndOffset();
		}
		return endOffset;
	}

	protected IASTProblemExpression skipProblemConditionInParenthesis(int offset) {
		failParse();
		int compExpr = 0;
		int depth = 0;
		int endOffset = offset;
		loop: try {
			while (true) {
				switch (LT(1)) {
				case IToken.tEOC:
					endOffset = getEndOffset();
					break loop;
				case IToken.tSEMI:
				case IToken.tLBRACE:
					if (compExpr == 0) {
						break loop;
					}
					break;
				case IToken.tLPAREN:
					depth++;
					if (LTcatchEOF(2) == IToken.tLBRACE) {
						if (compExpr == 0) {
							compExpr = depth;
						}
						consume();
					}
					break;
				case IToken.tRPAREN:
					if (--depth < 0) {
						break loop;
					}
					if (depth < compExpr) {
						compExpr = 0;
					}
					break;
				}
				endOffset = consume().getEndOffset();
			}
		} catch (EndOfFileException e) {
			endOffset = getEndOffset();
		}
		IASTProblem problem = createProblem(IProblem.SYNTAX_ERROR, offset, endOffset - offset);
		return buildProblemExpression(problem);
	}

	protected IASTCompoundStatement compoundStatement() throws EndOfFileException, BacktrackException {
		IASTCompoundStatement result = nodeFactory.newCompoundStatement();
		if (LT(1) == IToken.tEOC)
			return result;

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

		int stmtOffset = -1;
		while (true) {
			IToken next = LAcatchEOF(1);
			if (next == null) {
				((ASTNode) result).setOffsetAndLength(offset, endOffset - offset);
				throwBacktrack(createProblem(IProblem.SYNTAX_ERROR, endOffset, 0), result);
				return null; // To make Java compiler happy.
			}
			try {
				if (next.getType() == IToken.tEOC)
					break;

				if (next.getType() == IToken.tRBRACE) {
					endOffset = consume().getEndOffset();
					break;
				}

				final int nextOffset = next.getOffset();
				declarationMark = next;
				next = null; // Don't hold on to the token while parsing namespaces, class bodies, etc.

				IASTStatement stmt;
				if (stmtOffset == nextOffset) {
					// no progress
					stmt = skipProblemStatement(stmtOffset);
				} else {
					stmtOffset = nextOffset;
					stmt = statement();
				}
				result.addStatement(stmt);
				endOffset = calculateEndOffset(stmt);
			} catch (BacktrackException bt) {
				final IASTNode beforeProblem = bt.getNodeBeforeProblem();
				final IASTProblem problem = bt.getProblem();
				if (problem != null && beforeProblem instanceof IASTStatement) {
					result.addStatement((IASTStatement) beforeProblem);
					result.addStatement(buildProblemStatement(problem));
					endOffset = calculateEndOffset(beforeProblem);
				} else {
					IASTStatement stmt = skipProblemStatement(stmtOffset);
					result.addStatement(stmt);
					endOffset = calculateEndOffset(stmt);
				}
			} catch (EndOfFileException e) {
				IASTStatement stmt = skipProblemStatement(stmtOffset);
				result.addStatement(stmt);
				endOffset = calculateEndOffset(stmt);
				break;
			} finally {
				declarationMark = null;
			}
		}
		((ASTNode) result).setOffsetAndLength(offset, endOffset - offset);
		return result;
	}

	private IASTProblemDeclaration buildProblemDeclaration(IASTProblem problem) {
		IASTProblemDeclaration pd = nodeFactory.newProblemDeclaration(problem);
		((ASTNode) pd).setOffsetAndLength(((ASTNode) problem));
		return pd;
	}

	private IASTProblemStatement buildProblemStatement(IASTProblem problem) {
		IASTProblemStatement pstmt = nodeFactory.newProblemStatement(problem);
		((ASTNode) pstmt).setOffsetAndLength(((ASTNode) problem));
		return pstmt;
	}

	private IASTProblemExpression buildProblemExpression(IASTProblem problem) {
		IASTProblemExpression pexpr = nodeFactory.newProblemExpression(problem);
		((ASTNode) pexpr).setOffsetAndLength(((ASTNode) problem));
		return pexpr;
	}

	protected IASTExpression compoundStatementExpression() throws EndOfFileException, BacktrackException {
		int startingOffset = consume().getOffset(); // tLPAREN always
		IASTCompoundStatement compoundStatement = null;
		if (mode == ParserMode.QUICK_PARSE || mode == ParserMode.STRUCTURAL_PARSE || !isActiveCode()) {
			skipOverCompoundStatement(true);
		} else if (mode == ParserMode.COMPLETION_PARSE || mode == ParserMode.SELECTION_PARSE) {
			if (scanner.isOnTopContext()) {
				compoundStatement();
			} else {
				skipOverCompoundStatement(true);
			}
		} else if (mode == ParserMode.COMPLETE_PARSE) {
			compoundStatement = compoundStatement();
		}

		int lastOffset = consume(IToken.tRPAREN).getEndOffset();
		IGNUASTCompoundStatementExpression resultExpression = nodeFactory
				.newGNUCompoundStatementExpression(compoundStatement);
		((ASTNode) resultExpression).setOffsetAndLength(startingOffset, lastOffset - startingOffset);
		return resultExpression;
	}

	/**
	 * Models a cast expression followed by an operator. Can be linked into a chain.
	 * This is done right to left, such that a tree of variants can be built.
	 */
	public static class BinaryOperator {
		final int fOperatorToken;
		final int fLeftPrecedence;
		final int fRightPrecedence;
		BinaryOperator fNext;
		IASTInitializerClause fExpression;
		final CastAmbiguityMarker fAmbiguityMarker;

		public BinaryOperator(BinaryOperator nextOp, IASTInitializerClause expression, int operatorToken,
				int leftPrecedence, int rightPrecedence) {
			fNext = nextOp;
			fOperatorToken = operatorToken;
			fLeftPrecedence = leftPrecedence;
			fRightPrecedence = rightPrecedence;
			if (expression instanceof CastAmbiguityMarker) {
				fAmbiguityMarker = (CastAmbiguityMarker) expression;
				fExpression = fAmbiguityMarker.fExpression;
				fAmbiguityMarker.fExpression = null;
			} else {
				fExpression = expression;
				fAmbiguityMarker = null;
			}
		}

		public IASTInitializerClause exchange(IASTInitializerClause expr) {
			IASTInitializerClause e = fExpression;
			fExpression = expr;
			return e;
		}

		public IASTInitializerClause getExpression() {
			return fExpression;
		}

		public BinaryOperator getNext() {
			return fNext;
		}

		public void setNext(BinaryOperator next) {
			fNext = next;
		}
	}

	public final IASTExpression buildExpression(BinaryOperator leftChain, IASTInitializerClause expr) {
		BinaryOperator rightChain = null;
		for (;;) {
			if (leftChain == null) {
				if (rightChain == null)
					return (IASTExpression) expr;

				expr = buildExpression((IASTExpression) expr, rightChain);
				rightChain = rightChain.fNext;
			} else if (rightChain != null && leftChain.fRightPrecedence < rightChain.fLeftPrecedence) {
				expr = buildExpression((IASTExpression) expr, rightChain);
				rightChain = rightChain.fNext;
			} else {
				BinaryOperator op = leftChain;
				leftChain = leftChain.fNext;
				expr = op.exchange(expr);
				op.fNext = rightChain;
				rightChain = op;
			}
		}
	}

	private IASTExpression buildExpression(IASTExpression left, BinaryOperator operator) {
		int op, unaryOp = 0;
		final IASTInitializerClause right = operator.fExpression;
		switch (operator.fOperatorToken) {
		case IToken.tQUESTION:
			final IASTInitializerClause negative;
			if (operator.fNext == null || operator.fNext.fOperatorToken != IToken.tCOLON) {
				negative = null;
			} else {
				negative = operator.fNext.fExpression;
				operator.fNext = operator.fNext.fNext;
			}
			IASTConditionalExpression conditionalEx = nodeFactory.newConditionalExpession(left, (IASTExpression) right,
					(IASTExpression) negative);
			setRange(conditionalEx, left);
			if (negative != null) {
				adjustLength(conditionalEx, negative);
			}
			return conditionalEx;

		case IToken.tCOMMA:
			IASTExpressionList list;
			if (left instanceof IASTExpressionList) {
				list = (IASTExpressionList) left;
			} else {
				list = nodeFactory.newExpressionList();
				list.addExpression(left);
				setRange(list, left);
			}
			list.addExpression((IASTExpression) right);
			adjustLength(list, right);
			return list;

		case IToken.tASSIGN:
			op = IASTBinaryExpression.op_assign;
			break;
		case IToken.tSTARASSIGN:
			op = IASTBinaryExpression.op_multiplyAssign;
			break;
		case IToken.tDIVASSIGN:
			op = IASTBinaryExpression.op_divideAssign;
			break;
		case IToken.tMODASSIGN:
			op = IASTBinaryExpression.op_moduloAssign;
			break;
		case IToken.tPLUSASSIGN:
			op = IASTBinaryExpression.op_plusAssign;
			break;
		case IToken.tMINUSASSIGN:
			op = IASTBinaryExpression.op_minusAssign;
			break;
		case IToken.tSHIFTRASSIGN:
			op = IASTBinaryExpression.op_shiftRightAssign;
			break;
		case IToken.tSHIFTLASSIGN:
			op = IASTBinaryExpression.op_shiftLeftAssign;
			break;
		case IToken.tAMPERASSIGN:
			op = IASTBinaryExpression.op_binaryAndAssign;
			break;
		case IToken.tXORASSIGN:
			op = IASTBinaryExpression.op_binaryXorAssign;
			break;
		case IToken.tBITORASSIGN:
			op = IASTBinaryExpression.op_binaryOrAssign;
			break;
		case IToken.tOR:
			op = IASTBinaryExpression.op_logicalOr;
			break;
		case IToken.tAND:
			op = IASTBinaryExpression.op_logicalAnd;
			unaryOp = IASTUnaryExpression.op_labelReference;
			break;
		case IToken.tBITOR:
			op = IASTBinaryExpression.op_binaryOr;
			break;
		case IToken.tXOR:
			op = IASTBinaryExpression.op_binaryXor;
			break;
		case IToken.tAMPER:
			op = IASTBinaryExpression.op_binaryAnd;
			unaryOp = IASTUnaryExpression.op_amper;
			break;
		case IToken.tEQUAL:
			op = IASTBinaryExpression.op_equals;
			break;
		case IToken.tNOTEQUAL:
			op = IASTBinaryExpression.op_notequals;
			break;
		case IToken.tGT:
			op = IASTBinaryExpression.op_greaterThan;
			break;
		case IToken.tLT:
			op = IASTBinaryExpression.op_lessThan;
			break;
		case IToken.tLTEQUAL:
			op = IASTBinaryExpression.op_lessEqual;
			break;
		case IToken.tGTEQUAL:
			op = IASTBinaryExpression.op_greaterEqual;
			break;
		case IGCCToken.tMAX:
			op = IASTBinaryExpression.op_max;
			break;
		case IGCCToken.tMIN:
			op = IASTBinaryExpression.op_min;
			break;
		case IToken.tSHIFTL:
			op = IASTBinaryExpression.op_shiftLeft;
			break;
		case IToken.tSHIFTR:
			op = IASTBinaryExpression.op_shiftRight;
			break;
		case IToken.tPLUS:
			op = IASTBinaryExpression.op_plus;
			unaryOp = IASTUnaryExpression.op_plus;
			break;
		case IToken.tMINUS:
			op = IASTBinaryExpression.op_minus;
			unaryOp = IASTUnaryExpression.op_minus;
			break;
		case IToken.tSTAR:
			op = IASTBinaryExpression.op_multiply;
			unaryOp = IASTUnaryExpression.op_star;
			break;
		case IToken.tDIV:
			op = IASTBinaryExpression.op_divide;
			break;
		case IToken.tMOD:
			op = IASTBinaryExpression.op_modulo;
			break;
		case IToken.tDOTSTAR:
			op = IASTBinaryExpression.op_pmdot;
			break;
		case IToken.tARROWSTAR:
			op = IASTBinaryExpression.op_pmarrow;
			break;

		default:
			assert false;
			return null;
		}

		IASTExpression result = buildBinaryExpression(op, left, right, calculateEndOffset(right));
		final CastAmbiguityMarker am = operator.fAmbiguityMarker;
		if (am != null) {
			if (unaryOp != 0) {
				result = createCastVsBinaryExpressionAmbiguity((IASTBinaryExpression) result, am.getTypeIdForCast(),
						unaryOp, am.getUnaryOperatorOffset());
			} else {
				assert false;
			}
		}
		return result;
	}

	protected abstract IASTExpression expression() throws BacktrackException, EndOfFileException;

	protected abstract IASTExpression constantExpression() throws BacktrackException, EndOfFileException;

	protected abstract IASTExpression unaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat)
			throws BacktrackException, EndOfFileException;

	protected abstract IASTExpression primaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat)
			throws BacktrackException, EndOfFileException;

	protected abstract IASTTypeId typeId(DeclarationOptions option) throws EndOfFileException, BacktrackException;

	// Methods for parsing a type-id and an expression with an optional trailing ellipsis.
	// The optional trailing ellipsis can only appear in C++ code, and only the C++ parser
	// allows it, but being able to invoke this from here allows reusing more productions
	// between C and C++, such as alignmentSpecifier().
	protected abstract IASTExpression expressionWithOptionalTrailingEllipsis()
			throws BacktrackException, EndOfFileException;

	protected abstract IASTTypeId typeIdWithOptionalTrailingEllipsis(DeclarationOptions option)
			throws EndOfFileException, BacktrackException;

	private final static class CastAmbiguityMarker extends ASTNode implements IASTExpression {
		private IASTExpression fExpression;
		private final IASTTypeId fTypeIdForCast;
		private final int fUnaryOperatorOffset;

		CastAmbiguityMarker(IASTExpression unary, IASTTypeId typeIdForCast, int unaryOperatorOffset) {
			fExpression = unary;
			fTypeIdForCast = typeIdForCast;
			fUnaryOperatorOffset = unaryOperatorOffset;
		}

		public CastAmbiguityMarker updateExpression(IASTExpression expression) {
			fExpression = expression;
			return this;
		}

		public IASTExpression getExpression() {
			return fExpression;
		}

		public IASTTypeId getTypeIdForCast() {
			return fTypeIdForCast;
		}

		public int getUnaryOperatorOffset() {
			return fUnaryOperatorOffset;
		}

		@Override
		public IASTExpression copy() {
			throw new UnsupportedOperationException();
		}

		@Override
		public IASTExpression copy(CopyStyle style) {
			throw new UnsupportedOperationException();
		}

		@Override
		public IType getExpressionType() {
			throw new UnsupportedOperationException();
		}

		@Override
		public boolean isLValue() {
			throw new UnsupportedOperationException();
		}

		@Override
		public ValueCategory getValueCategory() {
			throw new UnsupportedOperationException();
		}
	}

	protected final IASTExpression castExpression(CastExprCtx ctx, ITemplateIdStrategy strat)
			throws EndOfFileException, BacktrackException {
		if (LT(1) == IToken.tLPAREN) {
			final IToken mark = mark();
			final int startingOffset = mark.getOffset();
			final boolean canBeCast = canBeCastExpression();
			consume();
			IASTTypeId typeId = null;
			if (canBeCast) {
				try {
					typeId = typeId(DeclarationOptions.TYPEID);
				} catch (BacktrackException e) {
				}
			}
			if (typeId != null && LT(1) == IToken.tRPAREN) {
				consume();
				boolean unaryFailed = false;
				if (ctx == CastExprCtx.eDirectlyInBExpr) {
					switch (LT(1)) {
					// ambiguity with unary operator
					case IToken.tPLUS:
					case IToken.tMINUS:
					case IToken.tSTAR:
					case IToken.tAMPER:
					case IToken.tAND:
						final int operatorOffset = LA(1).getOffset();
						IToken markEnd = mark();
						backup(mark);
						try {
							IASTExpression unary = unaryExpression(CastExprCtx.eInBExpr, strat);
							return new CastAmbiguityMarker(unary, typeId, operatorOffset);
						} catch (BacktrackException bt) {
							backup(markEnd);
							unaryFailed = true;
						}
					}
				}
				try {
					boolean couldBeFunctionCall = LT(1) == IToken.tLPAREN;
					IASTExpression rhs = castExpression(ctx, strat);

					CastAmbiguityMarker ca = null;
					if (rhs instanceof CastAmbiguityMarker) {
						ca = (CastAmbiguityMarker) rhs;
						rhs = ca.getExpression();
						assert !(rhs instanceof CastAmbiguityMarker);
					}
					IASTCastExpression result = buildCastExpression(IASTCastExpression.op_cast, typeId, rhs,
							startingOffset, calculateEndOffset(rhs));
					if (!unaryFailed && couldBeFunctionCall && !(rhs instanceof IASTCastExpression)) {
						IToken markEnd = mark();
						backup(mark);
						try {
							IASTExpression expr = primaryExpression(ctx, strat);
							IASTFunctionCallExpression fcall = nodeFactory.newFunctionCallExpression(expr,
									(IASTExpression[]) null);
							IASTAmbiguousExpression ambiguity = createAmbiguousCastVsFunctionCallExpression(result,
									fcall);
							((ASTNode) ambiguity).setOffsetAndLength((ASTNode) result);
							return ca == null ? ambiguity : ca.updateExpression(ambiguity);
						} catch (BacktrackException bt) {
						} finally {
							backup(markEnd);
						}
					}
					return ca == null ? result : ca.updateExpression(result);
				} catch (BacktrackException b) {
					if (unaryFailed)
						throw b;
				}
			}
			backup(mark);
		}
		return unaryExpression(ctx, strat);
	}

	protected abstract IASTTranslationUnit getTranslationUnit();

	protected abstract void setupTranslationUnit() throws Exception;

	protected void translationUnit() {
		try {
			setupTranslationUnit();
		} catch (Exception e) {
			logException("translationUnit::createCompilationUnit()", e); //$NON-NLS-1$
			return;
		}
		parseTranslationUnit();
	}

	protected void parseTranslationUnit() {
		final IASTTranslationUnit tu = getTranslationUnit();
		declarationList(tu, DeclarationOptions.GLOBAL, false, 0);
	}

	protected final void declarationListInBraces(final IASTDeclarationListOwner tu, int offset,
			DeclarationOptions options) throws EndOfFileException, BacktrackException {
		// consume brace, if requested
		int codeBranchNesting = getCodeBranchNesting();
		consume(IToken.tLBRACE);
		declarationList(tu, options, true, codeBranchNesting);

		final int lt1 = LTcatchEOF(1);
		if (lt1 == IToken.tRBRACE) {
			int endOffset = consume().getEndOffset();
			setRange(tu, offset, endOffset);
			return;
		}

		final int endOffset = getEndOffset();
		setRange(tu, offset, endOffset);
		if (lt1 == IToken.tEOC || (lt1 == 0 && tu instanceof IASTCompositeTypeSpecifier)) {
			return;
		}
		throwBacktrack(createProblem(IProblem.SYNTAX_ERROR, endOffset, 0), tu);
	}

	private final void declarationList(final IASTDeclarationListOwner tu, DeclarationOptions options, boolean upToBrace,
			int codeBranchNesting) {
		final boolean wasActive = isActiveCode();
		while (true) {
			final boolean ok = acceptInactiveCodeBoundary(codeBranchNesting);
			if (!ok) {
				// We left to an enclosing code branch. If we started in inactive code, it's time to leave.
				if (!wasActive)
					return;

				// If we started in active code, we need to skip the outer and therefore unrelated
				// inactive branches until we hit active code again.
				try {
					skipInactiveCode();
				} catch (OffsetLimitReachedException e) {
					return;
				}
				codeBranchNesting = Math.min(getCodeBranchNesting() + 1, codeBranchNesting);

				// We could be at the start of inactive code so restart the loop.
				continue;
			}

			final boolean active = isActiveCode();
			IToken next = LAcatchEOF(1);
			if (next == null || next.getType() == IToken.tEOC)
				return;

			if (upToBrace && next.getType() == IToken.tRBRACE && active == wasActive) {
				return;
			}

			final int offset = next.getOffset();
			declarationMark = next;
			next = null; // Don't hold on to the token while parsing namespaces, class bodies, etc.
			try {
				IASTDeclaration declaration = declaration(options);
				if (((ASTNode) declaration).getLength() == 0 && LTcatchEOF(1) != IToken.tEOC) {
					declaration = skipProblemDeclaration(offset);
				}
				addDeclaration(tu, declaration, active);
			} catch (BacktrackException bt) {
				IASTDeclaration[] decls = problemDeclaration(offset, bt, options);
				for (IASTDeclaration declaration : decls) {
					addDeclaration(tu, declaration, active);
				}
			} catch (EndOfFileException e) {
				IASTDeclaration declaration = skipProblemDeclaration(offset);
				addDeclaration(tu, declaration, active);
				if (!e.endsInactiveCode()) {
					break;
				}
			} finally {
				declarationMark = null;
			}
		}
	}

	private void addDeclaration(final IASTDeclarationListOwner parent, IASTDeclaration declaration,
			final boolean active) {
		if (!active) {
			declaration.accept(MARK_INACTIVE);
		}
		parent.addDeclaration(declaration);
	}

	abstract protected IASTExpression buildBinaryExpression(int operator, IASTExpression expr1,
			IASTInitializerClause expr2, int lastOffset);

	private IASTExpression createCastVsBinaryExpressionAmbiguity(IASTBinaryExpression expr, final IASTTypeId typeid,
			int unaryOperator, int unaryOpOffset) {
		IASTUnaryExpression unary = nodeFactory.newUnaryExpression(unaryOperator, null);
		((ASTNode) unary).setOffset(unaryOpOffset);
		IASTCastExpression castExpr = buildCastExpression(IASTCastExpression.op_cast, typeid, unary, 0, 0);
		IASTExpression result = createAmbiguousBinaryVsCastExpression(expr, castExpr);
		((ASTNode) result).setOffsetAndLength((ASTNode) expr);
		return result;
	}

	protected IASTExpression unaryExpression(int operator, CastExprCtx ctx, ITemplateIdStrategy strat)
			throws EndOfFileException, BacktrackException {
		final IToken operatorToken = consume();
		IASTExpression operand = castExpression(ctx, strat);

		CastAmbiguityMarker ca = null;
		if (operand instanceof CastAmbiguityMarker) {
			ca = (CastAmbiguityMarker) operand;
			operand = ca.getExpression();
			assert !(operand instanceof CastAmbiguityMarker);
		}

		if (operator == IASTUnaryExpression.op_star && operand instanceof IASTLiteralExpression) {
			IASTLiteralExpression lit = (IASTLiteralExpression) operand;
			switch (lit.getKind()) {
			case IASTLiteralExpression.lk_char_constant:
			case IASTLiteralExpression.lk_float_constant:
			case IASTLiteralExpression.lk_integer_constant:
			case IASTLiteralExpression.lk_true:
			case IASTLiteralExpression.lk_false:
			case IASTLiteralExpression.lk_nullptr:
				throwBacktrack(operatorToken);
			}
		}

		IASTExpression result = buildUnaryExpression(operator, operand, operatorToken.getOffset(),
				calculateEndOffset(operand));
		return ca == null ? result : ca.updateExpression(result);
	}

	protected IASTExpression buildUnaryExpression(int operator, IASTExpression operand, int offset, int lastOffset) {
		IASTUnaryExpression result = nodeFactory.newUnaryExpression(operator, operand);
		setRange(result, offset, lastOffset);
		return result;
	}

	protected IASTStatement handleFunctionBody() throws BacktrackException, EndOfFileException {
		declarationMark = null;
		if (mode == ParserMode.QUICK_PARSE || mode == ParserMode.STRUCTURAL_PARSE || !isActiveCode()) {
			int offset = LA(1).getOffset();
			IToken last = skipOverCompoundStatement(true);
			IASTCompoundStatement cs = nodeFactory.newCompoundStatement();
			setRange(cs, offset, last.getEndOffset());
			return cs;
		} else if (mode == ParserMode.COMPLETION_PARSE || mode == ParserMode.SELECTION_PARSE) {
			if (scanner.isOnTopContext())
				return functionBody();
			int offset = LA(1).getOffset();
			IToken last = skipOverCompoundStatement(true);
			IASTCompoundStatement cs = nodeFactory.newCompoundStatement();
			setRange(cs, offset, last.getEndOffset());
			return cs;
		}

		// full parse
		return functionBody();
	}

	/**
	 * Parses a function body.
	 *
	 * @return the compound statement representing the function body.
	 * @throws BacktrackException
	 *             request a backtrack
	 */
	protected IASTCompoundStatement functionBody() throws EndOfFileException, BacktrackException {
		return compoundStatement();
	}

	protected abstract IASTDeclarator initDeclarator(IASTDeclSpecifier declSpec, DeclarationOptions option)
			throws EndOfFileException, BacktrackException, FoundAggregateInitializer;

	/**
	 * Parse an enumeration specifier, as according to the ANSI specs in C &
	 * C++. enumSpecifier: "enum" (name)? "{" (enumerator-list) "}"
	 * enumerator-list: enumerator-definition enumerator-list ,
	 * enumerator-definition enumerator-definition: enumerator enumerator =
	 * constant-expression enumerator: identifier
	 *
	 * @throws BacktrackException request a backtrack
	 */
	protected IASTEnumerationSpecifier enumSpecifier() throws BacktrackException, EndOfFileException {
		final IToken mark = mark();
		final int offset = consume().getOffset();

		// if __attribute__ or __declspec occurs after struct/union/class and before the identifier
		__attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers);

		IASTName name;
		if (LT(1) == IToken.tIDENTIFIER) {
			name = identifier();
		} else {
			name = nodeFactory.newName();
		}

		if (LT(1) != IToken.tLBRACE) {
			backup(mark);
			throwBacktrack(mark);
		}

		final IASTEnumerationSpecifier result = nodeFactory.newEnumerationSpecifier(name);

		int endOffset = enumBody(result);
		return setRange(result, offset, endOffset);
	}

	protected int enumBody(final IASTEnumerationSpecifier result) throws EndOfFileException, BacktrackException {
		boolean needComma = false;
		int endOffset = consume(IToken.tLBRACE).getEndOffset(); // IToken.tLBRACE
		int problemOffset = endOffset;
		try {
			loop: while (true) {
				switch (LTcatchEOF(1)) {
				case 0: // eof
					endOffset = getEndOffset();
					break loop;
				case IToken.tRBRACE:
					endOffset = consume().getEndOffset();
					break loop;
				case IToken.tEOC:
					break loop;
				case IToken.tCOMMA:
					if (!needComma) {
						problemOffset = LA(1).getOffset();
						throw backtrack;
					}
					endOffset = consume().getEndOffset();
					needComma = false;
					continue loop;
				case IToken.tIDENTIFIER:
				case IToken.tCOMPLETION:
					problemOffset = LA(1).getOffset();
					if (needComma)
						throw backtrack;

					final IASTName etorName = identifier();
					final IASTEnumerator enumerator = nodeFactory.newEnumerator(etorName, null);

					List<IASTAttributeSpecifier> attributes = anyAttributes(supportAttributeSpecifiers,
							supportDeclspecSpecifiers);
					addAttributeSpecifiers(attributes, enumerator);
					endOffset = attributesEndOffset(calculateEndOffset(etorName), attributes);
					setRange(enumerator, problemOffset, endOffset);

					result.addEnumerator(enumerator);
					if (LTcatchEOF(1) == IToken.tASSIGN) {
						problemOffset = consume().getOffset();
						final IASTExpression value = constantExpression();
						enumerator.setValue(value);
						adjustLength(enumerator, value);
						endOffset = calculateEndOffset(value);
					}
					needComma = true;
					continue loop;
				default:
					problemOffset = LA(1).getOffset();
					throw backtrack;
				}
			}
		} catch (EndOfFileException eof) {
			throwBacktrack(createProblem(IProblem.SYNTAX_ERROR, problemOffset, getEndOffset() - problemOffset), result);
		} catch (BacktrackException bt) {
			IASTProblem problem = skipProblemEnumerator(problemOffset);
			throwBacktrack(problem, result);
		}
		return endOffset;
	}

	protected abstract IASTStatement statement() throws EndOfFileException, BacktrackException;

	protected IASTExpression condition(boolean followedByParenthesis) throws BacktrackException, EndOfFileException {
		IToken mark = mark();
		try {
			IASTExpression expr = expression();
			if (!followedByParenthesis)
				return expr;

			switch (LT(1)) {
			case IToken.tEOC:
			case IToken.tRPAREN:
				return expr;
			}
		} catch (BacktrackException b) {
			if (!followedByParenthesis)
				throw b;
		}
		backup(mark);
		return skipProblemConditionInParenthesis(mark.getOffset());
	}

	@Override
	public boolean encounteredError() {
		return !parsePassed;
	}

	protected abstract IASTDeclaration declaration(DeclarationOptions option)
			throws BacktrackException, EndOfFileException;

	protected Decl declSpecifierSeq(DeclarationOptions option) throws BacktrackException, EndOfFileException {
		return declSpecifierSeq(option, null);
	}

	/**
	 * Parses for two alternatives of a declspec sequence. If there is a second alternative the token after the second alternative
	 * is returned, such that the parser can continue after both variants.
	 */
	protected abstract Decl declSpecifierSeq(DeclarationOptions option, ITemplateIdStrategy strat)
			throws BacktrackException, EndOfFileException;

	protected Decl declSpecifierSequence_initDeclarator(final DeclarationOptions option,
			boolean acceptCompoundWithoutDtor)
			throws EndOfFileException, FoundAggregateInitializer, BacktrackException {
		return declSpecifierSequence_initDeclarator(option, acceptCompoundWithoutDtor, null);
	}

	/**
	 * Checks whether the type specified by the {@code declSpecifier} is "auto" and
	 * the subsequent token, when looking beyond potential ref-qualifiers (& and &&),
	 * will be a single opening bracket ([) followed by an identifier.
	 */
	protected boolean isAtStartOfStructuredBinding(IASTDeclSpecifier declSpecifier) {
		if (!isAutoType(declSpecifier)) {
			return false;
		}
		int expectedBracketOffset = 1;
		int nextToken = LTcatchEOF(expectedBracketOffset);
		if (nextToken == IToken.tAMPER || nextToken == IToken.tAND) {
			expectedBracketOffset++;
		}
		return LTcatchEOF(expectedBracketOffset) == IToken.tLBRACKET
				&& LTcatchEOF(expectedBracketOffset + 1) == IToken.tIDENTIFIER;
	}

	/**
	 * Parses for two alternatives of a declspec sequence followed by a initDeclarator.
	 * A second alternative is accepted only, if it ends at the same point of the first alternative. Otherwise the
	 * longer alternative is selected.
	 */
	protected Decl declSpecifierSequence_initDeclarator(final DeclarationOptions option,
			boolean acceptCompoundWithoutDtor, ITemplateIdStrategy strat)
			throws EndOfFileException, FoundAggregateInitializer, BacktrackException {
		Decl result = declSpecifierSeq(option, strat);

		final int lt1 = LTcatchEOF(1);
		if (lt1 == IToken.tEOC)
			return result;

		// support for structured bindings
		if (isAtStartOfStructuredBinding(result.fDeclSpec1)) {
			result.isAtStartOfStructuredBinding = true;
			return result;
		}

		// support simple declarations without declarators
		final boolean acceptEmpty = acceptCompoundWithoutDtor && isLegalWithoutDtor(result.fDeclSpec1);
		if (acceptEmpty) {
			switch (lt1) {
			case 0:
			case IToken.tEOC:
			case IToken.tSEMI:
				return result;
			}
		}

		final IToken dtorMark1 = mark();
		final IToken dtorMark2 = result.fDtorToken1;
		final IASTDeclSpecifier declspec1 = result.fDeclSpec1;
		final IASTDeclSpecifier declspec2 = result.fDeclSpec2;
		IASTDeclarator dtor1, dtor2;
		try {
			// declarator for first variant
			dtor1 = initDeclarator(declspec1, option);
		} catch (BacktrackException e) {
			if (acceptEmpty) {
				backup(dtorMark1);
				dtor1 = null;
			} else {
				// try second variant, if possible
				if (dtorMark2 == null)
					throw e;

				backup(dtorMark2);
				dtor2 = initDeclarator(declspec2, option);
				return result.set(declspec2, dtor2, dtorMark2);
			}
		}

		// first variant was a success. If possible, try second one.
		if (dtorMark2 == null) {
			return result.set(declspec1, dtor1, dtorMark1);
		}

		final IToken end1 = mark();
		backup(dtorMark2);
		try {
			dtor2 = initDeclarator(declspec2, option);
		} catch (BacktrackException e) {
			backup(end1);
			return result.set(declspec1, dtor1, dtorMark1);
		}

		final IToken end2 = mark();
		if (end1 == end2) {
			return result.set(declspec1, dtor1, declspec2, dtor2);
		}
		if (end1.getEndOffset() > end2.getEndOffset()) {
			backup(end1);
			return result.set(declspec1, dtor1, dtorMark1);
		}

		return result.set(declspec2, dtor2, dtorMark2);
	}

	protected boolean isLegalWithoutDtor(IASTDeclSpecifier declSpec) {
		if (declSpec instanceof IASTCompositeTypeSpecifier)
			return true;
		if (declSpec instanceof IASTElaboratedTypeSpecifier)
			return true;
		if (declSpec instanceof IASTEnumerationSpecifier)
			return true;

		return false;
	}

	protected IASTDeclaration[] problemDeclaration(int offset, BacktrackException bt, DeclarationOptions option) {
		failParse();
		IASTProblem origProblem = createProblem(bt);

		// a node was detected by assuming additional tokens (e.g. missing semicolon)
		IASTNode n = bt.getNodeBeforeProblem();
		if (n instanceof IASTDeclaration) {
			IToken la1 = LAcatchEOF(1);
			if (la1 == null || la1.getOffset() > offset) {
				declarationMark = null;
				return new IASTDeclaration[] { (IASTDeclaration) n, buildProblemDeclaration(origProblem) };
			}
		}

		if (declarationMark != null && isActiveCode()) {
			IASTDeclaration trailingProblem = null;
			offset = declarationMark.getOffset();

			// try to skip identifiers (undefined macros?)
			IASTDeclaration decl = null;
			int endOffset = 0;
			loop: while (declarationMark != null && declarationMark.getType() == IToken.tIDENTIFIER) {
				endOffset = declarationMark.getEndOffset();
				declarationMark = declarationMark.getNext();
				if (declarationMark != null) {
					backup(declarationMark);
					// avoid creating an empty declaration
					switch (LTcatchEOF(1)) {
					case 0: // eof
					case IToken.tEOC:
					case IToken.tSEMI:
						break loop;
					}
					try {
						decl = declaration(option);
						break;
					} catch (BacktrackException bt2) {
						n = bt2.getNodeBeforeProblem();
						if (n instanceof IASTDeclaration) {
							decl = (IASTDeclaration) n;
							trailingProblem = buildProblemDeclaration(bt2.getProblem());
							break;
						}
					} catch (EndOfFileException e) {
						endOffset = getEndOffset();
						break;
					}
				}
			}
			declarationMark = null;

			if (decl != null) {
				IASTProblem problem = createProblem(IProblem.SYNTAX_ERROR, offset, endOffset - offset);
				IASTDeclaration pd = buildProblemDeclaration(problem);
				if (trailingProblem != null)
					return new IASTDeclaration[] { pd, decl, trailingProblem };
				return new IASTDeclaration[] { pd, decl };
			}
		}

		return new IASTDeclaration[] { skipProblemDeclaration(offset, origProblem) };
	}

	protected IASTDeclaration asmDeclaration() throws EndOfFileException, BacktrackException {
		final int offset = consume().getOffset(); // t_asm
		if (LT(1) == IToken.t_volatile) {
			consume();
		}

		if (supportFunctionStyleAsm && LT(1) != IToken.tLPAREN) {
			return functionStyleAsmDeclaration();
		}

		StringBuilder buffer = new StringBuilder();
		asmExpression(buffer);
		int lastOffset = consume(IToken.tSEMI).getEndOffset();

		return buildASMDirective(offset, buffer.toString(), lastOffset);
	}

	protected IASTDeclaration functionStyleAsmDeclaration() throws BacktrackException, EndOfFileException {
		final int offset = LA(1).getOffset();
		IASTDeclSpecifier declSpec;
		IASTDeclarator dtor;
		try {
			Decl decl = declSpecifierSequence_initDeclarator(DeclarationOptions.FUNCTION_STYLE_ASM, false);
			declSpec = decl.fDeclSpec1;
			dtor = decl.fDtor1;
		} catch (FoundAggregateInitializer lie) {
			declSpec = lie.fDeclSpec;
			dtor = addInitializer(lie, DeclarationOptions.FUNCTION_STYLE_ASM);
		}

		if (LT(1) != IToken.tLBRACE)
			throwBacktrack(LA(1));

		final IASTDeclarator fdtor = ASTQueries.findTypeRelevantDeclarator(dtor);
		if (!(fdtor instanceof IASTFunctionDeclarator))
			throwBacktrack(offset, LA(1).getEndOffset() - offset);

		final int compoundOffset = LA(1).getOffset();
		final int endOffset = skipOverCompoundStatement(false).getEndOffset();
		IASTCompoundStatement cs = nodeFactory.newCompoundStatement(); //createCompoundStatement();
		((ASTNode) cs).setOffsetAndLength(compoundOffset, endOffset - compoundOffset);

		IASTFunctionDefinition funcDefinition = nodeFactory.newFunctionDefinition(declSpec,
				(IASTFunctionDeclarator) fdtor, cs);
		((ASTNode) funcDefinition).setOffsetAndLength(offset, endOffset - offset);

		return funcDefinition;
	}

	protected abstract IASTInitializer optionalInitializer(IASTDeclarator dtor, DeclarationOptions options)
			throws EndOfFileException, BacktrackException;

	protected IASTDeclarator addInitializer(FoundAggregateInitializer e, DeclarationOptions options)
			throws EndOfFileException, BacktrackException {
		final IASTDeclarator d = e.fDeclarator;
		IASTInitializer i = optionalInitializer(d, options);
		if (i != null) {
			d.setInitializer(i);
			((ASTNode) d).setLength(calculateEndOffset(i) - ((ASTNode) d).getOffset());
		}
		return d;
	}

	protected IToken asmExpression(StringBuilder content) throws EndOfFileException, BacktrackException {
		IToken t = consume(IToken.tLPAREN);
		boolean needspace = false;
		int open = 1;
		while (open > 0) {
			t = consume();
			switch (t.getType()) {
			case IToken.tLPAREN:
				open++;
				break;
			case IToken.tRPAREN:
				open--;
				break;
			case IToken.tEOC:
				throw new EndOfFileException(t.getOffset());

			default:
				if (content != null) {
					if (needspace) {
						content.append(' ');
					}
					content.append(t.getCharImage());
					needspace = true;
				}
				break;
			}
		}
		return t;
	}

	protected IASTASMDeclaration buildASMDirective(int offset, String assembly, int lastOffset) {
		IASTASMDeclaration result = nodeFactory.newASMDeclaration(assembly);
		((ASTNode) result).setOffsetAndLength(offset, lastOffset - offset);
		return result;
	}

	protected IASTCastExpression buildCastExpression(int op, IASTTypeId typeId, IASTExpression operand, int offset,
			int endOffset) {
		IASTCastExpression result = nodeFactory.newCastExpression(op, typeId, operand);
		((ASTNode) result).setOffsetAndLength(offset, endOffset - offset);
		return result;
	}

	/**
	 * There are many ambiguities in C and C++ between expressions and declarations.
	 * This method will attempt to parse a statement as both an expression and a declaration,
	 * if both parses succeed then an ambiguity node is returned.
	 */
	protected IASTStatement parseDeclarationOrExpressionStatement() throws EndOfFileException, BacktrackException {
		return parseDeclarationOrExpressionStatement(null);
	}

	protected IASTStatement parseDeclarationOrExpressionStatement(List<IASTAttributeSpecifier> attributeSpecifiers)
			throws EndOfFileException, BacktrackException {
		// First attempt to parse an expressionStatement
		// Note: the function style cast ambiguity is handled in expression
		// Since it only happens when we are in a statement
		IToken mark = mark();
		IASTExpressionStatement expressionStatement = null;
		IToken afterExpression = null;
		boolean foundSemicolon = false;
		try {
			IASTExpression expression = expression();
			expressionStatement = nodeFactory.newExpressionStatement(expression);
			addAttributeSpecifiers(attributeSpecifiers, expressionStatement);
			setRange(expressionStatement, expression);
			afterExpression = LA();

			IToken semi = consumeOrEOC(IToken.tSEMI);
			foundSemicolon = true;
			adjustEndOffset(expressionStatement, semi.getEndOffset());
			afterExpression = LA();
		} catch (BacktrackException b) {
		}

		backup(mark);

		// Now attempt to parse a declarationStatement
		IASTDeclarationStatement ds = null;
		try {
			IASTDeclaration d = declaration(DeclarationOptions.LOCAL);
			if (d instanceof IASTAttributeOwner) {
				addAttributeSpecifiers(attributeSpecifiers, (IASTAttributeOwner) d);
			}
			ds = nodeFactory.newDeclarationStatement(d);
			setRange(ds, d);
		} catch (BacktrackException b) {
			IASTNode node = b.getNodeBeforeProblem();
			final boolean isProblemDecl = node instanceof IASTDeclaration;
			if (expressionStatement == null
					|| (!foundSemicolon && isProblemDecl && node.contains(expressionStatement))) {
				if (isProblemDecl) {
					ds = nodeFactory.newDeclarationStatement((IASTDeclaration) node);
					b.initialize(b.getProblem(), setRange(ds, node));
				}
				throw b;
			}
		}

		if (ds == null) {
			backup(afterExpression);
			if (foundSemicolon)
				return expressionStatement;

			throwBacktrack(createProblem(IProblem.MISSING_SEMICOLON, calculateEndOffset(expressionStatement) - 1, 1),
					expressionStatement);
			return null; // Hint for java-compiler
		}

		if (expressionStatement == null || !foundSemicolon) {
			return ds;
		}

		// At this point we know we have an ambiguity.
		// Attempt to resolve some ambiguities that are easy to detect.

		// A * B = C;  // A*B cannot be a lvalue.
		// foo() = x;  // foo() cannot be a lvalue in c, in c++ it can.
		if (expressionStatement.getExpression() instanceof IASTBinaryExpression) {
			IASTBinaryExpression exp = (IASTBinaryExpression) expressionStatement.getExpression();
			if (exp.getOperator() == IASTBinaryExpression.op_assign) {
				IASTExpression lhs = exp.getOperand1();
				if (lhs instanceof IASTBinaryExpression
						&& ((IASTBinaryExpression) lhs).getOperator() == IASTBinaryExpression.op_multiply) {
					return ds;
				}
				if (lhs instanceof IASTFunctionCallExpression && !functionCallCanBeLValue) {
					return ds;
				}
			}
		}

		final IASTDeclaration declaration = ds.getDeclaration();
		if (declaration instanceof IASTSimpleDeclaration) {
			final IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) declaration;
			IASTDeclSpecifier declspec = simpleDecl.getDeclSpecifier();
			if (declspec instanceof IASTNamedTypeSpecifier) {
				final IASTDeclarator[] declarators = simpleDecl.getDeclarators();

				// x;
				// can be parsed as a named declaration specifier without a declarator
				if (declarators.length == 0) {
					backup(afterExpression);
					return expressionStatement;
				}
			}
		}

		// create and return ambiguity node
		IASTAmbiguousStatement statement = createAmbiguousStatement();
		statement.addStatement(expressionStatement);
		statement.addStatement(ds);
		return setRange(statement, ds);
	}

	/**
	 * Returns true if the given declaration has unspecified type,
	 * in this case the type defaults to int and is know as "implicit int".
	 */
	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;
	}

	protected static boolean isAutoType(IASTDeclSpecifier declSpec) {
		if (declSpec instanceof IASTSimpleDeclSpecifier) {
			IASTSimpleDeclSpecifier simpleDeclSpecifier = (IASTSimpleDeclSpecifier) declSpec;
			return simpleDeclSpecifier.getType() == IASTSimpleDeclSpecifier.t_auto;
		}
		return false;
	}

	protected abstract IASTAmbiguousStatement createAmbiguousStatement();

	protected IASTStatement parseLabelStatement() throws EndOfFileException, BacktrackException {
		int offset = LA(1).getOffset();
		IASTName name = identifier(); // tIDENTIFIER
		consume(IToken.tCOLON); // tCOLON
		IASTStatement nestedStatement = statement();
		int lastOffset = calculateEndOffset(nestedStatement);

		IASTLabelStatement label_statement = nodeFactory.newLabelStatement(name, nestedStatement);
		setRange(label_statement, offset, lastOffset);
		return label_statement;
	}

	protected IASTStatement parseNullStatement() throws EndOfFileException, BacktrackException {
		IToken t = consume(); // tSEMI

		IASTNullStatement null_statement = nodeFactory.newNullStatement();
		((ASTNode) null_statement).setOffsetAndLength(t.getOffset(), t.getEndOffset() - t.getOffset());
		return null_statement;
	}

	protected IASTStatement parseGotoStatement() throws EndOfFileException, BacktrackException {
		int startOffset = consume(IToken.t_goto).getOffset();
		IASTStatement gotoStatement = null;

		if (LT(1) == IToken.tSTAR) {
			IASTExpression gotoLabelNameExpression = expression();
			gotoStatement = nodeFactory.newGotoStatement(gotoLabelNameExpression);
		} else {
			IASTName gotoLabelName = identifier();
			gotoStatement = nodeFactory.newGotoStatement(gotoLabelName);
		}

		int lastOffset = consume(IToken.tSEMI).getEndOffset();
		((ASTNode) gotoStatement).setOffsetAndLength(startOffset, lastOffset - startOffset);
		return gotoStatement;
	}

	protected IASTStatement parseBreakStatement() throws EndOfFileException, BacktrackException {
		int startOffset = consume().getOffset(); // t_break
		int lastOffset = consume(IToken.tSEMI).getEndOffset();

		IASTBreakStatement break_statement = nodeFactory.newBreakStatement();
		((ASTNode) break_statement).setOffsetAndLength(startOffset, lastOffset - startOffset);
		return break_statement;
	}

	protected IASTStatement parseSwitchBody() throws EndOfFileException, BacktrackException {
		IASTStatement stmt = null;
		if (LT(1) != IToken.tEOC)
			stmt = statement();

		if (!(stmt instanceof IASTCaseStatement) && !(stmt instanceof IASTDefaultStatement))
			return stmt;

		// bug 105334, switch without compound statement
		IASTCompoundStatement comp = nodeFactory.newCompoundStatement();
		((ASTNode) comp).setOffsetAndLength((ASTNode) stmt);
		comp.addStatement(stmt);

		while (LT(1) != IToken.tEOC && (stmt instanceof IASTCaseStatement || stmt instanceof IASTDefaultStatement)) {
			stmt = statement();
			comp.addStatement(stmt);
		}
		adjustLength(comp, stmt);
		return comp;
	}

	protected IASTStatement parseContinueStatement() throws EndOfFileException, BacktrackException {
		int startOffset = consume().getOffset(); // t_continue
		int lastOffset = consume(IToken.tSEMI).getEndOffset();

		IASTContinueStatement continue_statement = nodeFactory.newContinueStatement();
		((ASTNode) continue_statement).setOffsetAndLength(startOffset, lastOffset - startOffset);
		return continue_statement;
	}

	protected IASTStatement parseReturnStatement() throws EndOfFileException, BacktrackException {
		final int offset = consume(IToken.t_return).getOffset();

		// Optional expression
		IASTExpression expr = null;
		if (LT(1) != IToken.tSEMI) {
			expr = expression();
		}
		// Semicolon
		final int endOffset = consumeOrEOC(IToken.tSEMI).getEndOffset();

		return setRange(nodeFactory.newReturnStatement(expr), offset, endOffset);
	}

	protected IASTStatement parseDoStatement() throws EndOfFileException, BacktrackException {
		int startOffset;
		startOffset = consume().getOffset(); // t_do
		IASTStatement do_body = statement();

		IASTExpression do_condition = null;
		if (LT(1) != IToken.tEOC) {
			consume(IToken.t_while);
			consume(IToken.tLPAREN);
			do_condition = condition(true);
		}

		int lastOffset;
		switch (LT(1)) {
		case IToken.tRPAREN:
		case IToken.tEOC:
			consume();
			break;
		default:
			throw backtrack;
		}

		switch (LT(1)) {
		case IToken.tSEMI:
		case IToken.tEOC:
			lastOffset = consume().getEndOffset();
			break;
		default:
			throw backtrack;
		}

		IASTDoStatement do_statement = nodeFactory.newDoStatement(do_body, do_condition);
		((ASTNode) do_statement).setOffsetAndLength(startOffset, lastOffset - startOffset);
		return do_statement;
	}

	protected IASTStatement parseWhileStatement() throws EndOfFileException, BacktrackException {
		int startOffset = consume().getOffset();
		consume(IToken.tLPAREN);
		IASTExpression while_condition = condition(true);
		switch (LT(1)) {
		case IToken.tRPAREN:
			consume();
			break;
		case IToken.tEOC:
			break;
		default:
			throwBacktrack(LA(1));
		}
		IASTStatement while_body = null;
		if (LT(1) != IToken.tEOC)
			while_body = statement();

		IASTWhileStatement while_statement = nodeFactory.newWhileStatement(while_condition, while_body);
		((ASTNode) while_statement).setOffsetAndLength(startOffset,
				(while_body != null ? calculateEndOffset(while_body) : LA(1).getEndOffset()) - startOffset);

		return while_statement;
	}

	/**
	 * @param result
	 */
	protected void reconcileLengths(IASTIfStatement result) {
		if (result == null)
			return;
		IASTIfStatement current = result;
		while (current.getElseClause() instanceof IASTIfStatement)
			current = (IASTIfStatement) current.getElseClause();

		while (current != null) {
			ASTNode r = ((ASTNode) current);
			if (current.getElseClause() != null) {
				ASTNode else_clause = ((ASTNode) current.getElseClause());
				r.setLength(else_clause.getOffset() + else_clause.getLength() - r.getOffset());
			} else {
				ASTNode then_clause = (ASTNode) current.getThenClause();
				if (then_clause != null)
					r.setLength(then_clause.getOffset() + then_clause.getLength() - r.getOffset());
			}
			if (current.getParent() != null && current.getParent() instanceof IASTIfStatement)
				current = (IASTIfStatement) current.getParent();
			else
				current = null;
		}
	}

	protected IASTStatement parseCompoundStatement() throws EndOfFileException, BacktrackException {
		IASTCompoundStatement compound = compoundStatement();
		return compound;
	}

	protected IASTStatement parseDefaultStatement() throws EndOfFileException, BacktrackException {
		int startOffset = consume(IToken.t_default).getOffset();
		int lastOffset = consume(IToken.tCOLON).getEndOffset();

		IASTDefaultStatement df = nodeFactory.newDefaultStatement();
		((ASTNode) df).setOffsetAndLength(startOffset, lastOffset - startOffset);
		return df;
	}

	protected IASTStatement parseCaseStatement() throws EndOfFileException, BacktrackException {
		int startOffset = consume().getOffset(); // t_case
		IASTExpression caseExpression = constantExpression();
		int lt1 = LT(1);
		if (lt1 == IToken.tELLIPSIS) {
			consume();
			IASTExpression upperBoundExpression = constantExpression();
			caseExpression = buildBinaryExpression(IASTBinaryExpression.op_ellipses, caseExpression,
					upperBoundExpression, calculateEndOffset(upperBoundExpression));
			lt1 = LT(1);
		}
		int lastOffset = 0;
		switch (lt1) {
		case IToken.tCOLON:
		case IToken.tEOC:
			lastOffset = consume().getEndOffset();
			break;
		default:
			throwBacktrack(LA(1));
		}

		IASTCaseStatement cs = nodeFactory.newCaseStatement(caseExpression);
		((ASTNode) cs).setOffsetAndLength(startOffset, lastOffset - startOffset);
		return cs;
	}

	protected int figureEndOffset(IASTDeclSpecifier declSpec, IASTDeclarator[] declarators) {
		if (declarators.length == 0)
			return calculateEndOffset(declSpec);
		return calculateEndOffset(declarators[declarators.length - 1]);
	}

	protected int figureEndOffset(IASTDeclSpecifier declSpecifier, IASTDeclarator declarator) {
		if (declarator == null || ((ASTNode) declarator).getLength() == 0)
			return calculateEndOffset(declSpecifier);
		return calculateEndOffset(declarator);
	}

	protected void throwBacktrack(IToken token) throws BacktrackException {
		throwBacktrack(token.getOffset(), token.getLength());
	}

	protected IASTExpression parseTypeidInParenthesisOrUnaryExpression(boolean exprIsLimitedToParenthesis, int offset,
			int typeExprKind, int unaryExprKind, CastExprCtx ctx, ITemplateIdStrategy strat)
			throws BacktrackException, EndOfFileException {
		IASTTypeId typeid;
		IASTExpression expr = null;
		IToken typeidLA = null;
		IToken mark = mark();
		int endOffset1 = -1;
		int endOffset2 = -1;

		try {
			consume(IToken.tLPAREN);
			int typeidOffset = LA(1).getOffset();
			typeid = typeId(DeclarationOptions.TYPEID);
			if (!isValidTypeIDForUnaryExpression(unaryExprKind, typeid)) {
				typeid = null;
			} else {
				switch (LT(1)) {
				case IToken.tRPAREN:
				case IToken.tEOC:
					endOffset1 = consume().getEndOffset();
					typeidLA = LA(1);
					break;
				case IToken.tCOMMA:
					if (supportExtendedSizeofOperator && typeExprKind == IASTTypeIdExpression.op_sizeof) {
						consume();
						IASTExpression expr2 = expression();
						endOffset1 = consumeOrEOC(IToken.tRPAREN).getEndOffset();
						expr = nodeFactory.newTypeIdExpression(IASTTypeIdExpression.op_typeof, typeid);
						setRange(expr, typeidOffset, calculateEndOffset(typeid));

						IASTExpressionList expressionList = nodeFactory.newExpressionList();
						((ASTNode) expressionList).setOffsetAndLength(typeidOffset,
								calculateEndOffset(expr2) - typeidOffset);
						expressionList.addExpression(expr);
						if (expr2 instanceof IASTExpressionList) {
							for (IASTExpression e : ((IASTExpressionList) expr2).getExpressions()) {
								expressionList.addExpression(e);
							}
						} else {
							expressionList.addExpression(expr2);
						}

						return buildUnaryExpression(unaryExprKind, expressionList, offset, endOffset1);
					}
					typeid = null;
					break;
				default:
					typeid = null;
					break;
				}
			}
		} catch (BacktrackException e) {
			typeid = null;
		}

		CastAmbiguityMarker ca = null;
		backup(mark);
		try {
			if (exprIsLimitedToParenthesis) {
				consume(IToken.tLPAREN);
				expr = expression();
				endOffset2 = consumeOrEOC(IToken.tRPAREN).getEndOffset();
			} else {
				expr = unaryExpression(ctx, strat);
				if (expr instanceof CastAmbiguityMarker) {
					ca = (CastAmbiguityMarker) expr;
					expr = ca.getExpression();
					assert !(expr instanceof CastAmbiguityMarker);
				}
				endOffset2 = calculateEndOffset(expr);
			}
		} catch (BacktrackException bte) {
			if (typeid == null)
				throw bte;
		}

		IASTExpression result1 = null;
		if (typeid != null && endOffset1 >= endOffset2) {
			IASTTypeIdExpression typeIdExpression = nodeFactory.newTypeIdExpression(typeExprKind, typeid);
			setRange(typeIdExpression, offset, endOffset1);
			result1 = typeIdExpression;
			backup(typeidLA);

			if (expr == null || endOffset1 > endOffset2)
				return result1;
		}

		IASTExpression result2 = unaryExprKind == -1 ? expr
				: buildUnaryExpression(unaryExprKind, expr, offset, endOffset2);
		if (ca != null)
			result2 = ca.updateExpression(result2);

		if (result1 == null)
			return result2;

		IASTAmbiguousExpression ambExpr = createAmbiguousExpression();
		ambExpr.addExpression(result1);
		ambExpr.addExpression(result2);
		((ASTNode) ambExpr).setOffsetAndLength((ASTNode) result1);
		return ambExpr;
	}

	private boolean isValidTypeIDForUnaryExpression(int unaryExprKind, IASTTypeId typeid) {
		if (typeid == null)
			return false;
		if (unaryExprKind == IASTUnaryExpression.op_sizeof) {
			// 5.3.3.1
			if (ASTQueries.findTypeRelevantDeclarator(typeid.getAbstractDeclarator()) instanceof IASTFunctionDeclarator)
				return false;
		}
		return true;
	}

	protected abstract IASTAmbiguousExpression createAmbiguousExpression();

	protected abstract IASTAmbiguousExpression createAmbiguousBinaryVsCastExpression(IASTBinaryExpression binary,
			IASTCastExpression castExpr);

	protected abstract IASTAmbiguousExpression createAmbiguousCastVsFunctionCallExpression(IASTCastExpression castExpr,
			IASTFunctionCallExpression funcCall);

	protected IASTStatement initStatement() throws BacktrackException, EndOfFileException {
		if (LT(1) == IToken.tSEMI)
			return parseNullStatement();
		try {
			return parseDeclarationOrExpressionStatement();
		} catch (BacktrackException e) {
			// A init statement always terminates with a semicolon
			IASTNode before = e.getNodeBeforeProblem();
			if (before != null) {
				e.initialize(e.getProblem());
			}
			throw e;
		}
	}

	protected List<IASTAttributeSpecifier> anyAttributes(boolean allowAttrib, boolean allowDeclspec)
			throws BacktrackException, EndOfFileException {
		return __attribute_decl_seq(allowAttrib, allowDeclspec);
	}

	/**
	 * Accepts a sequence of __attribute__ or __declspec.
	 *
	 * @param allowAttrib if true accept any number of __attribute__
	 * @param allowDeclspec if true accept any number of __declspec
	 * @return the list of attributes, or {@code null} if there are none
	 * @throws BacktrackException
	 * @throws EndOfFileException
	 */
	protected List<IASTAttributeSpecifier> __attribute_decl_seq(boolean allowAttrib, boolean allowDeclspec)
			throws BacktrackException, EndOfFileException {
		List<IASTAttributeSpecifier> result = null;
		while (true) {
			final int lt = LTcatchEOF(1);
			if (allowAttrib && (lt == IGCCToken.t__attribute__)) {
				if (result == null) {
					result = new ArrayList<>();
				}
				result.add(__attribute__());
			} else if (allowDeclspec && (lt == IGCCToken.t__declspec)) {
				if (result == null) {
					result = new ArrayList<>();
				}
				result.add(__declspec());
			} else {
				break;
			}
		}
		return result;
	}

	/**
	 * Parses an __attribute__ clause.
	 * @return the list of attributes, or {@code null} if the __attribute__ clause contained
	 *     no attributes
	 * @throws BacktrackException
	 * @throws EndOfFileException
	 */
	protected IASTAttributeList __attribute__() throws BacktrackException, EndOfFileException {
		if (LT(1) != IGCCToken.t__attribute__)
			return null;

		IASTAttributeList result = nodeFactory.newGCCAttributeList();
		final int startOffset = LA().getOffset();
		int endOffset = consume().getEndOffset();
		if (LT(1) == IToken.tLPAREN) {
			consume();
			consume(IToken.tLPAREN);

			addAttributesOrDeclspecs(result);

			consumeOrEOC(IToken.tRPAREN);
			endOffset = consumeOrEOC(IToken.tRPAREN).getEndOffset();
		}
		return setRange(result, startOffset, endOffset);
	}

	protected IASTAttribute singleAttribute() throws EndOfFileException, BacktrackException {
		// Get an identifier including keywords
		IToken nameToken = identifierOrKeyword();
		IASTToken argumentClause = null;
		int endOffset = nameToken.getEndOffset();

		// Check for arguments
		if (LT(1) == IToken.tLPAREN) {
			int argumentOffset = consume().getEndOffset();
			argumentClause = balancedTokenSeq(argumentOffset, IToken.tRPAREN);
			endOffset = consumeOrEOC(IToken.tRPAREN).getEndOffset();
		}
		char[] attributeName = nameToken.getCharImage();

		IASTAttribute result = nodeFactory.newAttribute(attributeName, argumentClause);
		setRange(result, nameToken.getOffset(), endOffset);
		return result;
	}

	protected final int attributesStartOffset(int startOffset, List<IASTAttributeSpecifier> specifiers) {
		if (specifiers == null || specifiers.isEmpty()) {
			return startOffset;
		}
		ASTNode firstSpecifier = (ASTNode) specifiers.get(0);
		return Math.min(startOffset, firstSpecifier.getOffset());
	}

	protected final int attributesEndOffset(int endOffset, List<IASTAttributeSpecifier> specifiers) {
		if (specifiers == null || specifiers.isEmpty()) {
			return endOffset;
		}
		ASTNode lastSpecifier = (ASTNode) specifiers.get(specifiers.size() - 1);
		return Math.max(endOffset, calculateEndOffset(lastSpecifier));
	}

	protected final void addAttributeSpecifiers(List<IASTAttributeSpecifier> specifiers, IASTAttributeOwner owner) {
		assert owner != null;
		if (specifiers != null) {
			for (IASTAttributeSpecifier specifier : specifiers) {
				owner.addAttributeSpecifier(specifier);
			}
		}
	}

	protected boolean isIdentifierOrKeyword(IToken token) {
		char[] image = token.getCharImage();
		if (image.length == 0) {
			return false;
		}
		char firstChar = image[0];
		return Character.isLetter(firstChar) || firstChar == '_';
	}

	protected IToken identifierOrKeyword() throws EndOfFileException, BacktrackException {
		IToken token = LA(1);
		if (!isIdentifierOrKeyword(token)) {
			throw backtrack;
		}
		consume();
		return token;
	}

	/**
	 * Parses sequence of tokens until encountering a token of a given type
	 * @param offset the offset for the returned token node.
	 * @param endType the type of the token to stop before
	 * @return a token sequence, possibly empty but never {@code null}
	 */
	protected IASTTokenList balancedTokenSeq(int offset, int endType) throws EndOfFileException, BacktrackException {
		IASTTokenList result = nodeFactory.newTokenList();
		IToken t;
		while ((t = LA(1)).getType() != endType) {
			t = consume();

			if (t.getType() == IToken.tCOMPLETION || t.getType() == IToken.tEOC) {
				break;
			}

			result.addToken(createASTToken(t));

			IASTToken token;
			switch (t.getType()) {
			case IToken.tLPAREN:
				token = balancedTokenSeq(t.getOffset(), IToken.tRPAREN);
				break;

			case IToken.tLBRACKET:
				token = balancedTokenSeq(t.getOffset(), IToken.tRBRACKET);
				break;

			case IToken.tLBRACE:
				token = balancedTokenSeq(t.getOffset(), IToken.tRBRACE);
				break;

			default:
				continue;
			}
			result.addToken(token);
			t = consume();
			token = createASTToken(t);
			result.addToken(token);
		}

		setRange(result, offset, t.getEndOffset());
		return result;
	}

	private IASTToken createASTToken(IToken t) {
		IASTToken token;
		token = nodeFactory.newToken(t.getType(), t.getCharImage());
		setRange(token, t.getOffset(), t.getEndOffset());
		return token;
	}

	protected void addAttributesOrDeclspecs(IASTAttributeList result) throws EndOfFileException, BacktrackException {
		int lt1 = LT(1);
		do {
			// Allow empty attribute
			if (lt1 != IToken.tCOMMA) {
				result.addAttribute(singleAttribute());
			}

			// Continue on comma
			if (LT(1) != IToken.tCOMMA) {
				return;
			}
			consume();
			lt1 = LT(1);
		} while (lt1 != IToken.tRPAREN && lt1 != IToken.tEOC);
	}

	/**
	 * Parses an __declspec clause.
	 * @return the list of __declspec attributes
	 * @throws BacktrackException
	 * @throws EndOfFileException
	 */
	protected IASTAttributeList __declspec() throws BacktrackException, EndOfFileException {
		IASTAttributeList result = nodeFactory.newMSDeclspecList();
		final int startOffset = consume(IGCCToken.t__declspec).getOffset();
		if (LT(1) == IToken.tLPAREN) {
			consume();
			addAttributesOrDeclspecs(result);
			final int endOffset = consumeOrEOC(IToken.tRPAREN).getEndOffset();
			setRange(result, startOffset, endOffset);
		}
		return result;
	}

	/**
	 * Hook method to support (skip) additional declspec modifiers.
	 * @throws BacktrackException
	 * @throws EndOfFileException
	 */
	protected void handleOtherDeclSpecModifier() throws BacktrackException, EndOfFileException {
		// default action: consume keyword plus optional parenthesized "something"
		consume();

		IToken token = LA(1);

		if (token.getType() == IToken.tLPAREN) {
			consume();
			int openParen = 1;
			while (true) {
				token = LA(1);
				consume();
				if (token.getType() == IToken.tLPAREN) {
					++openParen;
				} else if (token.getType() == IToken.tRPAREN) {
					--openParen;
					if (openParen == 0) {
						break;
					}
				}
			}
		}
	}

	protected boolean canBeCompoundLiteral() throws EndOfFileException {
		IToken m = mark();
		try {
			// The parenthesis cannot be followed by a binary operator
			skipBrackets(IToken.tLPAREN, IToken.tRPAREN, IToken.tSEMI);
			return LTcatchEOF(1) == IToken.tLBRACE;
		} catch (BacktrackException bt) {
			return false;
		} finally {
			backup(m);
		}
	}

	protected boolean canBeCastExpression() throws EndOfFileException {
		IToken m = mark();
		try {
			// The parenthesis cannot be followed by a binary operator
			skipBrackets(IToken.tLPAREN, IToken.tRPAREN, IToken.tSEMI);
			switch (LTcatchEOF(1)) {
			case IToken.tAMPERASSIGN:
			case IToken.tARROW:
			case IToken.tARROWSTAR:
			case IToken.tASSIGN:
			case IToken.tBITOR:
			case IToken.tBITORASSIGN:
			case IToken.tCOLON:
			case IToken.tCOMMA:
			case IToken.tDIV:
			case IToken.tDIVASSIGN:
			case IToken.tDOT:
			case IToken.tDOTSTAR:
			case IToken.tEQUAL:
			case IToken.tGT:
			case IToken.tGT_in_SHIFTR:
			case IToken.tGTEQUAL:
			case IToken.tLBRACKET:
			case IToken.tLTEQUAL:
			case IToken.tMINUSASSIGN:
			case IToken.tMOD:
			case IToken.tMODASSIGN:
			case IToken.tNOTEQUAL:
			case IToken.tOR:
			case IToken.tPLUSASSIGN:
			case IToken.tQUESTION:
			case IToken.tRBRACE:
			case IToken.tRBRACKET:
			case IToken.tRPAREN:
			case IToken.tSEMI:
			case IToken.tSHIFTL:
			case IToken.tSHIFTLASSIGN:
			case IToken.tSHIFTR:
			case IToken.tSHIFTRASSIGN:
			case IToken.tSTARASSIGN:
				return false;
			}
			return true;
		} catch (BacktrackException bt) {
			return false;
		} finally {
			backup(m);
		}
	}

	protected boolean canBeTypeSpecifier() throws EndOfFileException {
		final int lt1 = LT(1);
		switch (lt1) {
		// simple type specifiers:
		case IToken.tIDENTIFIER:
		case IToken.tCOLONCOLON:
		case IToken.t_void:
		case IToken.t_char:
		case IToken.t_char16_t:
		case IToken.t_char32_t:
		case IToken.t_wchar_t:
		case IToken.t_bool:
		case IToken.t_short:
		case IToken.t_int:
		case IToken.t_long:
		case IToken.t_float:
		case IToken.t_double:
		case IToken.t__Bool:
		case IToken.t__Complex:
		case IToken.t__Imaginary:
		case IGCCToken.t__int128:
		case IGCCToken.t__float128:
		case IGCCToken.t_decimal32:
		case IGCCToken.t_decimal64:
		case IGCCToken.t_decimal128:
		case IToken.t_signed:
		case IToken.t_unsigned:
		case IToken.t_decltype:
		case IToken.t_auto:

			// class-specifier:
		case IToken.t_class:
		case IToken.t_struct:
		case IToken.t_union:

			// enum-specifier:
		case IToken.t_enum:

			// elaborated type specifier: (together with class, struct, union, enum)
		case IToken.t_typename:

			// cq-qualifiers
		case IToken.t_const:
		case IToken.t_volatile:
		case IToken.t_restrict:

			// gcc-special
		case IGCCToken.t_typeof:
		case IGCCToken.t__attribute__:
		case IGCCToken.tTT_underlying_type:

			// content assist
		case IToken.tCOMPLETION:
			return true;

		default:
			if (lt1 >= IExtensionToken.t__otherDeclSpecModifierFirst
					&& lt1 <= IExtensionToken.t__otherDeclSpecModifierLast)
				return true;

			return false;
		}
	}

	protected void skipBrackets(int left, int right, int terminator) throws EndOfFileException, BacktrackException {
		consume(left);
		int nesting = 0;
		int braceNesting = 0;
		while (true) {
			final int lt1 = LT(1);

			if (lt1 == IToken.tEOC)
				throwBacktrack(LA(1));

			// Ignore passages inside braces (such as for a statement-expression),
			// as they can basically contain tokens of any kind.
			if (lt1 == IToken.tLBRACE) {
				braceNesting++;
			} else if (lt1 == IToken.tRBRACE) {
				braceNesting--;
			}
			if (braceNesting > 0) {
				consume();
				continue;
			}

			if (lt1 == terminator)
				throwBacktrack(LA(1));

			consume();
			if (lt1 == left) {
				nesting++;
			} else if (lt1 == right) {
				if (--nesting < 0) {
					return;
				}
			}
		}
	}

	protected abstract IASTAlignmentSpecifier createAmbiguousAlignmentSpecifier(IASTAlignmentSpecifier expression,
			IASTAlignmentSpecifier typeId);

	protected IASTAlignmentSpecifier alignmentSpecifier() throws BacktrackException, EndOfFileException {
		int startOffset = consume(IToken.t_alignas, IToken.t__Alignas).getOffset();

		consume(IToken.tLPAREN);

		IASTTypeId typeId = null;
		IASTExpression expression = null;

		// Try parsing a type-id.
		IToken beginning = mark();
		IToken typeIdEnd = null;
		try {
			typeId = typeIdWithOptionalTrailingEllipsis(DeclarationOptions.TYPEID);
			typeIdEnd = mark();
		} catch (BacktrackException e) {
		}

		// Back up and try parsing an expression.
		backup(beginning);
		try {
			expression = expressionWithOptionalTrailingEllipsis();
		} catch (BacktrackException e) {
			// If neither parses successfully, throw.
			if (typeId == null) {
				throw e;
			}
		}

		IASTAlignmentSpecifier result;
		if (typeId == null) {
			// No type id - use the expression.
			result = nodeFactory.newAlignmentSpecifier(expression);
		} else if (expression == null) {
			// No expression - use the type id.
			backup(typeIdEnd);
			result = nodeFactory.newAlignmentSpecifier(typeId);
		} else if (expression.contains(typeId)) { // otherwise, pick the longer one
			if (typeId.contains(expression)) {
				// They are both the same length - ambiguous.
				int endOffset = consume(IToken.tRPAREN).getEndOffset();
				IASTAlignmentSpecifier expressionAlternative = nodeFactory.newAlignmentSpecifier(expression);
				IASTAlignmentSpecifier typeIdAlternative = nodeFactory.newAlignmentSpecifier(typeId);
				setRange(expressionAlternative, startOffset, endOffset);
				setRange(typeIdAlternative, startOffset, endOffset);
				return createAmbiguousAlignmentSpecifier(expressionAlternative, typeIdAlternative);
			} else {
				// Expression is longer - use it.
				result = nodeFactory.newAlignmentSpecifier(expression);
			}
		} else {
			// Type-id is longer - use it.
			backup(typeIdEnd);
			result = nodeFactory.newAlignmentSpecifier(typeId);
		}

		int endOffset = consume(IToken.tRPAREN).getEndOffset();

		setRange(result, startOffset, endOffset);
		return result;
	}
}
