/*******************************************************************************
 * Copyright (c) 2004, 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:
 *     IBM - Initial API and implementation
 *     Markus Schorn (Wind River Systems)
 *     Richard Eames
 *******************************************************************************/
package org.eclipse.cdt.core.parser.tests.scanner;

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

import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.ParserLanguage;

import junit.framework.TestSuite;

/**
 * Scanner2Tests ported to use the CPreprocessor
 */
public class PreprocessorTests extends PreprocessorTestsBase {

	public static TestSuite suite() {
		return suite(PreprocessorTests.class);
	}

	// #define f(x) x+x
	// #define obj_f f
	// #define obj_fx f x
	// #define obj_fopen f (
	// obj_f
	// (y)
	// obj_f
	// y
	// obj_fx
	// (y)
	// obj_fopen y)
	public void testParenthesisOnNextLine() throws Exception {
		initializeScanner();
		validateIdentifier("y");
		validateToken(IToken.tPLUS);
		validateIdentifier("y");

		validateIdentifier("f");
		validateIdentifier("y");

		validateIdentifier("f");
		validateIdentifier("x");
		validateToken(IToken.tLPAREN);
		validateIdentifier("y");
		validateToken(IToken.tRPAREN);

		validateIdentifier("y");
		validateToken(IToken.tPLUS);
		validateIdentifier("y");
		validateEOF();
	}

	// #define f(x) x
	// f(f(x));
	// f(f);
	// f(f)(x);
	public void testRecursiveInArgument() throws Exception {
		initializeScanner();
		validateIdentifier("x");
		validateToken(IToken.tSEMI);

		validateIdentifier("f");
		validateToken(IToken.tSEMI);

		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateIdentifier("x");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
	}

	// #define f(x) x
	// f(f(
	public void testMissingParenthesis() throws Exception {
		initializeScanner();
		validateEOF();
		validateProblemCount(1);
		validateProblem(0, IProblem.PREPROCESSOR_MISSING_RPAREN_PARMLIST, "f");
	}

	// #define b(x) ok
	// #define step1 b
	// #define step2 step1 (x)
	// step2
	public void testSpaceBeforeParenthesis() throws Exception {
		initializeScanner();
		validateIdentifier("ok");
		validateEOF();
	}

	// #define m1(x) a1
	// #define m2(x...) a2
	// m1(1,2);
	// m2(1,2);
	public void testSuperfluousComma() throws Exception {
		initializeScanner();
		validateIdentifier("a1");
		validateInteger("2");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateIdentifier("a2");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(1);
		validateProblem(0, IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, "m1");
	}

	// #define str(x,y) #x#y
	//	str(a,b );
	//	str( a,b);
	//	str(a a,b);
	//	str(a, b);
	public void testSpaceInArgs() throws Exception {
		initializeScanner();
		validateString("ab");
		validateToken(IToken.tSEMI);

		validateString("ab");
		validateToken(IToken.tSEMI);

		validateString("a ab");
		validateToken(IToken.tSEMI);

		validateString("ab");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define str(x) #x
	// #define m0( )    a0
	// #define m1(x)    str( .x. )
	// #define m2(x,y)  str( .x.y. )
	// #define open0 m0(
	// #define open1 m1(
	// #define open2 m2(
	// open0 );
	// open1 a );
	// open2 a , b c );
	public void testSpaceInArgsViaOpenMacro() throws Exception {
		initializeScanner();
		validateIdentifier("a0");
		validateToken(IToken.tSEMI);

		validateString(".a.");
		validateToken(IToken.tSEMI);

		validateString(".a.b c.");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define str(x) #x
	// #define m0( )    a0
	// #define m1(x)    str(.x.)
	// #define m2(x,y)  str(.x.y.)
	// #define _a a
	// #define _b b
	// #define _c c
	// #define use0 m0( )
	// #define use1 m1( _a )
	// #define use2 m2( _a , _b _c )
	// use0;
	// use1;
	// use2;
	public void testSpaceInArgsViaExpansion() throws Exception {
		initializeScanner();
		validateIdentifier("a0");
		validateToken(IToken.tSEMI);

		validateString(".a.");
		validateToken(IToken.tSEMI);

		validateString(".a.b c.");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define m0() a
	// m0;
	// m0();
	// m0( );
	// m0(x);
	public void testFunctionStyleWithoutArgs() throws Exception {
		initializeScanner();
		validateIdentifier("m0");
		validateToken(IToken.tSEMI);

		validateIdentifier("a");
		validateToken(IToken.tSEMI);

		validateIdentifier("a");
		validateToken(IToken.tSEMI);

		validateIdentifier("a");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(1);
		validateProblem(0, IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, "m0");
	}

	// #define tp(x,y) #x##y
	// tp(a, );
	// tp(a,_b);
	public void testStringifyAndPasteCPP() throws Exception {
		initializeScanner(getAboveComment(), ParserLanguage.CPP);
		validateString("a");
		validateToken(IToken.tSEMI);

		validateUserDefinedLiteralString("a", "_b");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define tp(x,y) #x##y
	// tp(a, );
	// tp(a,b);
	public void testStringifyAndPasteC() throws Exception {
		initializeScanner(getAboveComment(), ParserLanguage.C);
		validateString("a");
		validateToken(IToken.tSEMI);

		validateString("a");
		validateIdentifier("b");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(1);
		validateProblem(0, IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, "tp");
	}

	// #define tp(x,y) x##y
	// tp(a, b c);
	// tp(a b,c);
	public void testPasteMultipleTokens() throws Exception {
		initializeScanner();
		validateIdentifier("ab");
		validateIdentifier("c");
		validateToken(IToken.tSEMI);

		validateIdentifier("a");
		validateIdentifier("bc");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define obj a b ## c ## d e
	// obj;
	public void testObjectStyleTokenPaste() throws Exception {
		initializeScanner();
		validateIdentifier("a");
		validateIdentifier("bcd");
		validateIdentifier("e");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define variadic(x...) (a, ##x)
	// variadic();
	// variadic(b);
	// variadic(c,d);
	public void testGccVariadicMacroExtensions() throws Exception {
		initializeScanner();
		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tCOMMA);
		validateIdentifier("b");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tCOMMA);
		validateIdentifier("c");
		validateToken(IToken.tCOMMA);
		validateIdentifier("d");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define variadic(x...) (a, ##x)
	// #define _c c
	// variadic();
	// variadic(_c);
	// variadic(_c,_c);
	public void testGccVariadicMacroExtensions2() throws Exception {
		initializeScanner();
		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tCOMMA);
		validateIdentifier("c");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tCOMMA);
		validateIdentifier("c");
		validateToken(IToken.tCOMMA);
		validateIdentifier("c");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define variadic(y, x...) (a, ##x)
	// #define _c c
	// variadic(1);
	// variadic(,_c);
	// variadic(,_c,_c);
	public void testGccVariadicMacroExtensions3() throws Exception {
		initializeScanner();
		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tCOMMA);
		validateIdentifier("c");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateIdentifier("a");
		validateToken(IToken.tCOMMA);
		validateIdentifier("c");
		validateToken(IToken.tCOMMA);
		validateIdentifier("c");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define str(x) #x
	// str();
	public void testEmptyStringify() throws Exception {
		initializeScanner();
		validateString("");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define tp(x,y) x##y
	// #define _p p
	// tp(_p,);
	// tp(_p, a);
	public void testRescanAfterTokenPaste() throws Exception {
		initializeScanner();
		validateIdentifier("p");
		validateToken(IToken.tSEMI);

		validateIdentifier("_pa");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define vararg(a, ...) (__VA_ARGS__)
	// vararg();
	// vararg( );
	// vararg(a);
	// vararg(a,b);
	// vararg(a, ,c);
	public void testVaargs() throws Exception {
		initializeScanner();
		validateToken(IToken.tLPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateIdentifier("b");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateToken(IToken.tLPAREN);
		validateToken(IToken.tCOMMA);
		validateIdentifier("c");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define OBJ __VA_ARGS__
	// #define func(x) __VA_ARGS__
	// OBJ;
	// func(a);
	public void testVaargsWarning() throws Exception {
		initializeScanner();
		validateIdentifier("__VA_ARGS__");
		validateToken(IToken.tSEMI);

		validateIdentifier("__VA_ARGS__");
		validateToken(IToken.tSEMI);
		validateEOF();
		// gcc actually warns about using __VA_ARGS__ in object-style macros too.
		validateProblemCount(1);
		validateProblem(0, IProblem.PREPROCESSOR_INVALID_VA_ARGS, null);
	}

	// #define str(x) #x
	// #define _p p
	// #define obj str(_p)  // str is expanded before _p is rescanned.
	// obj;
	public void testRescanOrder() throws Exception {
		initializeScanner();
		validateString("_p");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define obj #str
	// obj;
	public void testStringifyOperatorInObject() throws Exception {
		initializeScanner();
		validateToken(IToken.tPOUND);
		validateIdentifier("str");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define str(x) #x
	// #define open_str() str(a
	// open_str()b);
	public void testOpenStringify() throws Exception {
		initializeScanner();
		validateString("ab");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define ONE(a, ...) int x
	// #define TWO(b, args...) int y
	// ONE("string");
	// TWO("string");
	public void testSkippingVarags() throws Exception {
		initializeScanner();
		validateToken(IToken.t_int);
		validateIdentifier("x");
		validateToken(IToken.tSEMI);

		validateToken(IToken.t_int);
		validateIdentifier("y");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define eval(f,x) f(x)
	// #define m(x) m[x]
	// eval(m,y);
	public void testReconsiderArgsForExpansion() throws Exception {
		initializeScanner();
		validateIdentifier("m");
		validateToken(IToken.tLBRACKET);
		validateIdentifier("y");
		validateToken(IToken.tRBRACKET);
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	//#define f\
	//(x) ok
	// f(x)
	public void testLineSpliceInMacroDefinition() throws Exception {
		initializeScanner();
		validateIdentifier("ok");
		validateEOF();
		validateProblemCount(0);
	}

	// #define f() fval
	// #define nospace f()f()
	// #define space f() f()
	// #define str(x) #x
	// #define xstr(x) str(x)
	// #define tp1(x,y,z) [x ## y ## z]
	// #define tp2(x,y,z) [ x ## y ## z ]
	// #define tstr1(x,y) [#x#y]
	// #define tstr2(x,y) [ #x #y ]
	// #define spaceBeforeStr(x) a #x b
	// xstr(nospace);
	// xstr(space);
	// xstr(tp1(a b, c d , e f));
	// xstr(tp2(a b, c d , e f));
	// xstr(tp1(a-b, c-d , e-f));
	// xstr(tp2(a-b, c-d , e-f));
	// xstr(tstr1(a b, c d));
	// xstr(tstr2(a b, c d));
	// xstr(spaceBeforeStr(c));
	public void testSpaceInStringify() throws Exception {
		initializeScanner();
		validateString("fvalfval");
		validateToken(IToken.tSEMI);

		validateString("fval fval");
		validateToken(IToken.tSEMI);

		validateString("[a bc de f]");
		validateToken(IToken.tSEMI);

		validateString("[ a bc de f ]");
		validateToken(IToken.tSEMI);

		validateString("[a-bc-de-f]");
		validateToken(IToken.tSEMI);

		validateString("[ a-bc-de-f ]");
		validateToken(IToken.tSEMI);

		validateString("[\\\"a b\\\"\\\"c d\\\"]");
		validateToken(IToken.tSEMI);

		validateString("[ \\\"a b\\\" \\\"c d\\\" ]");
		validateToken(IToken.tSEMI);

		validateString("a \\\"c\\\" b");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define empty
	// #define paste1(y) x##y z
	// #define paste2(x) x##empty z
	// paste1();
	// paste1(empty);
	// paste2();
	// paste2(empty);
	// paste2(a);
	public void testTokenPasteWithEmptyParam() throws Exception {
		initializeScanner();
		validateIdentifier("x");
		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateIdentifier("xempty");
		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateIdentifier("emptyempty");
		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateIdentifier("aempty");
		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define empty
	// #define paste1(y) x##y z
	// #define paste2(x) x##empty z
	// paste1();
	// paste1(empty);
	// paste2();
	// paste2(empty);
	// paste2(a);
	public void testSpacesBeforeStringify() throws Exception {
		initializeScanner();
		validateIdentifier("x");
		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateIdentifier("xempty");
		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateIdentifier("emptyempty");
		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateIdentifier("aempty");
		validateIdentifier("z");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define paste(x,y,z) x##y##z
	// paste(a,b,c);
	// paste(1,2,3);
	public void testTokenPasteChain() throws Exception {
		initializeScanner();
		validateIdentifier("abc");
		validateToken(IToken.tSEMI);

		validateInteger("123");
		validateToken(IToken.tSEMI);

		validateEOF();
		validateProblemCount(0);
	}

	// #define A(x,y,z) x + y + z
	// #define _t t
	// A ( _t , , _t )
	public void testEmptyToken() throws Exception {
		initializeScanner();
		validateIdentifier("t");
		validateToken(IToken.tPLUS);
		validateToken(IToken.tPLUS);
		validateIdentifier("t");
	}

	// #define FOO 5
	// # define BAR 10
	// int x = FOO + BAR;
	public void testSimpleObjectLike1() throws Exception {
		initializeScanner();
		validateToken(IToken.t_int);
		validateIdentifier("x");
		validateToken(IToken.tASSIGN);
		validateInteger("5");
		validateToken(IToken.tPLUS);
		validateInteger("10");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define FOO BAR
	// # define BAR 10
	// int x = BAR;
	public void testSimpleObjectLike2() throws Exception {
		initializeScanner();
		validateToken(IToken.t_int);
		validateIdentifier("x");
		validateToken(IToken.tASSIGN);
		validateInteger("10");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define MAX(a, b) (a) > (b) ? (a) : (b)
	// int max = MAX(x, y);
	public void testSimpleFunctionLike1() throws Exception {
		// int max = (x) > (y) ? (x) : (y);
		initializeScanner();

		validateToken(IToken.t_int);
		validateIdentifier("max");
		validateToken(IToken.tASSIGN);
		validateToken(IToken.tLPAREN);
		validateIdentifier("x");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tGT);
		validateToken(IToken.tLPAREN);
		validateIdentifier("y");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tQUESTION);
		validateToken(IToken.tLPAREN);
		validateIdentifier("x");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tCOLON);
		validateToken(IToken.tLPAREN);
		validateIdentifier("y");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define ADD(a, b) (a) + (b)
	// #define ADDPART(a) ADD(a
	// int sum = ADDPART (x) , y);
	public void testSimpleFunctionLike2() throws Exception {
		// int sum = (x) + (y) ;
		initializeScanner();

		validateToken(IToken.t_int);
		validateIdentifier("sum");
		validateToken(IToken.tASSIGN);
		validateToken(IToken.tLPAREN);
		validateIdentifier("x");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tPLUS);
		validateToken(IToken.tLPAREN);
		validateIdentifier("y");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define ADD(a, b) (a) + (b)
	// int sum = ADD(x+1,y+1);
	public void testSimpleFunctionLike3() throws Exception {
		// int sum = (x+1) + (y+1) ;
		initializeScanner();

		validateToken(IToken.t_int);
		validateIdentifier("sum");
		validateToken(IToken.tASSIGN);
		validateToken(IToken.tLPAREN);
		validateIdentifier("x");
		validateToken(IToken.tPLUS);
		validateInteger("1");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tPLUS);
		validateToken(IToken.tLPAREN);
		validateIdentifier("y");
		validateToken(IToken.tPLUS);
		validateInteger("1");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define ADD(a, b) (a) + (b)
	// int sum = ADD(f(x,y),z+1);
	public void testSimpleFunctionLike4() throws Exception {
		// int sum = (f(x,y)) + (z+1) ;
		initializeScanner();

		validateToken(IToken.t_int);
		validateIdentifier("sum");
		validateToken(IToken.tASSIGN);
		validateToken(IToken.tLPAREN);
		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateIdentifier("x");
		validateToken(IToken.tCOMMA);
		validateIdentifier("y");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tPLUS);
		validateToken(IToken.tLPAREN);
		validateIdentifier("z");
		validateToken(IToken.tPLUS);
		validateInteger("1");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define hash_hash # ## #
	// #define mkstr(a) # a
	// #define in_between(a) mkstr(a)
	// #define join(c, d) in_between(c hash_hash d)
	// char p[] = join(x, y);
	public void testSpecHashHashExample() throws Exception {
		// char p[] = "x ## y" ;
		initializeScanner();

		validateToken(IToken.t_char);
		validateIdentifier("p");
		validateToken(IToken.tLBRACKET);
		validateToken(IToken.tRBRACKET);
		validateToken(IToken.tASSIGN);
		validateString("x ## y");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	private static StringBuffer getExample3Defines() {
		return new StringBuffer().append("#define x 3 \n").append("#define f(a) f(x * (a)) \n").append("#undef x \n")
				.append("#define x 2 \n").append("#define g f \n").append("#define z z[0] \n")
				.append("#define h g(~ \n").append("#define m(a) a(w) \n").append("#define w 0,1 \n")
				.append("#define t(a) a \n").append("#define p() int \n").append("#define q(x) x \n")
				.append("#define r(x,y) x ## y \n").append("#define str(x) # x \n");
	}

	public void testSpecExample3_1() throws Exception {
		StringBuffer sb = getExample3Defines();
		sb.append("f(y+1) + f(f(z)) % t(t(g)(0) + t)(1); \n");

		// f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
		initializeScanner(sb.toString());

		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateInteger("2");
		validateToken(IToken.tSTAR);
		validateToken(IToken.tLPAREN);
		validateIdentifier("y");
		validateToken(IToken.tPLUS);
		validateInteger("1");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tPLUS);
		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateInteger("2");
		validateToken(IToken.tSTAR);
		validateToken(IToken.tLPAREN);
		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateInteger("2");
		validateToken(IToken.tSTAR);
		validateToken(IToken.tLPAREN);
		validateIdentifier("z");
		validateToken(IToken.tLBRACKET);
		validateInteger("0");
		validateToken(IToken.tRBRACKET);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tMOD);
		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateInteger("2");
		validateToken(IToken.tSTAR);
		validateToken(IToken.tLPAREN);
		validateInteger("0");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tPLUS);
		validateIdentifier("t");
		validateToken(IToken.tLPAREN);
		validateInteger("1");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample3_2() throws Exception {
		StringBuffer sb = getExample3Defines();
		sb.append("g(x+(3,4)-w) | h 5) & m (f)^m(m); \n");

		// f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1); //47
		initializeScanner(sb.toString());

		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateInteger("2");
		validateToken(IToken.tSTAR);
		validateToken(IToken.tLPAREN);
		validateInteger("2");
		validateToken(IToken.tPLUS);
		validateToken(IToken.tLPAREN);
		validateInteger("3");
		validateToken(IToken.tCOMMA);
		validateInteger("4");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tMINUS);
		validateInteger("0");
		validateToken(IToken.tCOMMA);
		validateInteger("1");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tBITOR);
		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateInteger("2");
		validateToken(IToken.tSTAR);
		validateToken(IToken.tLPAREN);
		validateToken(IToken.tBITCOMPLEMENT);
		validateInteger("5");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tAMPER);
		validateIdentifier("f");
		validateToken(IToken.tLPAREN);
		validateInteger("2");
		validateToken(IToken.tSTAR);
		validateToken(IToken.tLPAREN);
		validateInteger("0");
		validateToken(IToken.tCOMMA);
		validateInteger("1");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tXOR);
		validateIdentifier("m");
		validateToken(IToken.tLPAREN);
		validateInteger("0");
		validateToken(IToken.tCOMMA);
		validateInteger("1");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample3_3() throws Exception {
		StringBuffer sb = getExample3Defines();
		sb.append("p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; \n");

		// int i[] = { 1, 23, 4, 5, };
		initializeScanner(sb.toString());

		validateToken(IToken.t_int);
		validateIdentifier("i");
		validateToken(IToken.tLBRACKET);
		validateToken(IToken.tRBRACKET);
		validateToken(IToken.tASSIGN);
		validateToken(IToken.tLBRACE);
		validateInteger("1");
		validateToken(IToken.tCOMMA);
		validateInteger("23");
		validateToken(IToken.tCOMMA);
		validateInteger("4");
		validateToken(IToken.tCOMMA);
		validateInteger("5");
		validateToken(IToken.tCOMMA);
		validateToken(IToken.tRBRACE);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample3_4() throws Exception {
		StringBuffer sb = getExample3Defines();
		sb.append("char c[2][6] = { str(hello), str() }; \n"); //31

		// char c[2][6] = { "hello", "" }; //15
		initializeScanner(sb.toString());

		validateToken(IToken.t_char);
		validateIdentifier("c");
		validateToken(IToken.tLBRACKET);
		validateInteger("2");
		validateToken(IToken.tRBRACKET);
		validateToken(IToken.tLBRACKET);
		validateInteger("6");
		validateToken(IToken.tRBRACKET);
		validateToken(IToken.tASSIGN);
		validateToken(IToken.tLBRACE);
		validateString("hello");
		validateToken(IToken.tCOMMA);
		validateString("");
		validateToken(IToken.tRBRACE);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	private static StringBuffer getExample4Defines() {
		return new StringBuffer().append("#define str(s) # s \n").append("#define xstr(s) str(s) \n")
				.append("#define debug(s, t) printf(\"x\" # s \"= %d, x\" # t \"= %s\", \\ \n")
				.append("x ## s, x ## t) \n").append("#define INCFILE(n) vers ## n \n")
				.append("#define glue(a, b) a ## b \n").append("#define xglue(a, b) glue(a, b) \n")
				.append("#define HIGHLOW \"hello\" \n").append("#define LOW LOW \", world\" \n");
	}

	public void testSpecExample4_1() throws Exception {
		StringBuffer sb = getExample4Defines();
		sb.append("debug(1, 2); \n"); //31

		// printf("x1= %d, x2= %s", x1, x2); // 9
		initializeScanner(sb.toString());

		validateIdentifier("printf");
		validateToken(IToken.tLPAREN);
		validateString("x1= %d, x2= %s");
		validateToken(IToken.tCOMMA);
		validateIdentifier("x1");
		validateToken(IToken.tCOMMA);
		validateIdentifier("x2");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample4_2() throws Exception {
		StringBuffer sb = getExample4Defines();
		sb.append("fputs(str(strncmp(\"abc\\0d\", \"abc\", '\\4') // this goes away   \n");
		sb.append("== 0) str(: @\\n), s); \n");

		// fputs( "strncmp(\"abc\\0d\", \"abc\", '\\4') == 0: @\n", s); // 7
		initializeScanner(sb.toString());

		validateIdentifier("fputs");
		validateToken(IToken.tLPAREN);
		validateString("strncmp(\\\"abc\\\\0d\\\", \\\"abc\\\", '\\\\4') == 0: @\\n");
		validateToken(IToken.tCOMMA);
		validateIdentifier("s");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample4_3() throws Exception {
		StringBuffer sb = getExample4Defines();
		sb.append("xglue(HIGH, LOW) \n");

		// "hello, world"
		initializeScanner(sb.toString());

		validateString("hello, world");
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample4_4() throws Exception {
		StringBuffer sb = getExample4Defines();
		sb.append("glue(HIGH, LOW); \n");

		// "hello";
		initializeScanner(sb.toString());

		validateString("hello");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define t(x,y,z) x ## y ## z
	// int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), t(10,,), t(,11,), t(,,12), t(,,) };
	public void testSpecExample5() throws Exception {
		// int j[] = {123, 45, 67, 89, 10, 11, 12, };
		initializeScanner();

		validateToken(IToken.t_int);
		validateIdentifier("j");
		validateToken(IToken.tLBRACKET);
		validateToken(IToken.tRBRACKET);
		validateToken(IToken.tASSIGN);
		validateToken(IToken.tLBRACE);
		validateInteger("123");
		validateToken(IToken.tCOMMA);
		validateInteger("45");
		validateToken(IToken.tCOMMA);
		validateInteger("67");
		validateToken(IToken.tCOMMA);
		validateInteger("89");
		validateToken(IToken.tCOMMA);
		validateInteger("10");
		validateToken(IToken.tCOMMA);
		validateInteger("11");
		validateToken(IToken.tCOMMA);
		validateInteger("12");
		validateToken(IToken.tCOMMA);
		validateToken(IToken.tRBRACE);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public StringBuffer getExample7Defines() {
		return new StringBuffer().append("#define debug(...) fprintf(stderr, __VA_ARGS__) \n ")
				.append("#define showlist(...) puts(#__VA_ARGS__)\n ")
				.append("#define report(test, ...) ((test)?puts(#test):\\ \n ").append("printf(__VA_ARGS__))  \n ");
	}

	public void testSpecExample7_1() throws Exception {
		StringBuffer sb = getExample7Defines();
		sb.append("debug(\"Flag\"); \n");

		// fprintf(stderr, "Flag" ); //7
		initializeScanner(sb.toString());

		validateIdentifier("fprintf");
		validateToken(IToken.tLPAREN);
		validateIdentifier("stderr");
		validateToken(IToken.tCOMMA);
		validateString("Flag");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample7_2() throws Exception {
		StringBuffer sb = getExample7Defines();
		sb.append("debug(\"X = %d\\n\", x); \n");

		// fprintf(stderr, "X = %d\n", x ); //9
		initializeScanner(sb.toString());

		validateIdentifier("fprintf");
		validateToken(IToken.tLPAREN);
		validateIdentifier("stderr");
		validateToken(IToken.tCOMMA);
		validateString("X = %d\\n");
		validateToken(IToken.tCOMMA);
		validateIdentifier("x");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample7_3() throws Exception {
		StringBuffer sb = getExample7Defines();
		sb.append("showlist(The first, second, and third items.); \n");

		// puts( "The first, second, and third items." ); //5
		initializeScanner(sb.toString());

		validateIdentifier("puts");
		validateToken(IToken.tLPAREN);
		validateString("The first, second, and third items.");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testSpecExample7_4() throws Exception {
		StringBuffer sb = getExample7Defines();
		sb.append("report(x>y, \"x is %d but y is %d\", x, y); \n");

		// ( (x>y) ? puts("x>y") : printf("x is %d but y is %d", x, y) ); //22
		initializeScanner(sb.toString());

		validateToken(IToken.tLPAREN);
		validateToken(IToken.tLPAREN);
		validateIdentifier("x");
		validateToken(IToken.tGT);
		validateIdentifier("y");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tQUESTION);
		validateIdentifier("puts");
		validateToken(IToken.tLPAREN);
		validateString("x>y");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tCOLON);
		validateIdentifier("printf");
		validateToken(IToken.tLPAREN);
		validateString("x is %d but y is %d");
		validateToken(IToken.tCOMMA);
		validateIdentifier("x");
		validateToken(IToken.tCOMMA);
		validateIdentifier("y");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	// #define foo g g g
	// #define g f##oo
	// foo
	public void testRecursiveExpansion() throws Exception {
		initializeScanner();

		validateIdentifier("foo");
		validateIdentifier("foo");
		validateIdentifier("foo");
		validateEOF();
		validateProblemCount(0);
	}

	// #define m !(m)+n
	// #define n(n) n(m)
	// m(m)
	public void testRecursiveExpansion2() throws Exception {
		// !(m)+ !(m)+n(!(m)+n)
		initializeScanner();

		validateToken(IToken.tNOT);
		validateToken(IToken.tLPAREN);
		validateIdentifier("m");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tPLUS);
		validateToken(IToken.tNOT);
		validateToken(IToken.tLPAREN);
		validateIdentifier("m");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tPLUS);
		validateIdentifier("n");
		validateToken(IToken.tLPAREN);
		validateToken(IToken.tNOT);
		validateToken(IToken.tLPAREN);
		validateIdentifier("m");
		validateToken(IToken.tRPAREN);
		validateToken(IToken.tPLUS);
		validateIdentifier("n");
		validateToken(IToken.tRPAREN);
		validateEOF();
		validateProblemCount(0);
	}

	// #define f g
	// #define cat(a,b) a ## b
	// #define g bad
	// cat(f, f)
	public void testRecursiveExpansion3() throws Exception {
		// ff
		initializeScanner();

		validateIdentifier("ff");
		validateEOF();
		validateProblemCount(0);
	}

	//	f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
	//	f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);
	//	int i[] = { 1, 23, 4, 5, };
	//	char c[2][6] = { "hello", "" };
	//  --
	//	#define x 3
	//	#define f(a) f(x * (a))
	//	#undef x
	//	#define x 2
	//	#define g f
	//	#define z z[0]
	//	#define h g(~
	//	#define m(a) a(w)
	//	#define w 0,1
	//	#define t(a) a
	//	#define p() int
	//	#define q(x) x
	//	#define r(x,y) x ## y
	//	#define str(x) # x
	//
	//	f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
	//	g(x+(3,4)-w) | h 5) & m
	//	(f)^m(m);
	//	p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) };
	//	char c[2][6] = { str(hello), str() };
	public void testC99_6_7_5_3_5_Bug104869() throws Exception {
		initializeScanner();
		// read in expected tokens
		List<IToken> expect = new ArrayList<>();
		IToken t = fScanner.nextToken();
		while (t.getType() != IToken.tDECR) {
			expect.add(t);
			t = fScanner.nextToken();
		}

		for (IToken et : expect) {
			t = fScanner.nextToken();
			assertEquals(et.getImage(), t.getImage());
			assertEquals(et.getType(), t.getType());
		}
		validateEOF();
		validateProblemCount(0);
	}

	//	#define hash_hash # ## #
	//	#define mkstr(a) # a
	//	#define in_between(a) mkstr(a)
	//	#define join(c, d) in_between(c hash_hash d)
	//  join(x, y)
	public void testC99_6_10_3_3_4_Bug84268() throws Exception {
		initializeScanner();
		validateString("x ## y");
		validateEOF();
		validateProblemCount(0);
	}

	// #define BIN 0b10101010
	// #define HEX 0xAA
	// #define OCT 0252
	// #define DEC 170
	// #if (BIN == HEX && HEX == OCT && OCT == DEC)
	// int foo = BIN;
	// #endif
	public void testGCC43BinaryNumbers() throws Exception {
		initializeScanner();
		validateToken(IToken.t_int);
		validateIdentifier("foo");
		validateToken(IToken.tASSIGN);
		validateInteger("0b10101010");
		validateToken(IToken.tSEMI);
		validateEOF();
		validateProblemCount(0);
	}

	public void testBadBinaryNumbersC() throws Exception {
		String badbinary = "{0b012, 0b01b, 0b1111e01, 0b1111p10, 0b10010.10010}";
		initializeScanner(badbinary, ParserLanguage.C);
		fullyTokenize();
		validateProblemCount(5);
		validateProblem(0, IProblem.SCANNER_BAD_BINARY_FORMAT, null);
		validateProblem(1, IProblem.SCANNER_CONSTANT_WITH_BAD_SUFFIX, "b");
		validateProblem(2, IProblem.SCANNER_FLOAT_WITH_BAD_PREFIX, "0b");
		validateProblem(3, IProblem.SCANNER_CONSTANT_WITH_BAD_SUFFIX, "p10");
		validateProblem(4, IProblem.SCANNER_FLOAT_WITH_BAD_PREFIX, "0b");
	}

	public void testBadBinaryNumbersCPP() throws Exception {
		// First, third, and fifth are invalid in c++11.
		String badbinary = "{0b012, 0b01b, 0b1111e01, 0b1111p10, 0b10010.10010}";
		initializeScanner(badbinary);
		fullyTokenize();
		validateProblemCount(3);
		validateProblem(0, IProblem.SCANNER_BAD_BINARY_FORMAT, null);
		validateProblem(1, IProblem.SCANNER_FLOAT_WITH_BAD_PREFIX, "0b");
		validateProblem(2, IProblem.SCANNER_FLOAT_WITH_BAD_PREFIX, "0b");
	}

	// #if 123ASDF
	// #endif
	// #if 0xU
	// #endif
	public void testUDLInPP() throws Exception {
		initializeScanner();
		validateEOF();
		validateProblemCount(2);
		validateProblem(0, IProblem.SCANNER_CONSTANT_WITH_BAD_SUFFIX, "ASDF");
		validateProblem(1, IProblem.SCANNER_CONSTANT_WITH_BAD_SUFFIX, "xU");
	}
}
