/// <summary>Whether the specified token identifier is a numeric literal.</summary> /// <param name="id">Token to check.</param> /// <returns>true if it's a numeric literal; false otherwise.</returns> internal static bool IsNumeric(ExpressionTokenKind id) { return id == ExpressionTokenKind.IntegerLiteral || id == ExpressionTokenKind.DecimalLiteral || id == ExpressionTokenKind.DoubleLiteral || id == ExpressionTokenKind.Int64Literal || id == ExpressionTokenKind.SingleLiteral; }
/// <summary> /// Returns whether the <paramref name="tokenKind"/> is a primitive literal type: /// Binary, Boolean, DateTime, Decimal, Double, Guid, In64, Integer, Null, Single, or String. /// Internal for test use only /// </summary> /// <param name="tokenKind">InternalKind of token.</param> /// <returns>Whether the <paramref name="tokenKind"/> is a literal type.</returns> internal static Boolean IsLiteralType(this ExpressionTokenKind tokenKind) { switch (tokenKind) { case ExpressionTokenKind.BinaryLiteral: case ExpressionTokenKind.BooleanLiteral: case ExpressionTokenKind.DateTimeLiteral: case ExpressionTokenKind.DecimalLiteral: case ExpressionTokenKind.DoubleLiteral: case ExpressionTokenKind.GuidLiteral: case ExpressionTokenKind.Int64Literal: case ExpressionTokenKind.IntegerLiteral: case ExpressionTokenKind.NullLiteral: case ExpressionTokenKind.SingleLiteral: case ExpressionTokenKind.StringLiteral: case ExpressionTokenKind.TimeOfDayLiteral: case ExpressionTokenKind.DateLiteral: case ExpressionTokenKind.DateTimeOffsetLiteral: case ExpressionTokenKind.DurationLiteral: case ExpressionTokenKind.GeographyLiteral: case ExpressionTokenKind.GeometryLiteral: return(true); default: return(false); } }
/// <summary>Validates the current token is of the specified kind.</summary> /// <param name="t">Expected token kind.</param> internal void ValidateToken(ExpressionTokenKind t) { if (this.token.Kind != t) { throw ParseError(ODataErrorStrings.ExpressionLexer_SyntaxError(this.textPos, this.Text)); } }
/// <summary>Whether the specified token identifier is a numeric literal.</summary> /// <param name="id">Token to check.</param> /// <returns>true if it's a numeric literal; false otherwise.</returns> internal static bool IsNumeric(ExpressionTokenKind id) { return (id == ExpressionTokenKind.IntegerLiteral || id == ExpressionTokenKind.DecimalLiteral || id == ExpressionTokenKind.DoubleLiteral || id == ExpressionTokenKind.Int64Literal || id == ExpressionTokenKind.SingleLiteral); }
/// <summary>Validates the current token is of the specified kind.</summary> /// <param name="t">Expected token kind.</param> internal void ValidateToken(ExpressionTokenKind t) { if (this.token.Kind != t) { throw ParseError(Strings.RequestQueryParser_SyntaxError); } }
/// <summary>Validates the current token is of the specified kind.</summary> /// <param name="t">Expected token kind.</param> internal void ValidateId(ExpressionTokenKind t) { if (this.Kind != t) { throw DataServiceException.CreateSyntaxError(Strings.RequestQueryParser_SyntaxError); } }
internal static bool IsNumeric(ExpressionTokenKind id) { if (((id != ExpressionTokenKind.IntegerLiteral) && (id != ExpressionTokenKind.DecimalLiteral)) && ((id != ExpressionTokenKind.DoubleLiteral) && (id != ExpressionTokenKind.Int64Literal))) { return(id == ExpressionTokenKind.SingleLiteral); } return(true); }
/// <summary>Validates the current token is of the specified kind.</summary> /// <param name="t">Expected token kind.</param> internal void ValidateToken(ExpressionTokenKind t) { DebugUtils.CheckNoExternalCallers(); if (this.token.Kind != t) { throw ParseError(ODataErrorStrings.ExpressionLexer_SyntaxError(this.textPos, this.text)); } }
/// <summary>Whether the specified token identifier is a numeric literal.</summary> /// <param name="id">Token to check.</param> /// <returns>true if it's a numeric literal; false otherwise.</returns> internal static bool IsNumeric(ExpressionTokenKind id) { DebugUtils.CheckNoExternalCallers(); return (id == ExpressionTokenKind.IntegerLiteral || id == ExpressionTokenKind.DecimalLiteral || id == ExpressionTokenKind.DoubleLiteral || id == ExpressionTokenKind.Int64Literal || id == ExpressionTokenKind.SingleLiteral); }
/// <summary> /// Tries to parse a collection of function parameters. Allows path and filter to share the core algorithm while representing parameters differently. /// </summary> /// <param name="lexer">The lexer to read from.</param> /// <param name="endTokenKind">The token kind that marks the end of the parameters.</param> /// <param name="splitParameters">The parameters if they were successfully split.</param> /// <returns>Whether the parameters could be split.</returns> private static bool TrySplitFunctionParameters(this ExpressionLexer lexer, ExpressionTokenKind endTokenKind, out ICollection <FunctionParameterToken> splitParameters) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(lexer != null, "lexer != null"); var parameters = new List <FunctionParameterToken>(); splitParameters = parameters; ExpressionToken currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { return(true); } if (currentToken.Kind != ExpressionTokenKind.Identifier || lexer.PeekNextToken().Kind != ExpressionTokenKind.Equal) { return(false); } while (currentToken.Kind != endTokenKind) { lexer.ValidateToken(ExpressionTokenKind.Identifier); string identifier = lexer.CurrentToken.GetIdentifier(); lexer.NextToken(); lexer.ValidateToken(ExpressionTokenKind.Equal); lexer.NextToken(); QueryToken parameterValue; if (!TryCreateParameterValueToken(lexer.CurrentToken, out parameterValue)) { throw new ODataException(ODataErrorStrings.ExpressionLexer_SyntaxError(lexer.Position, lexer.ExpressionText)); } parameters.Add(new FunctionParameterToken(identifier, parameterValue)); // Read the next parameterToken. We should be at the end, or find // we have a comma followed by something. lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Kind == ExpressionTokenKind.Comma) { lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { // Trailing comma. throw new ODataException(ODataErrorStrings.ExpressionLexer_SyntaxError(lexer.Position, lexer.ExpressionText)); } } } return(true); }
/// <summary>Handles lexemes that are formed by an identifier followed by a quoted string.</summary> /// <remarks>This method modified the token field as necessary.</remarks> private void HandleTypePrefixedLiterals() { ExpressionTokenKind id = this.token.Kind; if (id != ExpressionTokenKind.Identifier) { return; } bool quoteFollows = this.ch == '\''; if (!quoteFollows) { return; } string tokenText = this.token.Text; if (String.Equals(tokenText, XmlConstants.LiteralPrefixBinary, StringComparison.OrdinalIgnoreCase) || tokenText == "X" || tokenText == "x") { id = ExpressionTokenKind.BinaryLiteral; } else if (String.Equals(tokenText, XmlConstants.LiteralPrefixGeography, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.GeographylLiteral; } else if (String.Equals(tokenText, XmlConstants.LiteralPrefixGeometry, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.GeometryLiteral; } else if (String.Equals(tokenText, XmlConstants.LiteralPrefixDuration, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.DurationLiteral; } else { return; } int tokenPos = this.token.Position; do { this.NextChar(); }while (this.ch.HasValue && this.ch != '\''); if (this.ch == null) { throw ParseError(Strings.RequestQueryParser_UnterminatedLiteral(this.text)); } this.NextChar(); this.token.Kind = id; this.token.Text = this.text.Substring(tokenPos, this.textPos - tokenPos); }
internal static IEdmTypeReference GetLiteralEdmTypeReference(ExpressionTokenKind tokenKind) { switch (tokenKind) { case ExpressionTokenKind.BooleanLiteral: return(EdmCoreModel.Instance.GetBoolean(false)); case ExpressionTokenKind.DecimalLiteral: return(EdmCoreModel.Instance.GetDecimal(false)); case ExpressionTokenKind.StringLiteral: return(EdmCoreModel.Instance.GetString(true)); case ExpressionTokenKind.Int64Literal: return(EdmCoreModel.Instance.GetInt64(false)); case ExpressionTokenKind.IntegerLiteral: return(EdmCoreModel.Instance.GetInt32(false)); case ExpressionTokenKind.DoubleLiteral: return(EdmCoreModel.Instance.GetDouble(false)); case ExpressionTokenKind.SingleLiteral: return(EdmCoreModel.Instance.GetSingle(false)); case ExpressionTokenKind.GuidLiteral: return(EdmCoreModel.Instance.GetGuid(false)); case ExpressionTokenKind.BinaryLiteral: return(EdmCoreModel.Instance.GetBinary(true)); case ExpressionTokenKind.DateLiteral: return(EdmCoreModel.Instance.GetDate(false)); case ExpressionTokenKind.DateTimeOffsetLiteral: return(EdmCoreModel.Instance.GetDateTimeOffset(false)); case ExpressionTokenKind.DurationLiteral: return(EdmCoreModel.Instance.GetTemporal(EdmPrimitiveTypeKind.Duration, false)); case ExpressionTokenKind.GeographyLiteral: return(EdmCoreModel.Instance.GetSpatial(EdmPrimitiveTypeKind.Geography, false)); case ExpressionTokenKind.GeometryLiteral: return(EdmCoreModel.Instance.GetSpatial(EdmPrimitiveTypeKind.Geometry, false)); case ExpressionTokenKind.QuotedLiteral: return(EdmCoreModel.Instance.GetString(true)); case ExpressionTokenKind.TimeOfDayLiteral: return(EdmCoreModel.Instance.GetTimeOfDay(false)); } return(null); }
/// <summary> /// Standard Constructor (all types except operators) /// </summary> public ExpressionToken(string name, ExpressionTokenKind kind) { if (kind == ExpressionTokenKind.Operator) { throw new InvalidOperationException("Priority must be specified for an operator."); } Name = name; Priority = 0; Kind = kind; }
/// <summary> /// Expand the token selection if the next token matches the input token /// </summary> /// <param name="id">the list of token id to match</param> /// <returns>true if matched</returns> private bool MoveNextWhenMatch(ExpressionTokenKind id) { ExpressionToken next = this.PeekNextToken(); if (id == next.Kind) { this.NextToken(); return(true); } return(false); }
/// <summary>Reads a $select clause.</summary> /// <param name="value">Value to read.</param> /// <param name="dataServiceProviderWrapper">The provider wrapper for the service.</param> /// <returns>A list of paths, each of which is a list of identifiers.</returns> private static IList <IList <string> > SplitSelect(string value, DataServiceProviderWrapper dataServiceProviderWrapper) { Debug.Assert(!String.IsNullOrEmpty(value), "!String.IsNullOrEmpty(value)"); List <IList <string> > result = new List <IList <string> >(); List <string> currentPath = null; ExpressionLexer lexer = new ExpressionLexer(value); while (lexer.CurrentToken.Kind != ExpressionTokenKind.End) { string identifier; bool lastSegment = false; if (lexer.CurrentToken.Kind == ExpressionTokenKind.Star) { identifier = lexer.CurrentToken.Text; lexer.NextToken(); lastSegment = true; } else { identifier = lexer.ReadDottedIdentifier(true /*allowEndWithDotStar*/); bool nameIsContainerQualifed; if (dataServiceProviderWrapper.GetNameFromContainerQualifiedName(identifier, out nameIsContainerQualifed) == "*") { lastSegment = true; } } if (currentPath == null) { currentPath = new List <string>(); result.Add(currentPath); } currentPath.Add(identifier); // Check whether we're at the end, whether we're drilling in, // or whether we're finishing with this path. ExpressionTokenKind tokenId = lexer.CurrentToken.Kind; if (tokenId != ExpressionTokenKind.End) { if (lastSegment || tokenId != ExpressionTokenKind.Slash) { lexer.ValidateToken(ExpressionTokenKind.Comma); currentPath = null; } lexer.NextToken(); } } return(result); }
/// <summary>Handles lexemes that are formed by an identifier followed by a quoted string.</summary> /// <remarks>This method modified the token field as necessary.</remarks> private void HandleTypePrefixedLiterals() { ExpressionTokenKind id = this.token.Kind; if (id != ExpressionTokenKind.Identifier) { return; } bool quoteFollows = this.ch == '\''; if (!quoteFollows) { return; } string tokenText = this.token.Text; if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixDateTime, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.DateTimeLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixGuid, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.GuidLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixBinary, StringComparison.OrdinalIgnoreCase) || String.Equals(tokenText, ExpressionConstants.LiteralPrefixShortBinary, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.BinaryLiteral; } else { return; } int tokenPos = this.token.Position; do { this.NextChar(); }while (this.ch != '\0' && this.ch != '\''); if (this.ch == '\0') { throw ParseError(Strings.ExpressionLexer_UnterminatedLiteral(this.textPos, this.text)); } this.NextChar(); this.token.Kind = id; this.token.Text = this.text.Substring(tokenPos, this.textPos - tokenPos); }
/// <summary> /// Parses identifiers which have been recognizes as function calls. /// </summary> /// <returns>The lexical token representing the expression.</returns> private QueryToken ParseIdentifierAsFunction() { ExpressionToken functionToken = this.lexer.CurrentToken; Debug.Assert(functionToken.Kind == ExpressionTokenKind.Identifier, "Only identifier tokens can be treated as function calls."); string funcName = this.lexer.CurrentToken.GetIdentifier(); this.lexer.NextToken(); ExpressionTokenKind splitToken = funcName == "any" ? ExpressionTokenKind.Colon : ExpressionTokenKind.Comma; QueryToken[] arguments = this.ParseArgumentList(splitToken); return(new FunctionCallQueryToken(functionToken.Text, arguments)); }
/// <summary> /// Tries to parse a collection of function parameters. Allows path and filter to share the core algorithm while representing parameters differently. /// </summary> /// <param name="parser">The UriQueryExpressionParser to read from.</param> /// <param name="endTokenKind">The token kind that marks the end of the parameters.</param> /// <param name="splitParameters">The parameters if they were successfully split.</param> /// <returns>Whether the parameters could be split.</returns> private static bool TrySplitOperationParameters(this UriQueryExpressionParser parser, ExpressionTokenKind endTokenKind, out ICollection<FunctionParameterToken> splitParameters) { Debug.Assert(parser != null, "parser != null"); var lexer = parser.Lexer; var parameters = new List<FunctionParameterToken>(); splitParameters = parameters; ExpressionToken currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { return true; } if (currentToken.Kind != ExpressionTokenKind.Identifier || lexer.PeekNextToken().Kind != ExpressionTokenKind.Equal) { return false; } while (currentToken.Kind != endTokenKind) { lexer.ValidateToken(ExpressionTokenKind.Identifier); string identifier = lexer.CurrentToken.GetIdentifier(); lexer.NextToken(); lexer.ValidateToken(ExpressionTokenKind.Equal); lexer.NextToken(); // the below UriQueryExpressionParser.ParseExpression() is able to parse common expression per ABNF: // functionExprParameter = parameterName EQ ( parameterAlias / parameterValue ) // parameterValue = arrayOrObject // / commonExpr QueryToken parameterValue = parser.ParseExpression(); parameters.Add(new FunctionParameterToken(identifier, parameterValue)); // the above parser.ParseExpression() already moves to the next token, now get CurrentToken checking a comma followed by something currentToken = lexer.CurrentToken; if (currentToken.Kind == ExpressionTokenKind.Comma) { lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { // Trailing comma. throw new ODataException(ODataErrorStrings.ExpressionLexer_SyntaxError(lexer.Position, lexer.ExpressionText)); } } } return true; }
/// <summary> /// Parses argument lists. /// </summary> /// <param name="splitToken">The token to split the argument list by.</param> /// <returns>The lexical tokens representing the arguments.</returns> private QueryToken[] ParseArgumentList(ExpressionTokenKind splitToken) { if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.OpenParen) { throw ParseError(Strings.UriQueryExpressionParser_OpenParenExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } this.lexer.NextToken(); QueryToken[] arguments = this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen ? this.ParseArguments(splitToken) : QueryToken.EmptyTokens; if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { throw ParseError(Strings.UriQueryExpressionParser_CloseParenOrCommaExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } this.lexer.NextToken(); return(arguments); }
/// <summary> /// Parses comma-separated arguments. /// </summary> /// <param name="splitToken">The token to split the argument list by.</param> /// <returns>The lexical tokens representing the arguments.</returns> private QueryToken[] ParseArguments(ExpressionTokenKind splitToken) { List <QueryToken> argList = new List <QueryToken>(); while (true) { argList.Add(this.ParseExpression()); if (this.lexer.CurrentToken.Kind != splitToken) { break; } this.lexer.NextToken(); } return(argList.ToArray()); }
/// <summary> /// Check whether the current identifier is a function. If so, expand the token text to the function signature /// </summary> /// <returns>True if the current identifier is a function call</returns> internal bool ExpandIdentifierAsFunction() { DebugUtils.CheckNoExternalCallers(); // FUNCTION := (<ID> {<DOT>}) ... <ID> <OpenParen> // if we fail to match then we leave the token as it ExpressionTokenKind id = this.token.Kind; if (id != ExpressionTokenKind.Identifier) { return(false); } int savedTextPos = this.textPos; char? savedChar = this.ch == null ? (char?)null : this.ch.Value; ExpressionToken savedToken = this.token; bool savedIgnoreWs = this.ignoreWhitespace; this.ignoreWhitespace = false; // Expansion left anchor int tokenStartPos = this.token.Position; while (this.MoveNextWhenMatch(ExpressionTokenKind.Dot) && this.MoveNextWhenMatch(ExpressionTokenKind.Identifier)) { } bool matched = this.CurrentToken.Kind == ExpressionTokenKind.Identifier && this.PeekNextToken().Kind == ExpressionTokenKind.OpenParen; if (matched) { this.token.Text = this.text.Substring(tokenStartPos, this.textPos - tokenStartPos); this.token.Position = tokenStartPos; } else { this.textPos = savedTextPos; this.ch = savedChar; this.token = savedToken; } this.ignoreWhitespace = savedIgnoreWs; return(matched); }
private static bool IsLiteralType(ExpressionTokenKind tokenKind) { switch (tokenKind) { case ExpressionTokenKind.NullLiteral: case ExpressionTokenKind.BooleanLiteral: case ExpressionTokenKind.StringLiteral: case ExpressionTokenKind.IntegerLiteral: case ExpressionTokenKind.Int64Literal: case ExpressionTokenKind.SingleLiteral: case ExpressionTokenKind.DateTimeLiteral: case ExpressionTokenKind.DateTimeOffsetLiteral: case ExpressionTokenKind.TimeLiteral: case ExpressionTokenKind.DecimalLiteral: case ExpressionTokenKind.DoubleLiteral: case ExpressionTokenKind.GuidLiteral: case ExpressionTokenKind.BinaryLiteral: case ExpressionTokenKind.GeographyLiteral: case ExpressionTokenKind.GeometryLiteral: return(true); } return(false); }
/// <summary>Validates the current token is of the specified kind.</summary> /// <param name="t">Expected token kind.</param> internal void ValidateToken(ExpressionTokenKind t) { DebugUtils.CheckNoExternalCallers(); if (this.token.Kind != t) { throw ParseError(Strings.ExpressionLexer_SyntaxError(this.textPos, this.text)); } }
internal void SetCustomEdmTypeLiteral(IEdmTypeReference edmType) { this.Kind = ExpressionTokenKind.CustomTypeLiteral; this.LiteralEdmType = edmType; }
/// <summary> /// Parses comma-separated arguments. /// </summary> /// <param name="splitToken">The token to split the argument list by.</param> /// <returns>The lexical tokens representing the arguments.</returns> private QueryToken[] ParseArguments(ExpressionTokenKind splitToken) { List<QueryToken> argList = new List<QueryToken>(); while (true) { argList.Add(this.ParseExpression()); if (this.lexer.CurrentToken.Kind != splitToken) { break; } this.lexer.NextToken(); } return argList.ToArray(); }
private static bool IsLiteralType(ExpressionTokenKind tokenKind) { switch (tokenKind) { case ExpressionTokenKind.NullLiteral: case ExpressionTokenKind.BooleanLiteral: case ExpressionTokenKind.StringLiteral: case ExpressionTokenKind.IntegerLiteral: case ExpressionTokenKind.Int64Literal: case ExpressionTokenKind.SingleLiteral: case ExpressionTokenKind.DateTimeLiteral: case ExpressionTokenKind.DateTimeOffsetLiteral: case ExpressionTokenKind.TimeLiteral: case ExpressionTokenKind.DecimalLiteral: case ExpressionTokenKind.DoubleLiteral: case ExpressionTokenKind.GuidLiteral: case ExpressionTokenKind.BinaryLiteral: case ExpressionTokenKind.GeographyLiteral: case ExpressionTokenKind.GeometryLiteral: return true; } return false; }
internal static bool IsNumeric(ExpressionTokenKind id) { if (((id != ExpressionTokenKind.IntegerLiteral) && (id != ExpressionTokenKind.DecimalLiteral)) && ((id != ExpressionTokenKind.DoubleLiteral) && (id != ExpressionTokenKind.Int64Literal))) { return (id == ExpressionTokenKind.SingleLiteral); } return true; }
/// <summary> /// Expand the token selection if the next token matches the input token /// </summary> /// <param name="id">the list of token id to match</param> /// <returns>true if matched</returns> private bool ExpandWhenMatch(ExpressionTokenKind id) { ExpressionToken next = this.PeekNextToken(); if (id == next.Kind) { this.NextToken(); return true; } return false; }
/// <summary>Handles lexemes that are formed by an identifier followed by a quoted string.</summary> /// <remarks>This method modified the token field as necessary.</remarks> private void HandleTypePrefixedLiterals() { ExpressionTokenKind id = this.token.Kind; if (id != ExpressionTokenKind.Identifier) { return; } bool quoteFollows = this.ch == '\''; if (!quoteFollows) { return; } string tokenText = this.token.Text; if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixDuration, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.DurationLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixBinary, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.BinaryLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixGeography, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.GeographyLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixGeometry, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.GeometryLiteral; } else if (string.Equals(tokenText, ExpressionConstants.KeywordNull, StringComparison.OrdinalIgnoreCase)) { // typed null literals are not supported. throw ParseError(ODataErrorStrings.ExpressionLexer_SyntaxError(this.textPos, this.Text)); } else { // treat as quoted literal id = ExpressionTokenKind.QuotedLiteral; } int tokenPos = this.token.Position; do { this.NextChar(); }while (this.ch.HasValue && this.ch != '\''); if (this.ch == null) { throw ParseError(ODataErrorStrings.ExpressionLexer_UnterminatedLiteral(this.textPos, this.Text)); } this.NextChar(); this.token.Kind = id; this.token.Text = this.Text.Substring(tokenPos, this.textPos - tokenPos); }
/// <summary> /// Tries to parse a collection of function parameters. Allows path and filter to share the core algorithm while representing parameters differently. /// </summary> /// <param name="parser">The UriQueryExpressionParser to read from.</param> /// <param name="endTokenKind">The token kind that marks the end of the parameters.</param> /// <param name="splitParameters">The parameters if they were successfully split.</param> /// <returns>Whether the parameters could be split.</returns> private static bool TrySplitOperationParameters(this UriQueryExpressionParser parser, ExpressionTokenKind endTokenKind, out ICollection <FunctionParameterToken> splitParameters) { Debug.Assert(parser != null, "parser != null"); var lexer = parser.Lexer; var parameters = new List <FunctionParameterToken>(); splitParameters = parameters; ExpressionToken currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { return(true); } if (currentToken.Kind != ExpressionTokenKind.Identifier || lexer.PeekNextToken().Kind != ExpressionTokenKind.Equal) { return(false); } while (currentToken.Kind != endTokenKind) { lexer.ValidateToken(ExpressionTokenKind.Identifier); string identifier = lexer.CurrentToken.GetIdentifier(); lexer.NextToken(); lexer.ValidateToken(ExpressionTokenKind.Equal); lexer.NextToken(); // the below UriQueryExpressionParser.ParseExpression() is able to parse common expression per ABNF: // functionExprParameter = parameterName EQ ( parameterAlias / parameterValue ) // parameterValue = arrayOrObject // / commonExpr QueryToken parameterValue = parser.ParseExpression(); parameters.Add(new FunctionParameterToken(identifier, parameterValue)); // the above parser.ParseExpression() already moves to the next token, now get CurrentToken checking a comma followed by something currentToken = lexer.CurrentToken; if (currentToken.Kind == ExpressionTokenKind.Comma) { lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { // Trailing comma. throw new ODataException(ODataErrorStrings.ExpressionLexer_SyntaxError(lexer.Position, lexer.ExpressionText)); } } } return(true); }
/// <summary> /// Makes best guess on numeric string without trailing letter like L, F, M, D /// </summary> /// <param name="numericStr">The numeric string.</param> /// <param name="guessedKind">The possbile kind (IntegerLiteral or DoubleLiteral) from ParseFromDigit() method.</param> /// <returns>A more accurate ExpressionTokenKind</returns> private static ExpressionTokenKind MakeBestGuessOnNoSuffixStr(string numericStr, ExpressionTokenKind guessedKind) { // no suffix, so // (1) make a best guess (note: later we support promoting each to later one: int32->int64->single->double->decimal). // look at value: "2147483647" may be Int32/long, "2147483649" must be long. // look at precision: "3258.67876576549" may be sinle/double/decimal, "3258.678765765489753678965390" must be decimal. // (2) then let MetadataUtilsCommon.CanConvertPrimitiveTypeTo() method does further promotion when knowing expected sematics type. int tmpInt = 0; long tmpLong = 0; float tmpFloat = 0; double tmpDouble = 0; decimal tmpDecimal = 0; if (guessedKind == ExpressionTokenKind.IntegerLiteral) { if (int.TryParse(numericStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out tmpInt)) { return(ExpressionTokenKind.IntegerLiteral); } if (long.TryParse(numericStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out tmpLong)) { return(ExpressionTokenKind.Int64Literal); } } bool canBeSingle = float.TryParse(numericStr, NumberStyles.Float, CultureInfo.InvariantCulture, out tmpFloat); bool canBeDouble = double.TryParse(numericStr, NumberStyles.Float, CultureInfo.InvariantCulture, out tmpDouble); bool canBeDecimal = decimal.TryParse(numericStr, NumberStyles.Integer | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out tmpDecimal); // 1. try high precision -> low precision if (canBeDouble && canBeDecimal) { decimal doubleToDecimalR; decimal doubleToDecimalN; // To keep the full precision of the current value, which if necessary is all 17 digits of precision supported by the Double type. bool doubleCanBeDecimalR = decimal.TryParse(tmpDouble.ToString("R", CultureInfo.InvariantCulture), NumberStyles.Float, CultureInfo.InvariantCulture, out doubleToDecimalR); // To cover the scientific notation case, such as 1e+19 in the tmpDouble bool doubleCanBeDecimalN = decimal.TryParse(tmpDouble.ToString("N29", CultureInfo.InvariantCulture), NumberStyles.Number, CultureInfo.InvariantCulture, out doubleToDecimalN); if ((doubleCanBeDecimalR && doubleToDecimalR != tmpDecimal) || (!doubleCanBeDecimalR && doubleCanBeDecimalN && doubleToDecimalN != tmpDecimal)) { // losing precision as double, so choose decimal return(ExpressionTokenKind.DecimalLiteral); } } // here can't use normal casting like the above double VS decimal. // prevent losing precision in float -> double, e.g. (double)1.234f will be 1.2339999675750732d not 1.234d if (canBeSingle && canBeDouble && (double.Parse(tmpFloat.ToString("R", CultureInfo.InvariantCulture), CultureInfo.InvariantCulture) != tmpDouble)) { // losing precision as single, so choose double return(ExpressionTokenKind.DoubleLiteral); } // 2. try most compatible -> least compatible if (canBeSingle) { return(ExpressionTokenKind.SingleLiteral); } if (canBeDouble) { return(ExpressionTokenKind.DoubleLiteral); } throw new ODataException(ODataErrorStrings.ExpressionLexer_InvalidNumericString(numericStr)); }
private static void ValidateTokenKind(ExpressionLexer lexer, ExpressionTokenKind kind) { Assert.Equal(kind, lexer.CurrentToken.Kind); lexer.NextToken(); }
/// <summary> /// Standard Constructor /// </summary> public ExpressionToken(string name, int priority, ExpressionTokenKind kind) { Name = name; Priority = priority; Kind = kind; }
/// <summary> /// Parses argument lists. /// </summary> /// <param name="splitToken">The token to split the argument list by.</param> /// <returns>The lexical tokens representing the arguments.</returns> private QueryToken[] ParseArgumentList(ExpressionTokenKind splitToken) { if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.OpenParen) { throw ParseError(Strings.UriQueryExpressionParser_OpenParenExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } this.lexer.NextToken(); QueryToken[] arguments = this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen ? this.ParseArguments(splitToken) : QueryToken.EmptyTokens; if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { throw ParseError(Strings.UriQueryExpressionParser_CloseParenOrCommaExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } this.lexer.NextToken(); return arguments; }
private static void ValidateTokenKind(ExpressionLexer lexer, ExpressionTokenKind kind) { lexer.CurrentToken.Kind.Should().Be(kind); lexer.NextToken(); }
/// <summary> /// Makes best guess on numeric string without trailing letter like L, F, M, D. /// </summary> /// <param name="numericStr">numeric string</param> /// <param name="guessedKind">The possbile kind (IntegerLiteral or DoubleLiteral) from ParseFromDigit() method.</param> /// <returns>ExpressionTokenKind</returns> private ExpressionTokenKind MakeBestGuessOnNoSuffixStr(string numericStr, ExpressionTokenKind guessedKind) { // no suffix, so // (1) make a best guess (note: later we support promoting each to later one: int32->int64->single->double->decimal). // look at value: "2147483647" may be Int32/long, "2147483649" must be long. // look at precision: "3258.67876576549" may be sinle/double/decimal, "3258.678765765489753678965390" must be decimal. // (2) then let MetadataUtilsCommon.CanConvertPrimitiveTypeTo() method does further promotion when knowing expected sematics type. int tmpInt = 0; long tmpLong = 0; float tmpFloat = 0; double tmpDouble = 0; decimal tmpDecimal = 0; if (guessedKind == ExpressionTokenKind.IntegerLiteral) { if (int.TryParse(numericStr, out tmpInt)) { return(ExpressionTokenKind.IntegerLiteral); } if (long.TryParse(numericStr, out tmpLong)) { return(ExpressionTokenKind.Int64Literal); } } bool canBeSingle = float.TryParse(numericStr, out tmpFloat); bool canBeDouble = double.TryParse(numericStr, out tmpDouble); bool canBeDecimal = decimal.TryParse(numericStr, out tmpDecimal); // 1. try high precision -> low precision if (canBeDouble && canBeDecimal) { decimal doubleToDecimalR; decimal doubleToDecimalN; bool doubleCanBeDecimalR = decimal.TryParse(tmpDouble.ToString("R", CultureInfo.InvariantCulture), out doubleToDecimalR); bool doubleCanBeDecimalN = decimal.TryParse(tmpDouble.ToString("N29", CultureInfo.InvariantCulture), out doubleToDecimalN); if ((doubleCanBeDecimalR && doubleToDecimalR != tmpDecimal) || (!doubleCanBeDecimalR && doubleCanBeDecimalN && doubleToDecimalN != tmpDecimal)) { // losing precision as double, so choose decimal return(ExpressionTokenKind.DecimalLiteral); } } // here can't use normal casting like the above double VS decimal. // e.g. for text "123.2", 123.2f should be == 123.2d, but normal casting (double)(123.2f) will return 123.19999694824219 thus becomes != 123.2d if (canBeSingle && canBeDouble && (double.Parse(tmpFloat.ToString("R", CultureInfo.InvariantCulture), CultureInfo.InvariantCulture) != tmpDouble)) { // losing precision as single, so choose double return(ExpressionTokenKind.DoubleLiteral); } // 2. try most compatible -> least compatible if (canBeSingle) { return(ExpressionTokenKind.SingleLiteral); } if (canBeDouble) { return(ExpressionTokenKind.DoubleLiteral); } throw new Microsoft.OData.ODataException(Strings.RequestQueryParser_InvalidNumericString(numericStr)); }
/// <summary>Whether the specified token identifier is a numeric literal.</summary> /// <param name="id">Token to check.</param> /// <returns>true if it's a numeric literal; false otherwise.</returns> internal static bool IsNumeric(ExpressionTokenKind id) { DebugUtils.CheckNoExternalCallers(); return id == ExpressionTokenKind.IntegerLiteral || id == ExpressionTokenKind.DecimalLiteral || id == ExpressionTokenKind.DoubleLiteral || id == ExpressionTokenKind.Int64Literal || id == ExpressionTokenKind.SingleLiteral; }
internal void ValidateToken(ExpressionTokenKind t) { if (this.token.Kind != t) { throw ParseError(Microsoft.Data.OData.Strings.ExpressionLexer_SyntaxError(this.textPos, this.text)); } }
/// <summary> /// Makes best guess on numeric string without trailing letter like L, F, M, D. /// </summary> /// <param name="numericStr">numeric string</param> /// <param name="guessedKind">The possbile kind (IntegerLiteral or DoubleLiteral) from ParseFromDigit() method.</param> /// <returns>ExpressionTokenKind</returns> private ExpressionTokenKind MakeBestGuessOnNoSuffixStr(string numericStr, ExpressionTokenKind guessedKind) { // no suffix, so // (1) make a best guess (note: later we support promoting each to later one: int32->int64->single->double->decimal). // look at value: "2147483647" may be Int32/long, "2147483649" must be long. // look at precision: "3258.67876576549" may be sinle/double/decimal, "3258.678765765489753678965390" must be decimal. // (2) then let MetadataUtilsCommon.CanConvertPrimitiveTypeTo() method does further promotion when knowing expected sematics type. int tmpInt = 0; long tmpLong = 0; float tmpFloat = 0; double tmpDouble = 0; decimal tmpDecimal = 0; if (guessedKind == ExpressionTokenKind.IntegerLiteral) { if (int.TryParse(numericStr, out tmpInt)) { return ExpressionTokenKind.IntegerLiteral; } if (long.TryParse(numericStr, out tmpLong)) { return ExpressionTokenKind.Int64Literal; } } bool canBeSingle = float.TryParse(numericStr, out tmpFloat); bool canBeDouble = double.TryParse(numericStr, out tmpDouble); bool canBeDecimal = decimal.TryParse(numericStr, out tmpDecimal); // 1. try high precision -> low precision if (canBeDouble && canBeDecimal) { decimal doubleToDecimalR; decimal doubleToDecimalN; bool doubleCanBeDecimalR = decimal.TryParse(tmpDouble.ToString("R", CultureInfo.InvariantCulture), out doubleToDecimalR); bool doubleCanBeDecimalN = decimal.TryParse(tmpDouble.ToString("N29", CultureInfo.InvariantCulture), out doubleToDecimalN); if ((doubleCanBeDecimalR && doubleToDecimalR != tmpDecimal) || (!doubleCanBeDecimalR && doubleCanBeDecimalN && doubleToDecimalN != tmpDecimal)) { // losing precision as double, so choose decimal return ExpressionTokenKind.DecimalLiteral; } } // here can't use normal casting like the above double VS decimal. // e.g. for text "123.2", 123.2f should be == 123.2d, but normal casting (double)(123.2f) will return 123.19999694824219 thus becomes != 123.2d if (canBeSingle && canBeDouble && (double.Parse(tmpFloat.ToString("R", CultureInfo.InvariantCulture), CultureInfo.InvariantCulture) != tmpDouble)) { // losing precision as single, so choose double return ExpressionTokenKind.DoubleLiteral; } // 2. try most compatible -> least compatible if (canBeSingle) { return ExpressionTokenKind.SingleLiteral; } if (canBeDouble) { return ExpressionTokenKind.DoubleLiteral; } throw new Microsoft.OData.Core.ODataException(Strings.RequestQueryParser_InvalidNumericString(numericStr)); }
internal static IEdmTypeReference GetLiteralEdmTypeReference(ExpressionTokenKind tokenKind) { switch (tokenKind) { case ExpressionTokenKind.BooleanLiteral: return EdmCoreModel.Instance.GetBoolean(false); case ExpressionTokenKind.DecimalLiteral: return EdmCoreModel.Instance.GetDecimal(false); case ExpressionTokenKind.StringLiteral: return EdmCoreModel.Instance.GetString(true); case ExpressionTokenKind.Int64Literal: return EdmCoreModel.Instance.GetInt64(false); case ExpressionTokenKind.IntegerLiteral: return EdmCoreModel.Instance.GetInt32(false); case ExpressionTokenKind.DoubleLiteral: return EdmCoreModel.Instance.GetDouble(false); case ExpressionTokenKind.SingleLiteral: return EdmCoreModel.Instance.GetSingle(false); case ExpressionTokenKind.GuidLiteral: return EdmCoreModel.Instance.GetGuid(false); case ExpressionTokenKind.BinaryLiteral: return EdmCoreModel.Instance.GetBinary(true); case ExpressionTokenKind.DateLiteral: return EdmCoreModel.Instance.GetDate(false); case ExpressionTokenKind.DateTimeOffsetLiteral: return EdmCoreModel.Instance.GetDateTimeOffset(false); case ExpressionTokenKind.DurationLiteral: return EdmCoreModel.Instance.GetTemporal(EdmPrimitiveTypeKind.Duration, false); case ExpressionTokenKind.GeographyLiteral: return EdmCoreModel.Instance.GetSpatial(EdmPrimitiveTypeKind.Geography, false); case ExpressionTokenKind.GeometryLiteral: return EdmCoreModel.Instance.GetSpatial(EdmPrimitiveTypeKind.Geometry, false); case ExpressionTokenKind.QuotedLiteral: return EdmCoreModel.Instance.GetString(true); case ExpressionTokenKind.TimeOfDayLiteral: return EdmCoreModel.Instance.GetTimeOfDay(false); } return null; }
/// <summary>Handles lexemes that are formed by an identifier followed by a quoted string.</summary> /// <remarks>This method modified the token field as necessary.</remarks> private void HandleTypePrefixedLiterals() { ExpressionTokenKind id = this.token.Kind; if (id != ExpressionTokenKind.Identifier) { return; } bool quoteFollows = this.ch == '\''; if (!quoteFollows) { return; } string tokenText = this.token.Text; if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixDateTime, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.DateTimeLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixDateTimeOffset, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.DateTimeOffsetLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixTime, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.TimeLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixGuid, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.GuidLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixBinary, StringComparison.OrdinalIgnoreCase) || String.Equals(tokenText, ExpressionConstants.LiteralPrefixShortBinary, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.BinaryLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixGeography, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.GeographyLiteral; } else if (String.Equals(tokenText, ExpressionConstants.LiteralPrefixGeometry, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.GeometryLiteral; } else if (this.parsingFunctionParameters && string.Equals(tokenText, ExpressionConstants.KeywordNull, StringComparison.OrdinalIgnoreCase)) { id = ExpressionTokenKind.NullLiteral; } else { return; } int tokenPos = this.token.Position; do { this.NextChar(); }while (this.ch.HasValue && this.ch != '\''); if (this.ch == null) { throw ParseError(ODataErrorStrings.ExpressionLexer_UnterminatedLiteral(this.textPos, this.text)); } this.NextChar(); this.token.Kind = id; this.token.Text = this.text.Substring(tokenPos, this.textPos - tokenPos); }