static Token() { Token token = new Token { Text = "gt", Id = TokenId.Identifier, Position = 0 }; GreaterThan = token; Token token2 = new Token { Text = "eq", Id = TokenId.Identifier, Position = 0 }; EqualsTo = token2; Token token3 = new Token { Text = "lt", Id = TokenId.Identifier, Position = 0 }; LessThan = token3; }
private void ExpandIdentifier(Func<bool> expander, bool ignoreWs) { if (this.token.Id == TokenId.Identifier) { int textPos = this.textPos; char ch = this.ch; Token token = this.token; bool ignoreWhitespace = this.ignoreWhitespace; this.ignoreWhitespace = ignoreWs; int position = this.token.Position; if (expander()) { this.token.Text = this.text.Substring(position, this.textPos - position); this.token.Position = position; } else { this.textPos = textPos; this.ch = ch; this.token = token; } this.ignoreWhitespace = ignoreWhitespace; } }
/// <summary>Returns the next token without advancing the lexer.</summary> /// <returns>The next token.</returns> internal Token PeekNextToken() { int savedTextPos = this.textPos; char savedChar = this.ch; Token savedToken = this.token; this.NextToken(); Token result = this.token; this.textPos = savedTextPos; this.ch = savedChar; this.token = savedToken; return result; }
/// <summary>Handles -, not unary operators.</summary> /// <returns>The parsed expression.</returns> private Expression ParseUnary() { this.RecurseEnter(); if (this.CurrentToken.Id == TokenId.Minus || this.CurrentToken.IdentifierIs(ExpressionConstants.KeywordNot)) { Token op = this.CurrentToken; this.lexer.NextToken(); if (op.Id == TokenId.Minus && (ExpressionLexer.IsNumeric(this.CurrentToken.Id))) { Token numberLiteral = this.CurrentToken; numberLiteral.Text = "-" + numberLiteral.Text; numberLiteral.Position = op.Position; this.CurrentToken = numberLiteral; this.RecurseLeave(); return this.ParsePrimary(); } Expression expr = this.ParseUnary(); if (op.Id == TokenId.Minus) { this.CheckAndPromoteOperand(typeof(OperationSignatures.INegationSignatures), op.Text, ref expr, op.Position); expr = GenerateNegate(expr); } else { this.CheckAndPromoteOperand(typeof(OperationSignatures.INotSignatures), op.Text, ref expr, op.Position); expr = GenerateNot(expr); } this.RecurseLeave(); return expr; } this.RecurseLeave(); return this.ParsePrimary(); }
/// <summary> /// Given left and right hand side expressions, generates a comparison expression based /// on the given comparison token /// </summary> /// <param name="left">Left hand side expression</param> /// <param name="right">Right hand side expression</param> /// <param name="op">Comparison operator</param> /// <returns>Resulting comparison expression</returns> private Expression GenerateComparisonExpression(Expression left, Expression right, Token op) { bool equality = op.IsEqualityOperator; if (equality && !left.Type.IsValueType && !right.Type.IsValueType) { if (left.Type != right.Type) { if (WebUtil.IsNullConstant(left)) { left = Expression.Constant(null, right.Type); } else if (WebUtil.IsNullConstant(right)) { right = Expression.Constant(null, left.Type); } else if (left.Type.IsAssignableFrom(right.Type)) { right = Expression.Convert(right, left.Type); } else if (right.Type.IsAssignableFrom(left.Type)) { left = Expression.Convert(left, right.Type); } else { throw ExpressionParser.IncompatibleOperandsError(op.Text, left, right, op.Position); } } } else if (left == WebUtil.NullLiteral || right == WebUtil.NullLiteral) { if (!equality) { throw ParseError( Strings.RequestQueryParser_NullOperatorUnsupported(op.Text, op.Position, this.lexer.ExpressionText)); } // Because we don't have an explicit "is null" check, literal comparisons // to null are special. if (!WebUtil.TypeAllowsNull(left.Type)) { left = Expression.Convert(left, typeof(Nullable<>).MakeGenericType(left.Type)); } else if (!WebUtil.TypeAllowsNull(right.Type)) { right = Expression.Convert(right, typeof(Nullable<>).MakeGenericType(right.Type)); } } else { // Enums should be checked here for promotion when supported, but they aren't in this version. Debug.Assert(!IsEnumType(left.Type), "!IsEnumType(left.Type)"); Debug.Assert(!IsEnumType(right.Type), "!IsEnumType(right.Type)"); Type signatures = equality ? typeof(OperationSignatures.IEqualitySignatures) : typeof(OperationSignatures.IRelationalSignatures); this.CheckAndPromoteOperands(signatures, op.Text, ref left, ref right, op.Position); } Debug.Assert(op.Id == TokenId.Identifier, "op.id == TokenId.Identifier"); MethodInfo comparisonMethodInfo = null; if (!equality) { if (left.Type == typeof(string)) { comparisonMethodInfo = StringCompareMethodInfo; } else if (left.Type == typeof(bool)) { comparisonMethodInfo = BoolCompareMethodInfo; } else if (left.Type == typeof(bool?)) { comparisonMethodInfo = BoolCompareMethodInfoNullable; } else if (left.Type == typeof(Guid)) { comparisonMethodInfo = GuidCompareMethodInfo; } else if (left.Type == typeof(Guid?)) { comparisonMethodInfo = GuidCompareMethodInfoNullable; } } switch (op.Text) { case ExpressionConstants.KeywordEqual: left = GenerateEqual(left, right); break; case ExpressionConstants.KeywordNotEqual: left = GenerateNotEqual(left, right); break; case ExpressionConstants.KeywordGreaterThan: left = GenerateGreaterThan(left, right, comparisonMethodInfo); break; case ExpressionConstants.KeywordGreaterThanOrEqual: left = GenerateGreaterThanEqual(left, right, comparisonMethodInfo); break; case ExpressionConstants.KeywordLessThan: left = GenerateLessThan(left, right, comparisonMethodInfo); break; case ExpressionConstants.KeywordLessThanOrEqual: left = GenerateLessThanEqual(left, right, comparisonMethodInfo); break; } return left; }
/// <summary> /// Generates a comparison expression which can handle NULL values for any type. /// NULL is always treated as the smallest possible value. /// So for example for strings NULL is smaller than any non-NULL string. /// For now only GreaterThan and LessThan operators are supported by this method. /// </summary> /// <param name="left">Left hand side expression</param> /// <param name="rightLiteral">Literal for the right hand side</param> /// <param name="op">gt or lt operator token</param> /// <returns>Resulting comparison expression (has a Boolean value)</returns> private Expression GenerateNullAwareComparison(Expression left, String rightLiteral, Token op) { Debug.Assert( op.Text == ExpressionConstants.KeywordGreaterThan || op.Text == ExpressionConstants.KeywordLessThan, "Only GreaterThan or LessThan operators are supported by the GenerateNullAwateComparison method for now."); ExpressionLexer l = new ExpressionLexer(rightLiteral); Expression right = this.ParsePrimaryStart(l); if (WebUtil.TypeAllowsNull(left.Type)) { if (!WebUtil.TypeAllowsNull(right.Type)) { right = Expression.Convert(right, typeof(Nullable<>).MakeGenericType(right.Type)); } } else if (WebUtil.TypeAllowsNull(right.Type)) { left = Expression.Convert(left, typeof(Nullable<>).MakeGenericType(left.Type)); } else { // Can't perform NULL aware comparison on this type. Just let the normal // comparison deal with it. Since the type doesn't allow NULL one should // never appear, so normal comparison is just fine. return this.GenerateComparisonExpression(left, right, op); } switch (op.Text) { case ExpressionConstants.KeywordGreaterThan: // (left != null) && ((right == null) || Compare(left, right) > 0) if (left == WebUtil.NullLiteral) { return Expression.Constant(false, typeof(bool)); } else if (right == WebUtil.NullLiteral) { return GenerateNotEqual(left, Expression.Constant(null, left.Type)); } else { return GenerateLogicalAnd( GenerateNotEqual(left, Expression.Constant(null, left.Type)), GenerateLogicalOr( GenerateEqual(right, Expression.Constant(null, right.Type)), this.GenerateComparisonExpression(left, right, op))); } case ExpressionConstants.KeywordLessThan: // (right != null) && ((left == null) || Compare(left, right) < 0) if (right == WebUtil.NullLiteral) { return Expression.Constant(false, typeof(bool)); } else if (left == WebUtil.NullLiteral) { return GenerateNotEqual(right, Expression.Constant(null, right.Type)); } else { return GenerateLogicalAnd( GenerateNotEqual(right, Expression.Constant(null, left.Type)), GenerateLogicalOr( GenerateEqual(left, Expression.Constant(null, right.Type)), this.GenerateComparisonExpression(left, right, op))); } default: // For now only < and > are supported as we use this only from $skiptoken throw ParseError( Strings.RequestQueryParser_NullOperatorUnsupported(op.Text, op.Position, this.lexer.ExpressionText)); } }
/// <summary> /// Generates a comparison expression given a left hand side expression and literal for right hand side /// </summary> /// <param name="left">Left hand side experssions</param> /// <param name="rightLiteral">Literal for right hand side</param> /// <param name="op">gt, eq or lt operator token</param> /// <returns>Resulting comparison expression</returns> private Expression GenerateComparison(Expression left, String rightLiteral, Token op) { ExpressionLexer l = new ExpressionLexer(rightLiteral); return this.GenerateComparisonExpression(left, this.ParsePrimaryStart(l), op); }
/// <summary>Checks that the given token has the specified identifier.</summary> /// <param name="token">Token to check</param> /// <param name="id">Identifier to check.</param> /// <returns>true if <paramref name="token"/> is an identifier with the specified text.</returns> private static bool TokenIdentifierIs(Token token, string id) { return token.Id == TokenId.Identifier && String.Equals(id, token.Text, StringComparison.OrdinalIgnoreCase); }
internal Token PeekNextToken() { int textPos = this.textPos; char ch = this.ch; Token token = this.token; this.NextToken(); Token token2 = this.token; this.textPos = textPos; this.ch = ch; this.token = token; return token2; }