private bool ParseNumeric(int start) { if ((_expression.Length - _parsePoint) > 2 && _expression[_parsePoint] == '0' && (_expression[_parsePoint + 1] == 'x' || _expression[_parsePoint + 1] == 'X')) { // Hex number _parsePoint += 2; SkipHexDigits(); _lookahead = new Token(Token.TokenType.Numeric, _expression.Substring(start, _parsePoint - start)); } else if (CharacterUtilities.IsNumberStart(_expression[_parsePoint])) { // Decimal number if (_expression[_parsePoint] == '+') { _parsePoint++; } else if (_expression[_parsePoint] == '-') { _parsePoint++; } do { SkipDigits(); if (_parsePoint < _expression.Length && _expression[_parsePoint] == '.') { _parsePoint++; } if (_parsePoint < _expression.Length) { SkipDigits(); } } while (_parsePoint < _expression.Length && _expression[_parsePoint] == '.'); // Do we need to error on malformed input like 0.00.00)? or will the conversion handle it? // For now, let the conversion generate the error. _lookahead = new Token(Token.TokenType.Numeric, _expression.Substring(start, _parsePoint - start)); } else { // Unreachable _errorState = true; _errorPosition = start + 1; return false; } return true; }
internal bool IsNext(Token.TokenType type) { return _lookahead.IsToken(type); }
/// <summary> /// Parse any part of the conditional expression that is quoted. It may contain a property, item, or /// metadata element that needs expansion during evaluation. /// </summary> private bool ParseQuotedString() { _parsePoint++; int start = _parsePoint; bool expandable = false; while (_parsePoint < _expression.Length && _expression[_parsePoint] != '\'') { // Standalone percent-sign must be allowed within a condition because it's // needed to escape special characters. However, percent-sign followed // by open-parenthesis is an indication of an item metadata reference, and // that is only allowed in certain contexts. if ((_expression[_parsePoint] == '%') && ((_parsePoint + 1) < _expression.Length) && (_expression[_parsePoint + 1] == '(')) { expandable = true; string name = String.Empty; int endOfName = _expression.IndexOf(')', _parsePoint) - 1; if (endOfName < 0) { endOfName = _expression.Length - 1; } // If it's %(a.b) the name is just 'b' if (_parsePoint + 3 < _expression.Length) { name = _expression.Substring(_parsePoint + 2, (endOfName - _parsePoint - 2 + 1)); } if (!CheckForUnexpectedMetadata(name)) { return false; } } else if (_expression[_parsePoint] == '@' && ((_parsePoint + 1) < _expression.Length) && (_expression[_parsePoint + 1] == '(')) { expandable = true; // If the caller specified that he DOESN'T want to allow item lists ... if ((_options & ParserOptions.AllowItemLists) == 0) { _errorPosition = start + 1; _errorState = true; _errorResource = "ItemListNotAllowedInThisConditional"; return false; } // Item lists have to be parsed because of the replacement syntax e.g. @(Foo,'_'). // I have to know how to parse those so I can skip over the tic marks. I don't // have to do that with other things like propertygroups, hence itemlists are // treated specially. ParseInternalItemList(); continue; } else if (_expression[_parsePoint] == '$' && ((_parsePoint + 1) < _expression.Length) && (_expression[_parsePoint + 1] == '(')) { expandable = true; } else if (_expression[_parsePoint] == '%') { // There may be some escaped characters in the expression expandable = true; } _parsePoint++; } if (_parsePoint >= _expression.Length) { // Quoted string wasn't closed _errorState = true; _errorPosition = start; // The message is going to say "expected after position n" so don't add 1 here. _errorResource = "IllFormedQuotedStringInCondition"; // Not useful to set unexpectedlyFound here. By definition it got to the end of the string. return false; } string originalTokenString = _expression.Substring(start, _parsePoint - start); _lookahead = new Token(Token.TokenType.String, originalTokenString, expandable); _parsePoint++; return true; }
// There is a bug here that spaces are not required around 'and' and 'or'. For example, // this works perfectly well: // Condition="%(a.Identity)!=''and%(a.m)=='1'" // Since people now depend on this behavior, we must not change it. private bool ParseSimpleStringOrFunction(int start) { SkipSimpleStringChars(); if (0 == string.Compare(_expression.Substring(start, _parsePoint - start), "and", StringComparison.OrdinalIgnoreCase)) { _lookahead = Token.And; } else if (0 == string.Compare(_expression.Substring(start, _parsePoint - start), "or", StringComparison.OrdinalIgnoreCase)) { _lookahead = Token.Or; } else { int end = _parsePoint; SkipWhiteSpace(); if (_parsePoint < _expression.Length && _expression[_parsePoint] == '(') { _lookahead = new Token(Token.TokenType.Function, _expression.Substring(start, end - start)); } else { string tokenValue = _expression.Substring(start, end - start); _lookahead = new Token(Token.TokenType.String, tokenValue); } } return true; }
private bool ParseItemList() { int start = _parsePoint; if (!ParseInternalItemList()) { return false; } _lookahead = new Token(Token.TokenType.ItemList, _expression.Substring(start, _parsePoint - start)); return true; }
/// <summary> /// Parses a string of the form %(itemmetadataname). /// </summary> /// <returns></returns> private bool ParseItemMetadata() { string itemMetadataExpression = this.ParsePropertyOrItemMetadata(); if (itemMetadataExpression == null) { // The ParsePropertyOrItemMetadata method returns the correct error resources // for parsing properties such as $(propertyname). At this stage in the Whidbey // cycle, we're not allowed to add new string resources, so I can't add a new // resource specific to item metadata, so here, we just change the error to // the generic "UnexpectedCharacter". _errorResource = "UnexpectedCharacterInCondition"; return false; } _lookahead = new Token(Token.TokenType.ItemMetadata, itemMetadataExpression); if (!CheckForUnexpectedMetadata(itemMetadataExpression)) { return false; } return true; }
/// <summary> /// Parses a string of the form $(propertyname). /// </summary> /// <returns></returns> private bool ParseProperty() { string propertyExpression = this.ParsePropertyOrItemMetadata(); if (propertyExpression == null) { return false; } else { _lookahead = new Token(Token.TokenType.Property, propertyExpression); return true; } }
/// <summary> /// Advance /// returns true on successful advance /// and false on an erroneous token /// /// Doesn't return error until the bogus input is encountered. /// Advance() returns true even after EndOfInput is encountered. /// </summary> internal bool Advance() { if (_errorState) return false; if (_lookahead != null && _lookahead.IsToken(Token.TokenType.EndOfInput)) return true; SkipWhiteSpace(); // Update error position after skipping whitespace _errorPosition = _parsePoint + 1; if (_parsePoint >= _expression.Length) { _lookahead = Token.EndOfInput; } else { switch (_expression[_parsePoint]) { case ',': _lookahead = Token.Comma; _parsePoint++; break; case '(': _lookahead = Token.LeftParenthesis; _parsePoint++; break; case ')': _lookahead = Token.RightParenthesis; _parsePoint++; break; case '$': if (!ParseProperty()) return false; break; case '%': if (!ParseItemMetadata()) return false; break; case '@': int start = _parsePoint; // If the caller specified that he DOESN'T want to allow item lists ... if ((_options & ParserOptions.AllowItemLists) == 0) { if ((_parsePoint + 1) < _expression.Length && _expression[_parsePoint + 1] == '(') { _errorPosition = start + 1; _errorState = true; _errorResource = "ItemListNotAllowedInThisConditional"; return false; } } if (!ParseItemList()) return false; break; case '!': // negation and not-equal if ((_parsePoint + 1) < _expression.Length && _expression[_parsePoint + 1] == '=') { _lookahead = Token.NotEqualTo; _parsePoint += 2; } else { _lookahead = Token.Not; _parsePoint++; } break; case '>': // gt and gte if ((_parsePoint + 1) < _expression.Length && _expression[_parsePoint + 1] == '=') { _lookahead = Token.GreaterThanOrEqualTo; _parsePoint += 2; } else { _lookahead = Token.GreaterThan; _parsePoint++; } break; case '<': // lt and lte if ((_parsePoint + 1) < _expression.Length && _expression[_parsePoint + 1] == '=') { _lookahead = Token.LessThanOrEqualTo; _parsePoint += 2; } else { _lookahead = Token.LessThan; _parsePoint++; } break; case '=': if ((_parsePoint + 1) < _expression.Length && _expression[_parsePoint + 1] == '=') { _lookahead = Token.EqualTo; _parsePoint += 2; } else { _errorPosition = _parsePoint + 2; // expression[parsePoint + 1], counting from 1 _errorResource = "IllFormedEqualsInCondition"; if ((_parsePoint + 1) < _expression.Length) { // store the char we found instead _unexpectedlyFound = Convert.ToString(_expression[_parsePoint + 1], CultureInfo.InvariantCulture); } else { _unexpectedlyFound = EndOfInput; } _parsePoint++; _errorState = true; return false; } break; case '\'': if (!ParseQuotedString()) return false; break; default: // Simple strings, function calls, decimal numbers, hex numbers if (!ParseRemaining()) return false; break; } } return true; }
private bool Same(string expression, Token.TokenType token) { if (_lexer.IsNext(token)) { if (!_lexer.Advance()) { errorPosition = _lexer.GetErrorPosition(); if (null != _lexer.UnexpectedlyFound) { ProjectErrorUtilities.VerifyThrowInvalidProject(false, _elementLocation, _lexer.GetErrorResource(), expression, errorPosition, _lexer.UnexpectedlyFound); } else { ProjectErrorUtilities.VerifyThrowInvalidProject(false, _elementLocation, _lexer.GetErrorResource(), expression, errorPosition); } } return true; } else { return false; } }