Esempio n. 1
0
        private static void AppendStringDictValue(ParsedAttribute attribute, CodeObjectCreateExpression newKeyValueExpression)
        {
            var expressionStringParser = new ExpressionStringParser(attribute.Value);

            expressionStringParser.Parse();
            var values = expressionStringParser.ExpressionStringTokens;

            if (values.Count == 1)
            {
                var expressionStringToken = values[0];
                if (expressionStringToken.IsExpression)
                {
                    newKeyValueExpression.Parameters.Add(new CodeSnippetExpression
                    {
                        Value = expressionStringToken.Value
                    });
                }
                else
                {
                    newKeyValueExpression.Parameters.Add(new CodePrimitiveExpression
                    {
                        Value = expressionStringToken.Value
                    });
                }
            }
            else
            {
                var concatExpression = CodeDomClassBuilder.GetConcatExpression(values);
                newKeyValueExpression.Parameters.Add(concatExpression);
            }
        }
 public UnsupportedValue(KAOSCoreElement element, ParsedAttribute attribute, object value)
     : base(string.Format("'{0}' is not supported in '{1}' on '{2}'",
                          value?.GetType()?.Name,
                          attribute?.GetType()?.Name,
                          element?.GetType()?.Name), attribute)
 {
 }
Esempio n. 3
0
        /// <summary>
        /// Parses the next attribute.
        /// </summary>
        /// <param name="headerData">The header data.</param>
        /// <param name="lastAttribute">The last attribute.</param>
        /// <returns>THe parsed attribute.</returns>
        private static ParsedAttribute ParseNextAttribute(string headerData, ParsedAttribute lastAttribute)
        {
            char c;
            var  startIndex          = lastAttribute?.EndIndex ?? 0;
            var  attributePivotIndex = headerData.IndexOf("=\"", startIndex, StringComparison.Ordinal);
            var  attributeStartIndex = -1;
            var  attributeEndIndex   = -1;

            // Find the attribute name
            for (var i = attributePivotIndex - 1; i >= 0; i--)
            {
                c = headerData[i];
                if (!char.IsWhiteSpace(c))
                {
                    continue;
                }
                attributeStartIndex = i + 1;
                break;
            }

            // find the attribute value
            if (attributeStartIndex >= 0)
            {
                for (var i = attributePivotIndex + 2; i < headerData.Length; i++)
                {
                    c = headerData[i];
                    if (c != '"')
                    {
                        continue;
                    }
                    attributeEndIndex = i;
                    break;
                }

                if (attributeEndIndex == -1)
                {
                    attributeEndIndex = headerData.Length - 1;
                }
            }

            if (attributeStartIndex == -1)
            {
                return(null);
            }

            return(new ParsedAttribute
            {
                Key = HttpUtility.UrlDecode(headerData.Substring(attributeStartIndex, attributePivotIndex - attributeStartIndex)),
                Value = HttpUtility.UrlDecode(headerData.Substring(attributePivotIndex + 2, attributeEndIndex - attributePivotIndex - 2)),
                EndIndex = attributeEndIndex,
                StartIndex = attributeStartIndex,
                Substring = headerData.Substring(attributeStartIndex, attributeEndIndex - attributeStartIndex + 1)
            });
        }
