7 changed files with 329 additions and 3 deletions
@ -0,0 +1,140 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System.Linq; |
|||
using Perspex.Markup.Xaml.Parsers; |
|||
using Sprache; |
|||
using Xunit; |
|||
|
|||
namespace Perspex.Xaml.Base.UnitTest.Parsers |
|||
{ |
|||
public class SelectorGrammarTests |
|||
{ |
|||
[Fact] |
|||
public void OfType() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse("Button").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button" } }, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Name() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse("#foo").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new[] { new SelectorGrammar.NameSyntax { Name = "foo" }, }, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void OfType_Name() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse("Button#foo").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new SelectorGrammar.ISyntax[] |
|||
{ |
|||
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, |
|||
new SelectorGrammar.NameSyntax { Name = "foo" }, |
|||
}, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Class() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse(".foo").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new[] { new SelectorGrammar.ClassSyntax { Class = "foo" } }, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Pseudoclass() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse(":foo").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new[] { new SelectorGrammar.ClassSyntax { Class = ":foo" } }, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void OfType_Class() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse("Button.foo").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new SelectorGrammar.ISyntax[] |
|||
{ |
|||
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, |
|||
new SelectorGrammar.ClassSyntax { Class = "foo" }, |
|||
}, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void OfType_Child_Class() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse("Button < .foo").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new SelectorGrammar.ISyntax[] |
|||
{ |
|||
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, |
|||
new SelectorGrammar.ChildSyntax { }, |
|||
new SelectorGrammar.ClassSyntax { Class = "foo" }, |
|||
}, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void OfType_Descendent_Class() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse("Button .foo").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new SelectorGrammar.ISyntax[] |
|||
{ |
|||
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, |
|||
new SelectorGrammar.DescendentSyntax { }, |
|||
new SelectorGrammar.ClassSyntax { Class = "foo" }, |
|||
}, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void OfType_Template_Class() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse("Button /template/ .foo").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new SelectorGrammar.ISyntax[] |
|||
{ |
|||
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, |
|||
new SelectorGrammar.TemplateSyntax { }, |
|||
new SelectorGrammar.ClassSyntax { Class = "foo" }, |
|||
}, |
|||
result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void OfType_Property() |
|||
{ |
|||
var result = SelectorGrammar.Selector.Parse("Button[Foo=bar]").ToList(); |
|||
|
|||
Assert.Equal( |
|||
new SelectorGrammar.ISyntax[] |
|||
{ |
|||
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, |
|||
new SelectorGrammar.PropertySyntax { Property = "Foo", Value = "bar" }, |
|||
}, |
|||
result); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,176 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using Perspex.Styling; |
|||
using Sprache; |
|||
|
|||
namespace Perspex.Markup.Xaml.Parsers |
|||
{ |
|||
internal class SelectorGrammar |
|||
{ |
|||
public static readonly Parser<char> CombiningCharacter = Parse.Char( |
|||
c => |
|||
{ |
|||
var cat = CharUnicodeInfo.GetUnicodeCategory(c); |
|||
return cat == UnicodeCategory.NonSpacingMark || |
|||
cat == UnicodeCategory.SpacingCombiningMark; |
|||
}, |
|||
"Connecting Character"); |
|||
|
|||
public static readonly Parser<char> ConnectingCharacter = Parse.Char( |
|||
c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation, |
|||
"Connecting Character"); |
|||
|
|||
public static readonly Parser<char> FormattingCharacter = Parse.Char( |
|||
c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Format, |
|||
"Connecting Character"); |
|||
|
|||
public static readonly Parser<char> IdentifierStart = Parse.Letter.Or(Parse.Char('_')); |
|||
|
|||
public static readonly Parser<char> IdentifierChar = Parse |
|||
.LetterOrDigit |
|||
.Or(ConnectingCharacter) |
|||
.Or(CombiningCharacter) |
|||
.Or(FormattingCharacter); |
|||
|
|||
public static readonly Parser<string> Identifier = |
|||
from start in IdentifierStart.Once().Text() |
|||
from @char in IdentifierChar.Many().Text() |
|||
select start + @char; |
|||
|
|||
public static readonly Parser<OfTypeSyntax> OfType = |
|||
from identifier in Identifier select new OfTypeSyntax { TypeName = identifier }; |
|||
|
|||
public static readonly Parser<NameSyntax> Name = |
|||
from hash in Parse.Char('#') |
|||
from identifier in Identifier |
|||
select new NameSyntax { Name = identifier }; |
|||
|
|||
public static readonly Parser<char> ClassStart = Parse.Char('_').Or(Parse.Letter); |
|||
|
|||
public static readonly Parser<char> ClassChar = ClassStart.Or(Parse.Numeric); |
|||
|
|||
public static readonly Parser<string> ClassIdentifier = |
|||
from start in ClassStart.Once().Text() |
|||
from @char in ClassChar.Many().Text() |
|||
select start + @char; |
|||
|
|||
public static readonly Parser<ClassSyntax> StandardClass = |
|||
from dot in Parse.Char('.').Once() |
|||
from identifier in ClassIdentifier |
|||
select new ClassSyntax { Class = identifier }; |
|||
|
|||
public static readonly Parser<ClassSyntax> Pseduoclass = |
|||
from colon in Parse.Char(':').Once() |
|||
from identifier in ClassIdentifier |
|||
select new ClassSyntax { Class = ':' + identifier }; |
|||
|
|||
public static readonly Parser<ClassSyntax> Class = StandardClass.Or(Pseduoclass); |
|||
|
|||
public static readonly Parser<PropertySyntax> Property = |
|||
from open in Parse.Char('[').Once() |
|||
from identifier in Identifier |
|||
from eq in Parse.Char('=').Once() |
|||
from value in Parse.CharExcept(']').Many().Text() |
|||
from close in Parse.Char(']').Once() |
|||
select new PropertySyntax { Property = identifier, Value = value }; |
|||
|
|||
public static readonly Parser<ChildSyntax> Child = Parse.Char('<').Token().Return(new ChildSyntax()); |
|||
|
|||
public static readonly Parser<DescendentSyntax> Descendent = |
|||
from child in Parse.WhiteSpace.Many() |
|||
select new DescendentSyntax(); |
|||
|
|||
public static readonly Parser<TemplateSyntax> Template = |
|||
from template in Parse.String("/template/").Token() |
|||
select new TemplateSyntax(); |
|||
|
|||
//public static readonly
|
|||
|
|||
public static readonly Parser<ISyntax> SingleSelector = |
|||
OfType |
|||
.Or<ISyntax>(Name) |
|||
.Or<ISyntax>(Class) |
|||
.Or<ISyntax>(Property) |
|||
.Or<ISyntax>(Child) |
|||
.Or<ISyntax>(Template) |
|||
.Or<ISyntax>(Descendent); |
|||
|
|||
public static readonly Parser<IEnumerable<ISyntax>> Selector = SingleSelector.Many(); |
|||
|
|||
public interface ISyntax |
|||
{ |
|||
} |
|||
|
|||
public class OfTypeSyntax : ISyntax |
|||
{ |
|||
public string TypeName { get; set; } |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
return obj is OfTypeSyntax && ((OfTypeSyntax)obj).TypeName == TypeName; |
|||
} |
|||
} |
|||
|
|||
public class ClassSyntax : ISyntax |
|||
{ |
|||
public string Class { get; set; } |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
return obj is ClassSyntax && ((ClassSyntax)obj).Class == Class; |
|||
} |
|||
} |
|||
|
|||
public class NameSyntax : ISyntax |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
return obj is NameSyntax && ((NameSyntax)obj).Name == Name; |
|||
} |
|||
} |
|||
|
|||
public class PropertySyntax : ISyntax |
|||
{ |
|||
public string Property { get; set; } |
|||
|
|||
public string Value { get; set; } |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
return obj is PropertySyntax && |
|||
((PropertySyntax)obj).Property == Property && |
|||
((PropertySyntax)obj).Value == Value; |
|||
} |
|||
} |
|||
|
|||
public class ChildSyntax : ISyntax |
|||
{ |
|||
public override bool Equals(object obj) |
|||
{ |
|||
return obj is ChildSyntax; |
|||
} |
|||
} |
|||
|
|||
public class DescendentSyntax : ISyntax |
|||
{ |
|||
public override bool Equals(object obj) |
|||
{ |
|||
return obj is DescendentSyntax; |
|||
} |
|||
} |
|||
|
|||
public class TemplateSyntax : ISyntax |
|||
{ |
|||
public override bool Equals(object obj) |
|||
{ |
|||
return obj is TemplateSyntax; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue