function tokenize(fnResolveBinding, sInput, iStart) {
		var aParts = [], // the resulting parts (corresponds to aPrimitiveValueBindings)
			aPrimitiveValueBindings = [], // the bindings with primitive values only
			aTokens = [],
			oTokenizer = new JSTokenizer();

		/**
		 * Saves the binding as a part. Reuses an existing part if the binding is identical.
		 * @param {object} oBinding
		 *   the binding to save
		 * @param {int} iStart
		 *   the binding's start index in the input string
		 * @param {boolean} [bTargetTypeAny=false]
		 *   whether the binding's "targetType" should default to "any" (recursively, for all parts)
		 * @returns {int}
		 *   the index at which it has been saved/found in aParts
		 */
		function saveBindingAsPart(oBinding, iStart, bTargetTypeAny) {
			var bHasNonPrimitiveValue = false,
				sKey,
				oPrimitiveValueBinding,
				i;

			/*
			 * Sets the target type of the given binding to the default "any", if applicable.
			 *
			 * @param {object} oBinding
			 *   A binding
			 */
			function setTargetType(oBinding) {
				if (bTargetTypeAny) {
					if (oBinding.parts) {
						oBinding.parts.forEach(setTargetType);
						// Note: targetType not allowed here, see BindingParser.mergeParts
					} else {
						oBinding.targetType = oBinding.targetType || "any";
					}
				}
			}

			for (sKey in oBinding) {
				switch (typeof oBinding[sKey]) {
					case "boolean":
					case "number":
					case "string":
					case "undefined":
						break;
					default:
						// binding has at least one property of non-primitive value
						bHasNonPrimitiveValue = true;
				}
			}
			setTargetType(oBinding);
			if (bHasNonPrimitiveValue) {
				// the binding must be a complex binding; property "type" (and poss. others) are
				// newly created objects and thus incomparable -> parse again to have the names
				oPrimitiveValueBinding = JSTokenizer.parseJS(sInput, iStart).result;
				setTargetType(oPrimitiveValueBinding);
			} else {
				// only primitive values; easily comparable
				oPrimitiveValueBinding = oBinding;
			}
			for (i = 0; i < aParts.length; i += 1) {
				// Note: order of top-level properties must not matter for equality!
				if (deepEqual(aPrimitiveValueBindings[i], oPrimitiveValueBinding)) {
					return i;
				}
			}
			aPrimitiveValueBindings[i] = oPrimitiveValueBinding;
			aParts[i] = oBinding;
			return i;
		}

		/**
		 * Consumes the next token in the input string and pushes it to the array of tokens.
		 *
		 * @returns {boolean} whether a token is recognized
		 * @throws {Error|Object|SyntaxError}
		 *   <code>fnResolveBinding</code> may throw <code>SyntaxError</code>;
		 *   <code>oTokenizer.setIndex()</code> may throw <code>Error</code>;
		 *   <code>oTokenizer</code> may also throw <code>{name: 'SyntaxError', ...}</code>
		 */
		function consumeToken() {
			var ch, oBinding, iIndex, aMatches, oToken;

			oTokenizer.white();
			ch = oTokenizer.getCh();
			iIndex = oTokenizer.getIndex();

			if ((ch === "$" || ch === "%") && sInput[iIndex + 1] === "{") { //binding
				oBinding = fnResolveBinding(sInput, iIndex + 1);
				oToken = {
					id: "BINDING",
					value: saveBindingAsPart(oBinding.result, iIndex + 1, ch === "%")
				};
				oTokenizer.setIndex(oBinding.at); //go to first character after binding string
			} else if (rIdentifierStart.test(ch)) {
				aMatches = rIdentifier.exec(sInput.slice(iIndex));
				switch (aMatches[0]) {
				case "false":
				case "null":
				case "true":
					oToken = {id: "CONSTANT", value: oTokenizer.word()};
					break;
				case "in":
				case "typeof":
					oToken = {id: aMatches[0]};
					oTokenizer.setIndex(iIndex + aMatches[0].length);
					break;
				default:
					oToken = {id: "IDENTIFIER", value: aMatches[0]};
					oTokenizer.setIndex(iIndex + aMatches[0].length);
				}
			} else if (rDigit.test(ch)
					|| ch === "." && rDigit.test(sInput[iIndex + 1])) {
				oToken = {id: "CONSTANT", value: oTokenizer.number()};
			} else if (ch === "'" || ch === '"') {
				oToken = {id: "CONSTANT", value: oTokenizer.string()};
			} else {
				rTokens.lastIndex = iIndex;
				aMatches = rTokens.exec(sInput);
				if (!aMatches || aMatches.index !== iIndex) {
					return false; // end of input or unrecognized character
				}
				oToken = {id: aMatches[0]};
				oTokenizer.setIndex(iIndex + aMatches[0].length);
			}
			oToken.input = sInput;
			oToken.start = iIndex;
			oToken.end = oTokenizer.getIndex();
			aTokens.push(oToken);
			return true;
		}

		oTokenizer.init(sInput, iStart);

		try {
			/* eslint-disable no-empty */
			while (consumeToken()) { /* deliberately empty */ }
			/* eslint-enable no-empty */
		} catch (e) {
			// Note: new SyntaxError().name === "SyntaxError"
			if (e.name === "SyntaxError") { // remember tokenizer error
				aTokens.push({
					id: "ERROR",
					value: e
				});
			} else {
				throw e;
			}
		}

		return {
			at: oTokenizer.getIndex(),
			parts: aParts,
			tokens: aTokens
		};
	}
/**
	 * Computes the tokens according to the expression grammar in sInput starting at iStart and
	 * uses fnResolveBinding to resolve bindings embedded in the expression.
	 * @param {function} fnResolveBinding - the function to resolve embedded bindings
	 * @param {string} sInput - the string to be parsed
	 * @param {int} [iStart=0] - the index to start parsing
	 * @returns {object} Tokenization result object with the following properties
	 *   at: the index after the last character consumed by the tokenizer in the input string
	 *   parts: array with parts corresponding to resolved embedded bindings
	 *   tokens: the array of tokens where each token is a tuple of ID, optional value, and
	 *   optional source text
	 */
Computes the tokens according to the expression grammar in sInput starting at iStart and
uses fnResolveBinding to resolve bindings embedded in the expression.
@param {function} fnResolveBinding - the function to resolve embedded bindings
@param {string} sInput - the string to be parsed
@param {int} [iStart=0] - the index to start parsing
@returns {object} Tokenization result object with the following properties
at: the index after the last character consumed by the tokenizer in the input string
parts: array with parts corresponding to resolved embedded bindings
tokens: the array of tokens where each token is a tuple of ID, optional value, and
optional source text
Computes the tokens according to the expression grammar in sInput starting at iStart and uses fnResolveBinding to resolve bindings embedded in the expression . @param { function } fnResolveBinding - the function to resolve embedded bindings @param { string } sInput - the string to be parsed @param { int } [ iStart = 0 ] - the index to start parsing @returns { object } Tokenization result object with the following properties at : the index after the last character consumed by the tokenizer in the input string parts : array with parts corresponding to resolved embedded bindings tokens : the array of tokens where each token is a tuple of ID optional value and optional source text
function tokenize(fnResolveBinding, sInput, iStart) {
		var aParts = [], 
			aPrimitiveValueBindings = [], 
			aTokens = [],
			oTokenizer = new JSTokenizer();
		function saveBindingAsPart(oBinding, iStart, bTargetTypeAny) {
			var bHasNonPrimitiveValue = false,
				sKey,
				oPrimitiveValueBinding,
				i;
			function setTargetType(oBinding) {
				if (bTargetTypeAny) {
					if (oBinding.parts) {
						oBinding.parts.forEach(setTargetType);
					} else {
						oBinding.targetType = oBinding.targetType || "any";
					}
				}
			}
			for (sKey in oBinding) {
				switch (typeof oBinding[sKey]) {
					case "boolean":
					case "number":
					case "string":
					case "undefined":
						break;
					default:
						bHasNonPrimitiveValue = true;
				}
			}
			setTargetType(oBinding);
			if (bHasNonPrimitiveValue) {
				oPrimitiveValueBinding = JSTokenizer.parseJS(sInput, iStart).result;
				setTargetType(oPrimitiveValueBinding);
			} else {
				oPrimitiveValueBinding = oBinding;
			}
			for (i = 0; i < aParts.length; i += 1) {
				if (deepEqual(aPrimitiveValueBindings[i], oPrimitiveValueBinding)) {
					return i;
				}
			}
			aPrimitiveValueBindings[i] = oPrimitiveValueBinding;
			aParts[i] = oBinding;
			return i;
		}
		function consumeToken() {
			var ch, oBinding, iIndex, aMatches, oToken;
			oTokenizer.white();
			ch = oTokenizer.getCh();
			iIndex = oTokenizer.getIndex();
			if ((ch === "$" || ch === "%") && sInput[iIndex + 1] === "{") { 
				oBinding = fnResolveBinding(sInput, iIndex + 1);
				oToken = {
					id: "BINDING",
					value: saveBindingAsPart(oBinding.result, iIndex + 1, ch === "%")
				};
				oTokenizer.setIndex(oBinding.at); 
			} else if (rIdentifierStart.test(ch)) {
				aMatches = rIdentifier.exec(sInput.slice(iIndex));
				switch (aMatches[0]) {
				case "false":
				case "null":
				case "true":
					oToken = {id: "CONSTANT", value: oTokenizer.word()};
					break;
				case "in":
				case "typeof":
					oToken = {id: aMatches[0]};
					oTokenizer.setIndex(iIndex + aMatches[0].length);
					break;
				default:
					oToken = {id: "IDENTIFIER", value: aMatches[0]};
					oTokenizer.setIndex(iIndex + aMatches[0].length);
				}
			} else if (rDigit.test(ch)
					|| ch === "." && rDigit.test(sInput[iIndex + 1])) {
				oToken = {id: "CONSTANT", value: oTokenizer.number()};
			} else if (ch === "'" || ch === '"') {
				oToken = {id: "CONSTANT", value: oTokenizer.string()};
			} else {
				rTokens.lastIndex = iIndex;
				aMatches = rTokens.exec(sInput);
				if (!aMatches || aMatches.index !== iIndex) {
					return false; 
				}
				oToken = {id: aMatches[0]};
				oTokenizer.setIndex(iIndex + aMatches[0].length);
			}
			oToken.input = sInput;
			oToken.start = iIndex;
			oToken.end = oTokenizer.getIndex();
			aTokens.push(oToken);
			return true;
		}
		oTokenizer.init(sInput, iStart);
		try {
			while (consumeToken()) {  }
		} catch (e) {
			if (e.name === "SyntaxError") { 
				aTokens.push({
					id: "ERROR",
					value: e
				});
			} else {
				throw e;
			}
		}
		return {
			at: oTokenizer.getIndex(),
			parts: aParts,
			tokens: aTokens
		};
	}
function tokenize ( fnResolveBinding , sInput , iStart ) { var aParts = [ ] , aPrimitiveValueBindings = [ ] , aTokens = [ ] , oTokenizer = new JSTokenizer ( ) ; function saveBindingAsPart ( oBinding , iStart , bTargetTypeAny ) { var bHasNonPrimitiveValue = false , sKey , oPrimitiveValueBinding , i ; function setTargetType ( oBinding ) { if ( bTargetTypeAny ) { if ( oBinding . parts ) { oBinding . parts . forEach ( setTargetType ) ; } else { oBinding . targetType = oBinding . targetType || "any" ; } } } for ( sKey in oBinding ) { switch ( typeof oBinding [ sKey ] ) { case "boolean" : case "number" : case "string" : case "undefined" : break ; default : bHasNonPrimitiveValue = true ; } } setTargetType ( oBinding ) ; if ( bHasNonPrimitiveValue ) { oPrimitiveValueBinding = JSTokenizer . parseJS ( sInput , iStart ) . result ; setTargetType ( oPrimitiveValueBinding ) ; } else { oPrimitiveValueBinding = oBinding ; } for ( i = 0 ; i < aParts . length ; i += 1 ) { if ( deepEqual ( aPrimitiveValueBindings [ i ] , oPrimitiveValueBinding ) ) { return i ; } } aPrimitiveValueBindings [ i ] = oPrimitiveValueBinding ; aParts [ i ] = oBinding ; return i ; } function consumeToken ( ) { var ch , oBinding , iIndex , aMatches , oToken ; oTokenizer . white ( ) ; ch = oTokenizer . getCh ( ) ; iIndex = oTokenizer . getIndex ( ) ; if ( ( ch === "$" || ch === "%" ) && sInput [ iIndex + 1 ] === "{" ) { oBinding = fnResolveBinding ( sInput , iIndex + 1 ) ; oToken = { id : "BINDING" , value : saveBindingAsPart ( oBinding . result , iIndex + 1 , ch === "%" ) } ; oTokenizer . setIndex ( oBinding . at ) ; } else if ( rIdentifierStart . test ( ch ) ) { aMatches = rIdentifier . exec ( sInput . slice ( iIndex ) ) ; switch ( aMatches [ 0 ] ) { case "false" : case "null" : case "true" : oToken = { id : "CONSTANT" , value : oTokenizer . word ( ) } ; break ; case "in" : case "typeof" : oToken = { id : aMatches [ 0 ] } ; oTokenizer . setIndex ( iIndex + aMatches [ 0 ] . length ) ; break ; default : oToken = { id : "IDENTIFIER" , value : aMatches [ 0 ] } ; oTokenizer . setIndex ( iIndex + aMatches [ 0 ] . length ) ; } } else if ( rDigit . test ( ch ) || ch === "." && rDigit . test ( sInput [ iIndex + 1 ] ) ) { oToken = { id : "CONSTANT" , value : oTokenizer . number ( ) } ; } else if ( ch === "'" || ch === '"' ) { oToken = { id : "CONSTANT" , value : oTokenizer . string ( ) } ; } else { rTokens . lastIndex = iIndex ; aMatches = rTokens . exec ( sInput ) ; if ( ! aMatches || aMatches . index !== iIndex ) { return false ; } oToken = { id : aMatches [ 0 ] } ; oTokenizer . setIndex ( iIndex + aMatches [ 0 ] . length ) ; } oToken . input = sInput ; oToken . start = iIndex ; oToken . end = oTokenizer . getIndex ( ) ; aTokens . push ( oToken ) ; return true ; } oTokenizer . init ( sInput , iStart ) ; try { while ( consumeToken ( ) ) { } } catch ( e ) { if ( e . name === "SyntaxError" ) { aTokens . push ( { id : "ERROR" , value : e } ) ; } else { throw e ; } } return { at : oTokenizer . getIndex ( ) , parts : aParts , tokens : aTokens } ; }
Computes the tokens according to the expression grammar in sInput starting at iStart and
 uses fnResolveBinding to resolve bindings embedded in the expression.