Esempio n. 4
0
        /// <summary>
        /// Parses the next attribute.
        /// </summary>
        /// <param name="headerData">The header data.</param>
        /// <param name="lastAttribute">The last attribute.</param>
        /// <returns>THe parsed attribute</returns>
        private static ParsedAttribute ParseNextAttribute(string headerData, ParsedAttribute lastAttribute)
        {
            var c                   = default(char);
            var nc                  = default(char);
            var startIndex          = lastAttribute == null ? 0 : lastAttribute.EndIndex;
            var attributePivotIndex = headerData.IndexOf("=\"", startIndex);
            var attributeStartIndex = -1;
            var attributeEndIndex   = -1;

            for (var i = attributePivotIndex - 1; i >= 0; i--)
            {
                c = headerData[i];
                if (char.IsWhiteSpace(c))
                {
                    attributeStartIndex = i + 1;
                    break;
                }
            }

            if (attributeStartIndex >= 0)
            {
                for (var i = attributePivotIndex + 2; i < headerData.Length; i++)
                {
                    c  = headerData[i];
                    nc = i + 1 < headerData.Length ? headerData[i + 1] : default(char);
                    if (c == '"' && nc != '"')
                    {
                        attributeEndIndex = i;
                        break;
                    }
                }

                if (attributeEndIndex == -1)
                {
                    attributeEndIndex = headerData.Length - 1;
                }
            }

            if (attributeStartIndex == -1)
            {
                return(null);
            }

            return(new ParsedAttribute
            {
                Key = headerData.Substring(attributeStartIndex, attributePivotIndex - attributeStartIndex),
                Value = headerData.Substring(attributePivotIndex + 1, attributeEndIndex - attributePivotIndex),
                EndIndex = attributeEndIndex,
                StartIndex = attributeStartIndex,
                Substring = headerData.Substring(attributeStartIndex, attributeEndIndex - attributeStartIndex + 1)
            });
        }
