Browse Source

Started implementing Selector parser.

Steven Kirk 10 years ago
parent
commit
eb220a9388

+ 140 - 0
Tests/Perspex.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs

@@ -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);
+        }
+    }
+}

+ 7 - 2
Tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj

@@ -8,8 +8,8 @@
     <ProjectGuid>{99135EAB-653D-47E4-A378-C96E1278CA44}</ProjectGuid>
     <OutputType>Library</OutputType>
     <AppDesignerFolder>Properties</AppDesignerFolder>
-    <RootNamespace>Perspex.Xaml.Base.UnitTest</RootNamespace>
-    <AssemblyName>Perspex.Xaml.Base.UnitTest</AssemblyName>
+    <RootNamespace>Perspex.Markup.Xaml.UnitTests</RootNamespace>
+    <AssemblyName>Perspex.Markup.Xaml.UnitTests</AssemblyName>
     <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
     <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
@@ -61,6 +61,10 @@
       <HintPath>..\..\packages\Splat.1.6.1\lib\Net45\Splat.dll</HintPath>
       <Private>True</Private>
     </Reference>
+    <Reference Include="Sprache, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Sprache.SuperJMN.2.0.0.50\lib\portable-net451+netcore451+wpa81\Sprache.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
@@ -103,6 +107,7 @@
     <Compile Include="BindingDefinitionBuilder.cs" />
     <Compile Include="ChangeBranchTest.cs" />
     <Compile Include="DataContextChangeSynchronizerTest.cs" />
+    <Compile Include="Parsers\SelectorGrammarTests.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="SampleModel\Level1.cs" />
     <Compile Include="SampleModel\Level2.cs" />

+ 1 - 0
Tests/Perspex.Markup.Xaml.UnitTests/packages.config

@@ -8,6 +8,7 @@
   <package id="Rx-Main" version="2.2.5" targetFramework="net451" />
   <package id="Rx-PlatformServices" version="2.2.5" targetFramework="net451" />
   <package id="Splat" version="1.6.1" targetFramework="net451" />
+  <package id="Sprache.SuperJMN" version="2.0.0.50" targetFramework="net451" />
   <package id="xunit" version="2.0.0" targetFramework="net451" />
   <package id="xunit.abstractions" version="2.0.0" targetFramework="net451" />
   <package id="xunit.assert" version="2.0.0" targetFramework="net451" />

+ 176 - 0
src/Markup/Perspex.Markup.Xaml/Parsers/SelectorGrammar.cs

@@ -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;
+            }
+        }
+    }
+}

+ 2 - 0
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@@ -40,6 +40,7 @@
     </Compile>
     <Compile Include="Converters\RowDefinitionsTypeConverter.cs" />
     <Compile Include="Converters\ColumnDefinitionsTypeConverter.cs" />
+    <Compile Include="Converters\SelectorConverter.cs" />
     <Compile Include="Converters\ThicknessConverter.cs" />
     <Compile Include="Context\PerspexWiringContext.cs" />
     <Compile Include="MarkupExtensions\BindingExtension.cs" />
@@ -51,6 +52,7 @@
     <Compile Include="DataBinding\ChangeTracking\PropertyPath.cs" />
     <Compile Include="DataBinding\ChangeTracking\TargettedProperty.cs" />
     <Compile Include="Context\PerspexParserFactory.cs" />
+    <Compile Include="Parsers\SelectorGrammar.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="DataBinding\DataContextChangeSynchronizer.cs" />
     <Compile Include="DataBinding\IPerspexPropertyBinder.cs" />

+ 2 - 0
src/Markup/Perspex.Markup.Xaml/Properties/AssemblyInfo.cs

@@ -2,5 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System.Reflection;
+using System.Runtime.CompilerServices;
 
 [assembly: AssemblyTitle("Perspex.Markup.Xaml")]
+[assembly: InternalsVisibleTo("Perspex.Markup.Xaml.UnitTests")]

+ 1 - 1
src/Perspex.Styling/Styling/Selectors.cs

@@ -78,7 +78,7 @@ namespace Perspex.Styling
             return new Selector(
                 previous,
                 x => MatchTemplate(x, previous),
-                " /deep/ ",
+                " /template/ ",
                 inTemplate: true,
                 stopTraversal: true);
         }