Computes the tokens according to the expression grammar in sInput starting at iStart and uses fnResolveBinding to resolve bindings embedded in the expression .
// the resulting parts (corresponds to aPrimitiveValueBindings) // the bindings with primitive values only /**
		 * Saves the binding as a part. Reuses an existing part if the binding is identical.
		 * @param {object} oBinding
		 *   the binding to save
		 * @param {int} iStart
		 *   the binding's start index in the input string
		 * @param {boolean} [bTargetTypeAny=false]
		 *   whether the binding's "targetType" should default to "any" (recursively, for all parts)
		 * @returns {int}
		 *   the index at which it has been saved/found in aParts
		 */ /*
			 * Sets the target type of the given binding to the default "any", if applicable.
			 *
			 * @param {object} oBinding
			 *   A binding
			 */ // Note: targetType not allowed here, see BindingParser.mergeParts // binding has at least one property of non-primitive value // the binding must be a complex binding; property "type" (and poss. others) are // newly created objects and thus incomparable -> parse again to have the names // only primitive values; easily comparable // Note: order of top-level properties must not matter for equality! /**
		 * Consumes the next token in the input string and pushes it to the array of tokens.
		 *
		 * @returns {boolean} whether a token is recognized
		 * @throws {Error|Object|SyntaxError}
		 *   <code>fnResolveBinding</code> may throw <code>SyntaxError</code>;
		 *   <code>oTokenizer.setIndex()</code> may throw <code>Error</code>;
		 *   <code>oTokenizer</code> may also throw <code>{name: 'SyntaxError', ...}</code>
		 */ //binding //go to first character after binding string // end of input or unrecognized character /* eslint-disable no-empty */ /* deliberately empty */ /* eslint-enable no-empty */ // Note: new SyntaxError().name === "SyntaxError" // remember tokenizer error