Esempio n. 5
0
        /// <summary>
        /// Parse the next element
        /// </summary>
        public ParsedToken Parse()
        {
            CharInfo c;

            // If end of stream when stop here
            if (EOF)
            {
                return(null);
            }
            // Current token defined
            if (_CurrentToken != null)
            {
                // End tag out Doctype : error while end tag or doctype parsing, reset parser
                if (_CurrentToken.TokenType == ParsedTokenType.EndTag || _CurrentToken.TokenType == ParsedTokenType.Doctype)
                {
                    _CurrentRead = null;
                    _State       = ParseState.Content;
                    ResetTagBuffer();
                    LastParsed    = _CurrentToken;
                    _CurrentToken = null;
                    return(LastParsed);
                }
            }
            // Current Attribute defined
            if (_CurrentAttr != null)
            {
                LastParsed   = _CurrentAttr;
                _CurrentAttr = null;
                return(LastParsed);
            }
            // Current Read not empty ?
            if (_CurrentRead != null)
            {
                bool returnLast = true;
                switch (_State)
                {
                // Returns a non closed comment
                case ParseState.Comment:
                    String comment = GetCurrentRead(true);
                    comment    = comment.Substring(4).TrimStart();
                    LastParsed = new ParsedComment()
                    {
                        Position = _CurrentPosition,
                        Text     = HEntity.HtmlDecode(comment, RemoveUnknownOrInvalidEntities)
                    };
                    break;

                // Returns a text
                case ParseState.Content:
                    LastParsed = new ParsedText()
                    {
                        Position = _CurrentPosition,
                        Text     = HEntity.HtmlDecode(GetCurrentRead(true), RemoveUnknownOrInvalidEntities)
                    };
                    break;

                // Returns a text
                case ParseState.Doctype:
                case ParseState.ProcessInstruction:
                case ParseState.EndTag:
                case ParseState.Tag:
                    //LastParsed = new ParsedText() {
                    //    Position = _CurrentPosition,
                    //    Text = GetCurrentRead(true)
                    //};
                    _State     = ParseState.Content;
                    LastParsed = ParseText();
                    break;
                    // We forget the result
                    //default:
                    //    returnLast = false;
                    //    break;
                }
                _State = ParseState.Content;
                ResetTagBuffer();
                if (returnLast)
                {
                    return(LastParsed);
                }
            }
            // Read loop
            while (true)
            {
                // Read next char
                c = ReadChar();
                // EOF ?
                if (c == CharInfo.EOF)
                {
                    // Check unexpected EOF
                    if (_State != ParseState.Content)
                    {
                        _State = ParseState.Content;
                        ResetTagBuffer();
                        throw new ParseError("End of file unexpected.", ReadPosition);
                    }
                    // Stop the parsing
                    LastParsed = null;
                    EOF        = true;
                    return(null);
                }
                // Other case
                switch (_State)
                {
                // In text
                case ParseState.Content:
                    if (c == '<')
                    {
                        LastParsed = ParseStartTag();
                    }
                    else
                    {
                        LastParsed = ParseText();
                    }
                    return(LastParsed);

                // In tag or process instruction
                case ParseState.Tag:
                case ParseState.Doctype:
                case ParseState.ProcessInstruction:
                    SaveChar(c);
                    LastParsed = ParseInTag();
                    return(LastParsed);

                default:
                    break;
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Parse in tag
        /// </summary>
        protected ParsedToken ParseInTag()
        {
            _CurrentRead = null;
            // Whitespaces
            CharInfo c;

            while ((c = ReadChar(false)) != CharInfo.EOF && Char.IsWhiteSpace(c.AsChar))
            {
                ;
            }
            var cpos = c.Position;

            // EOF ?
            if (c == CharInfo.EOF)
            {
                _CurrentToken = null;
                _State        = ParseState.Content;
                ResetTagBuffer();
                throw new ParseError("Unexpected end of file. Tag not closed.", ReadPosition);
            }
            // End of auto closed tag ?
            else if (c == '/' && _State == ParseState.Tag)
            {
                CharInfo saveSlash = c;
                bool     spaces    = false;
                while ((c = ReadChar(false)) != CharInfo.EOF && Char.IsWhiteSpace(c.AsChar))
                {
                    spaces = true;
                }
                if (c != '>')
                {
                    // Prepare a correct next tag
                    SaveChar(new CharInfo('>', (c == CharInfo.EOF) ? saveSlash.Position : c.Position));
                    SaveChar(saveSlash);
                    throw new ParseError("Invalid char after '/'. End of auto closed tag expected.", cpos);
                }
                if (spaces)
                {
                    // Prepare a correct next tag
                    SaveChar(c);
                    SaveChar(saveSlash);
                    // Raise the error
                    throw new ParseError("Invalid auto closed tag, '/' need to be follow by '>'.", cpos);
                }
                // Returns autoclosed
                var result = ParsedTag.AutoClosedTag(((ParsedTag)_CurrentToken).TagName);
                result.Position = cpos;
                _CurrentToken   = null;
                _CurrentRead    = null;
                _State          = ParseState.Content;
                ResetTagBuffer();
                return(result);
            }
            // End of process instruction
            else if (c == '?' && _State == ParseState.ProcessInstruction)
            {
                c = ReadChar(false);
                if (c != '>')
                {
                    throw new ParseError("Invalid char after '?'. End of process instruction expected.", cpos);
                }
                // Returns processinstruction
                var result = ParsedTag.CloseProcessInstruction(((ParsedTag)_CurrentToken).TagName);
                result.Position = cpos;
                _CurrentToken   = null;
                _CurrentRead    = null;
                _State          = ParseState.Content;
                ResetTagBuffer();
                return(result);
            }
            else if (c == '>')
            {
                // Check tag
                if (_State == ParseState.ProcessInstruction)
                {
                    throw new ParseError("A process instruction need to be closed with '?>'.", cpos);
                }
                // Returns close
                var result = ParsedTag.CloseTag(((ParsedTag)_CurrentToken).TagName);
                result.Position = cpos;
                _CurrentToken   = null;
                _CurrentRead    = null;
                _State          = ParseState.Content;
                ResetTagBuffer();
                return(result);
            }
            // Get the attribute name
            if (!IsAttributeNameChar(c.AsChar))
            {
                throw new ParseError("Unexpected character.", cpos);
            }
            AddToCurrentRead(c);
            while ((c = ReadChar(false)) != CharInfo.EOF && IsAttributeNameChar(c.AsChar))
            {
                AddToCurrentRead(c);
            }
            if (c != CharInfo.EOF)
            {
                SaveChar(c);
            }
            String        attrName = GetCurrentRead(true);
            ParsePosition attrPos  = cpos;

            // Whitespaces
            while ((c = ReadChar(false)) != CharInfo.EOF && Char.IsWhiteSpace(c.AsChar))
            {
                ;
            }
            // Attribute whithout value
            if (c != '=')
            {
                SaveChar(c);
                // Attribute whithout content
                return(new ParsedAttribute()
                {
                    Position = attrPos,
                    Name = attrName,
                    Value = null,
                    Quote = '\0'
                });
            }
            // Whitespaces
            while ((c = ReadChar(false)) != CharInfo.EOF && Char.IsWhiteSpace(c.AsChar))
            {
                ;
            }
            // Search the value
            if (c == 0 || c == '/' || c == '?' || c == '>')
            {
                _CurrentAttr = new ParsedAttribute()
                {
                    Position = attrPos,
                    Name     = attrName,
                    Value    = null,
                    Quote    = '\0'
                };
                if (c != CharInfo.EOF)
                {
                    SaveChar(c);
                }
                throw new ParseError("Attribute value expected.", ReadPosition);
            }
            // Quoted value ?
            _CurrentRead = null;
            char quote = '\0';

            if (c == '"' || c == '\'')
            {
                quote = c.AsChar;
                while ((c = ReadChar(false)) != CharInfo.EOF && c != quote)
                {
                    AddToCurrentRead(c);
                }
                _CurrentAttr = new ParsedAttribute()
                {
                    Position = attrPos,
                    Name     = attrName,
                    Value    = HEntity.HtmlDecode(GetCurrentRead(true), RemoveUnknownOrInvalidEntities),
                    Quote    = quote
                };
                if (c == CharInfo.EOF)
                {
                    throw new ParseError("Unexpected end of file. Attribute is not closed.", ReadPosition);
                }
                var result = _CurrentAttr;
                _CurrentAttr = null;
                return(result);
            }
            // Unquoted value
            AddToCurrentRead(c);
            while ((c = ReadChar(false)) != CharInfo.EOF && !Char.IsWhiteSpace(c.AsChar) && c != '"' && c != '\'' && c != '=' && c != '<' && c != '>' && c != '`')
            {
                AddToCurrentRead(c);
            }
            SaveChar(c);
            return(new ParsedAttribute()
            {
                Position = attrPos,
                Name = attrName,
                Value = HEntity.HtmlDecode(GetCurrentRead(true), RemoveUnknownOrInvalidEntities),
                Quote = quote
            });
        }
Esempio n. 7
0
        public void TestParse_TagAttribute()
        {
            // Attributes
            StringReader reader = new StringReader("<div attr1  attr2 = value2 attr3=\"val&amp;ue&test;\" attr4 = 'value' >");
            HParser      parser = new HParser(reader);

            var pres = parser.Parse();

            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(0, 0, 0), pres.Position);
            Assert.Equal(new ParsePosition(4, 0, 4), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.OpenTag, pres.TokenType);
            Assert.Equal("div", ((ParsedTag)pres).TagName);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(5, 0, 5), pres.Position);
            Assert.Equal(new ParsePosition(12, 0, 12), parser.ReadPosition);
            ParsedAttribute pAttr = (ParsedAttribute)pres;

            Assert.Equal("attr1", pAttr.Name);
            Assert.Equal(null, pAttr.Value);
            Assert.Equal('\0', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(12, 0, 12), pres.Position);
            Assert.Equal(new ParsePosition(26, 0, 26), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.Attribute, pres.TokenType);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr2", pAttr.Name);
            Assert.Equal("value2", pAttr.Value);
            Assert.Equal('\0', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(27, 0, 27), pres.Position);
            Assert.Equal(new ParsePosition(51, 0, 51), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr3", pAttr.Name);
            Assert.Equal("val&ue&test;", pAttr.Value);
            Assert.Equal('"', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(52, 0, 52), pres.Position);
            Assert.Equal(new ParsePosition(67, 0, 67), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr4", pAttr.Name);
            Assert.Equal("value", pAttr.Value);
            Assert.Equal('\'', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(68, 0, 68), pres.Position);
            Assert.Equal(new ParsePosition(69, 0, 69), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.CloseTag, pres.TokenType);

            pres = parser.Parse();
            Assert.Equal(new ParsePosition(69, 0, 69), parser.ReadPosition);
            Assert.Null(pres);
            Assert.True(parser.EOF);

            // Attribute with remove entities option
            reader = new StringReader("<div attr1  attr2 = value2 attr3=\"val&amp;ue&test;\" attr4 = 'value' >");
            parser = new HParser(reader);
            parser.RemoveUnknownOrInvalidEntities = true;

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(0, 0, 0), pres.Position);
            Assert.Equal(new ParsePosition(4, 0, 4), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.OpenTag, pres.TokenType);
            Assert.Equal("div", ((ParsedTag)pres).TagName);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(5, 0, 5), pres.Position);
            Assert.Equal(new ParsePosition(12, 0, 12), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr1", pAttr.Name);
            Assert.Equal(null, pAttr.Value);
            Assert.Equal('\0', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(12, 0, 12), pres.Position);
            Assert.Equal(new ParsePosition(26, 0, 26), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.Attribute, pres.TokenType);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr2", pAttr.Name);
            Assert.Equal("value2", pAttr.Value);
            Assert.Equal('\0', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(27, 0, 27), pres.Position);
            Assert.Equal(new ParsePosition(51, 0, 51), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr3", pAttr.Name);
            Assert.Equal("val&ue", pAttr.Value);
            Assert.Equal('"', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(52, 0, 52), pres.Position);
            Assert.Equal(new ParsePosition(67, 0, 67), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr4", pAttr.Name);
            Assert.Equal("value", pAttr.Value);
            Assert.Equal('\'', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(68, 0, 68), pres.Position);
            Assert.Equal(new ParsePosition(69, 0, 69), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.CloseTag, pres.TokenType);

            pres = parser.Parse();
            Assert.Equal(new ParsePosition(69, 0, 69), parser.ReadPosition);
            Assert.Null(pres);
            Assert.True(parser.EOF);

            // Attribute without value
            reader = new StringReader("<div attr1 = >");
            parser = new HParser(reader);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(0, 0, 0), pres.Position);
            Assert.Equal(new ParsePosition(4, 0, 4), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.OpenTag, pres.TokenType);
            Assert.Equal("div", ((ParsedTag)pres).TagName);

            var pex = Assert.Throws <ParseError>(() => parser.Parse());

            Assert.Equal("Attribute value expected.", pex.Message);
            Assert.Equal(new ParsePosition(13, 0, 13), pex.Position);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(5, 0, 5), pres.Position);
            Assert.Equal(new ParsePosition(13, 0, 13), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr1", pAttr.Name);
            Assert.Equal(null, pAttr.Value);
            Assert.Equal('\0', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(13, 0, 13), pres.Position);
            Assert.Equal(new ParsePosition(14, 0, 14), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.CloseTag, pres.TokenType);

            pres = parser.Parse();
            Assert.Equal(new ParsePosition(14, 0, 14), parser.ReadPosition);
            Assert.Null(pres);
            Assert.True(parser.EOF);

            // Attribute with value unclosed
            reader = new StringReader("<div attr1 = \"value ><div attr2=\"value\" >");
            parser = new HParser(reader);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(0, 0, 0), pres.Position);
            Assert.Equal(new ParsePosition(4, 0, 4), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.OpenTag, pres.TokenType);
            Assert.Equal("div", ((ParsedTag)pres).TagName);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(5, 0, 5), pres.Position);
            Assert.Equal(new ParsePosition(33, 0, 33), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr1", pAttr.Name);
            Assert.Equal("value ><div attr2=", pAttr.Value);
            Assert.Equal('"', pAttr.Quote);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(33, 0, 33), pres.Position);
            Assert.Equal(new ParsePosition(38, 0, 38), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("value", pAttr.Name);
            Assert.Equal(null, pAttr.Value);
            Assert.Equal('\0', pAttr.Quote);

            pex = Assert.Throws <ParseError>(() => parser.Parse());
            Assert.Equal("Unexpected character.", pex.Message);
            Assert.Equal(new ParsePosition(38, 0, 38), pex.Position);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(40, 0, 40), pres.Position);
            Assert.Equal(new ParsePosition(41, 0, 41), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.CloseTag, pres.TokenType);

            pres = parser.Parse();
            Assert.Equal(new ParsePosition(41, 0, 41), parser.ReadPosition);
            Assert.Null(pres);
            Assert.True(parser.EOF);

            // Attribute with end of file
            reader = new StringReader("<div attr1 = \"value ");
            parser = new HParser(reader);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(0, 0, 0), pres.Position);
            Assert.Equal(new ParsePosition(4, 0, 4), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.OpenTag, pres.TokenType);
            Assert.Equal("div", ((ParsedTag)pres).TagName);

            pex = Assert.Throws <ParseError>(() => parser.Parse());
            Assert.Equal("Unexpected end of file. Attribute is not closed.", pex.Message);
            Assert.Equal(new ParsePosition(20, 0, 20), pex.Position);

            pres = parser.Parse();
            Assert.IsType <ParsedAttribute>(pres);
            Assert.Equal(new ParsePosition(5, 0, 5), pres.Position);
            Assert.Equal(new ParsePosition(20, 0, 20), parser.ReadPosition);
            pAttr = (ParsedAttribute)pres;
            Assert.Equal("attr1", pAttr.Name);
            Assert.Equal("value ", pAttr.Value);
            Assert.Equal('"', pAttr.Quote);

            pex = Assert.Throws <ParseError>(() => parser.Parse());
            Assert.Equal("End of file unexpected.", pex.Message);
            Assert.Equal(new ParsePosition(20, 0, 20), pex.Position);

            pres = parser.Parse();
            Assert.Equal(new ParsePosition(20, 0, 20), parser.ReadPosition);
            Assert.Null(pres);
            Assert.True(parser.EOF);

            // Invalid Attribute name
            reader = new StringReader("<div ' >");
            parser = new HParser(reader);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(0, 0, 0), pres.Position);
            Assert.Equal(new ParsePosition(4, 0, 4), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.OpenTag, pres.TokenType);
            Assert.Equal("div", ((ParsedTag)pres).TagName);

            pex = Assert.Throws <ParseError>(() => parser.Parse());
            Assert.Equal("Unexpected character.", pex.Message);
            Assert.Equal(new ParsePosition(5, 0, 5), pex.Position);

            pres = parser.Parse();
            Assert.IsType <ParsedTag>(pres);
            Assert.Equal(new ParsePosition(7, 0, 7), pres.Position);
            Assert.Equal(new ParsePosition(8, 0, 8), parser.ReadPosition);
            Assert.Equal(ParsedTokenType.CloseTag, pres.TokenType);

            pres = parser.Parse();
            Assert.Equal(new ParsePosition(8, 0, 8), parser.ReadPosition);
            Assert.Null(pres);
            Assert.True(parser.EOF);
        }
    	private static void AppendStringDictValue(ParsedAttribute attribute, CodeObjectCreateExpression newKeyValueExpression)
    	{
    		var expressionStringParser = new ExpressionStringParser(attribute.Value);

    		expressionStringParser.Parse();
            var values = expressionStringParser.ExpressionStringTokens;
    		if (values.Count == 1)
    		{
    			var expressionStringToken = values[0];
    			if (expressionStringToken.IsExpression)
    			{
    				newKeyValueExpression.Parameters.Add(new CodeSnippetExpression
    				                                     	{
    				                                     		Value = expressionStringToken.Value

    				                                     	});
    			}
    			else
    			{

    				newKeyValueExpression.Parameters.Add(new CodePrimitiveExpression
    				                                     	{
    				                                     		Value = expressionStringToken.Value

    				                                     	});
    			}
    		}
    		else
    		{
    			var concatExpression = CodeDomClassBuilder.GetConcatExpression(values);
    			newKeyValueExpression.Parameters.Add(concatExpression);
    		}
    	}