/// <summary>Attempts to parse key values from the specified text.</summary> /// <param name='text'>Text to parse (not null).</param> /// <param name="allowNamedValues">Set to true if the parser should accept named values /// so syntax like Name='value'. If this is false, the parsing will fail on such constructs.</param> /// <param name="allowNull">Set to true if the parser should accept null values. /// If set to false, the parser will fail on null values.</param> /// <param name='instance'>After invocation, the parsed key instance.</param> /// <returns> /// true if the key instance was parsed; false if there was a /// syntactic error. /// </returns> /// <remarks> /// The returned instance contains only string values. To get typed values, a call to /// <see cref="TryConvertValues"/> is necessary. /// </remarks> private static bool TryParseFromUri(string text, bool allowNamedValues, bool allowNull, out KeyInstance instance) { Debug.Assert(text != null, "text != null"); Dictionary<string, object> namedValues = null; List<object> positionalValues = null; ExpressionLexer lexer = new ExpressionLexer(text); Token currentToken = lexer.CurrentToken; if (currentToken.Id == TokenId.End) { instance = Empty; return true; } instance = null; do { if (currentToken.Id == TokenId.Identifier && allowNamedValues) { // Name-value pair. if (positionalValues != null) { // We cannot mix named and non-named values. return false; } string identifier = lexer.CurrentToken.GetIdentifier(); lexer.NextToken(); if (lexer.CurrentToken.Id != TokenId.Equal) { return false; } lexer.NextToken(); if (!lexer.CurrentToken.IsKeyValueToken) { return false; } string namedValue = lexer.CurrentToken.Text; WebUtil.CreateIfNull(ref namedValues); if (namedValues.ContainsKey(identifier)) { // Duplicate name. return false; } namedValues.Add(identifier, namedValue); } else if (currentToken.IsKeyValueToken || (allowNull && currentToken.Id == TokenId.NullLiteral)) { // Positional value. if (namedValues != null) { // We cannot mix named and non-named values. return false; } WebUtil.CreateIfNull(ref positionalValues); positionalValues.Add(lexer.CurrentToken.Text); } else { return false; } // Read the next token. We should be at the end, or find // we have a comma followed by something. lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Id == TokenId.Comma) { lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Id == TokenId.End) { // Trailing comma. return false; } } } while (currentToken.Id != TokenId.End); instance = new KeyInstance(namedValues, positionalValues); return true; }
/// <summary>Initializes a new <see cref="ExpressionParser"/>.</summary> /// <param name="service">Service with data and configuration.</param> /// <param name="setForIt">Resource set for "it" contextual variable</param> /// <param name="typeForIt">Type for "it" contextual variable</param> /// <param name="parameterForIt">Parameters for the current "it" context.</param> /// <param name="expression">Expression to parse.</param> internal ExpressionParser(IDataService service, ResourceSetWrapper setForIt, ResourceType typeForIt, ParameterExpression parameterForIt, string expression) { Debug.Assert(service != null, "service != null"); // For Open types, we could potentially have typeForIt parameter set to null value, // However, we have decided not support ordering on Open properties which also implies // that we can not have entity typed properties on Open types Debug.Assert(typeForIt != null, "typeForIt != null"); Debug.Assert(expression != null, "expression != null"); Debug.Assert(parameterForIt != null, "parameterForIt != null"); this.service = service; this.provider = service.Provider; this.nullPropagationRequired = this.provider.NullPropagationRequired; this.literals = new Dictionary<Expression, string>(ReferenceEqualityComparer<Expression>.Instance); this.setForIt = setForIt; this.typeForIt = typeForIt; this.it = parameterForIt; this.lexer = new ExpressionLexer(expression); }
/// <summary>Parse one of the literals of skip token.</summary> /// <param name="literal">Input literal.</param> /// <returns>Object resulting from conversion of literal.</returns> internal object ParseSkipTokenLiteral(String literal) { ExpressionLexer l = new ExpressionLexer(literal); Expression e = this.ParsePrimaryStart(l); if (e.NodeType != ExpressionType.Constant) { throw new InvalidOperationException(Strings.RequsetQueryParser_ExpectingLiteralInSkipToken(literal)); } return ((ConstantExpression)e).Value; }
/// <summary>Handles the start of primary expressions.</summary> /// <param name="l">Lexer to use for reading tokens</param> /// <returns>The parsed expression.</returns> private Expression ParsePrimaryStart(ExpressionLexer l) { switch (l.CurrentToken.Id) { case TokenId.BooleanLiteral: return this.ParseTypedLiteral(typeof(bool), XmlConstants.EdmBooleanTypeName, l); case TokenId.DateTimeLiteral: return this.ParseTypedLiteral(typeof(DateTime), XmlConstants.EdmDateTimeTypeName, l); case TokenId.DecimalLiteral: return this.ParseTypedLiteral(typeof(decimal), XmlConstants.EdmDecimalTypeName, l); case TokenId.NullLiteral: return ExpressionParser.ParseNullLiteral(l); case TokenId.Identifier: Debug.Assert(Object.ReferenceEquals(this.lexer, l), "Lexer should be the member lexer"); return this.ParseIdentifier(); case TokenId.StringLiteral: return this.ParseTypedLiteral(typeof(string), XmlConstants.EdmStringTypeName, l); case TokenId.Int64Literal: return this.ParseTypedLiteral(typeof(Int64), XmlConstants.EdmInt64TypeName, l); case TokenId.IntegerLiteral: return this.ParseTypedLiteral(typeof(Int32), XmlConstants.EdmInt32TypeName, l); case TokenId.DoubleLiteral: return this.ParseTypedLiteral(typeof(double), XmlConstants.EdmDoubleTypeName, l); case TokenId.SingleLiteral: return this.ParseTypedLiteral(typeof(Single), XmlConstants.EdmSingleTypeName, l); case TokenId.GuidLiteral: return this.ParseTypedLiteral(typeof(Guid), XmlConstants.EdmGuidTypeName, l); case TokenId.BinaryLiteral: return this.ParseTypedLiteral(typeof(byte[]), XmlConstants.EdmBinaryTypeName, l); case TokenId.OpenParen: Debug.Assert(Object.ReferenceEquals(this.lexer, l), "Lexer should be the member lexer"); return this.ParseParenExpression(); default: throw ParseError(Strings.RequestQueryParser_ExpressionExpected(l.CurrentToken.Position)); } }
/// <summary>Handles typed literals.</summary> /// <param name="targetType">Expected type to be parsed.</param> /// <param name="targetTypeName">Expected type name.</param> /// <param name="l">Lexer to use for reading tokens</param> /// <returns>The constants expression produced by building the given literal.</returns> private Expression ParseTypedLiteral(Type targetType, string targetTypeName, ExpressionLexer l) { object targetValue; if (!WebConvert.TryKeyStringToPrimitive(l.CurrentToken.Text, targetType, out targetValue)) { string message = Strings.RequestQueryParser_UnrecognizedLiteral(targetTypeName, l.CurrentToken.Text, l.CurrentToken.Position); throw ParseError(message); } Expression result = this.CreateLiteral(targetValue, l.CurrentToken.Text); l.NextToken(); return result; }
/// <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>Handles 'null' literals.</summary> /// <param name="l">Lexer to use for reading tokens</param> /// <returns>The parsed expression.</returns> private static Expression ParseNullLiteral(ExpressionLexer l) { Debug.Assert(l.CurrentToken.Id == TokenId.NullLiteral, "l.CurrentToken.Id == TokenId.NullLiteral"); l.NextToken(); return WebUtil.NullLiteral; }
private static List<List<string>> ReadExpandOrSelect(string value, bool select, IDataService dataService) { List<List<string>> list = new List<List<string>>(); List<string> list2 = null; ExpressionLexer lexer = new ExpressionLexer(value); while (lexer.CurrentToken.Id != TokenId.End) { string text; bool flag = false; if (select && (lexer.CurrentToken.Id == TokenId.Star)) { text = lexer.CurrentToken.Text; lexer.NextToken(); flag = true; } else if (select) { bool flag2; text = lexer.ReadDottedIdentifier(true); if (dataService.Provider.GetNameFromContainerQualifiedName(text, out flag2) == "*") { flag = true; } } else { text = lexer.ReadDottedIdentifier(false); } if (list2 == null) { list2 = new List<string> { }; } list2.Add(text); TokenId id = lexer.CurrentToken.Id; if (id != TokenId.End) { if (flag || (id != TokenId.Slash)) { lexer.ValidateToken(TokenId.Comma); list2 = null; } lexer.NextToken(); } } return list; }
private static bool TryParseFromUri(string text, bool allowNamedValues, bool allowNull, out KeyInstance instance) { Dictionary<string, object> dictionary = null; List<object> list = null; ExpressionLexer lexer = new ExpressionLexer(text); Token currentToken = lexer.CurrentToken; if (currentToken.Id == TokenId.End) { instance = Empty; return true; } instance = null; do { if ((currentToken.Id == TokenId.Identifier) && allowNamedValues) { if (list != null) { return false; } string identifier = lexer.CurrentToken.GetIdentifier(); lexer.NextToken(); if (lexer.CurrentToken.Id != TokenId.Equal) { return false; } lexer.NextToken(); if (!lexer.CurrentToken.IsKeyValueToken) { return false; } string str2 = lexer.CurrentToken.Text; WebUtil.CreateIfNull<Dictionary<string, object>>(ref dictionary); if (dictionary.ContainsKey(identifier)) { return false; } dictionary.Add(identifier, str2); } else { if (!currentToken.IsKeyValueToken && (!allowNull || (currentToken.Id != TokenId.NullLiteral))) { return false; } if (dictionary != null) { return false; } WebUtil.CreateIfNull<List<object>>(ref list); list.Add(lexer.CurrentToken.Text); } lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Id == TokenId.Comma) { lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Id == TokenId.End) { return false; } } } while (currentToken.Id != TokenId.End); instance = new KeyInstance(dictionary, list); return true; }