Browse Source

More work on parsing style selectors.

Steven Kirk 10 years ago
parent
commit
193ec29824

+ 50 - 1
Tests/Perspex.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs

@@ -16,7 +16,17 @@ namespace Perspex.Xaml.Base.UnitTest.Parsers
             var result = SelectorGrammar.Selector.Parse("Button").ToList();
 
             Assert.Equal(
-                new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button" } },
+                new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button", Xmlns = null } },
+                result);
+        }
+
+        [Fact]
+        public void NamespacedOfType()
+        {
+            var result = SelectorGrammar.Selector.Parse("x|Button").ToList();
+
+            Assert.Equal(
+                new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button", Xmlns = "x" } },
                 result);
         }
 
@@ -93,6 +103,21 @@ namespace Perspex.Xaml.Base.UnitTest.Parsers
                 result);
         }
 
+        [Fact]
+        public void OfType_Child_Class_No_Spaces()
+        {
+            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()
         {
@@ -136,5 +161,29 @@ namespace Perspex.Xaml.Base.UnitTest.Parsers
                 },
                 result);
         }
+
+        [Fact]
+        public void Namespace_Alone_Fails()
+        {
+            Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse("ns|").ToList());
+        }
+
+        [Fact]
+        public void Dot_Alone_Fails()
+        {
+            Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse(". dot").ToList());
+        }
+
+        [Fact]
+        public void Invalid_Identifier_Fails()
+        {
+            Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse("%foo").ToList());
+        }
+
+        [Fact]
+        public void Invalid_Class_Fails()
+        {
+            Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse(".%foo").ToList());
+        }
     }
 }

+ 16 - 5
src/Markup/Perspex.Markup.Xaml/Parsers/SelectorGrammar.cs

@@ -41,8 +41,19 @@ namespace Perspex.Markup.Xaml.Parsers
             from @char in IdentifierChar.Many().Text()
             select start + @char;
 
+        public static readonly Parser<string> Namespace =
+            from ns in Parse.Letter.Many().Text()
+            from bar in Parse.Char('|')
+            select ns;
+
         public static readonly Parser<OfTypeSyntax> OfType =
-            from identifier in Identifier select new OfTypeSyntax { TypeName = identifier };
+            from ns in Namespace.Optional()
+            from identifier in Identifier
+            select new OfTypeSyntax
+            {
+                TypeName = identifier,
+                Xmlns = ns.GetOrDefault(),
+            };
 
         public static readonly Parser<NameSyntax> Name =
             from hash in Parse.Char('#')
@@ -88,8 +99,6 @@ namespace Perspex.Markup.Xaml.Parsers
             from template in Parse.String("/template/").Token()
             select new TemplateSyntax();
 
-        //public static readonly 
-
         public static readonly Parser<ISyntax> SingleSelector =
             OfType
             .Or<ISyntax>(Name)
@@ -99,8 +108,8 @@ namespace Perspex.Markup.Xaml.Parsers
             .Or<ISyntax>(Template)
             .Or<ISyntax>(Descendent);
 
-        public static readonly Parser<IEnumerable<ISyntax>> Selector = SingleSelector.Many();
-
+        public static readonly Parser<IEnumerable<ISyntax>> Selector = SingleSelector.Many().End();
+        
         public interface ISyntax
         {
         }
@@ -109,6 +118,8 @@ namespace Perspex.Markup.Xaml.Parsers
         {
             public string TypeName { get; set; }
 
+            public string Xmlns { get; set; }
+
             public override bool Equals(object obj)
             {
                 return obj is OfTypeSyntax && ((OfTypeSyntax)obj).TypeName == TypeName;

+ 83 - 0
src/Markup/Perspex.Markup.Xaml/Parsers/SelectorParser.cs

@@ -0,0 +1,83 @@
+// 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;
+using Perspex.Styling;
+using Sprache;
+
+namespace Perspex.Markup.Xaml.Parsers
+{
+    /// <summary>
+    /// Parses a <see cref="Selector"/> from text.
+    /// </summary>
+    public class SelectorParser
+    {
+        private Func<string, string, Type> _typeResolver;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SelectorParser"/> class.
+        /// </summary>
+        /// <param name="typeResolver">
+        /// The type resolver to use. The type resolver is a function which accepts two strings:
+        /// a type name and a XML namespace prefix and a type name, and should return the resolved
+        /// type or throw an exception.
+        /// </param>
+        public SelectorParser(Func<string, string, Type> typeResolver)
+        {
+            this._typeResolver = typeResolver;
+        }
+
+        /// <summary>
+        /// Parses a <see cref="Selector"/> from a string.
+        /// </summary>
+        /// <param name="s">The string.</param>
+        /// <returns>The parsed selector.</returns>
+        public Selector Parse(string s)
+        {
+            var syntax = SelectorGrammar.Selector.Parse(s);
+            var result = new Selector();
+
+            foreach (var i in syntax)
+            {
+                var ofType = i as SelectorGrammar.OfTypeSyntax;
+                var @class = i as SelectorGrammar.ClassSyntax;
+                var name = i as SelectorGrammar.NameSyntax;
+                var property = i as SelectorGrammar.PropertySyntax;
+                var child = i as SelectorGrammar.ChildSyntax;
+                var descendent = i as SelectorGrammar.DescendentSyntax;
+                var template = i as SelectorGrammar.TemplateSyntax;
+
+                if (ofType != null)
+                {
+                    result = result.OfType(_typeResolver(ofType.TypeName, ofType.Xmlns));
+                }
+                else if (@class != null)
+                {
+                    result = result.Class(@class.Class);
+                }
+                else if (name != null)
+                {
+                    result = result.Name(name.Name);
+                }
+                else if (property != null)
+                {
+                    throw new NotImplementedException();
+                }
+                else if (child != null)
+                {
+                    result = result.Child();
+                }
+                else if (descendent != null)
+                {
+                    result = result.Descendent();
+                }
+                else if (template != null)
+                {
+                    result = result.Template();
+                }
+            }
+
+            return result;
+        }
+    }
+}

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

@@ -52,6 +52,7 @@
     <Compile Include="DataBinding\ChangeTracking\PropertyPath.cs" />
     <Compile Include="DataBinding\ChangeTracking\TargettedProperty.cs" />
     <Compile Include="Context\PerspexParserFactory.cs" />
+    <Compile Include="Parsers\SelectorParser.cs" />
     <Compile Include="Parsers\SelectorGrammar.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="DataBinding\DataContextChangeSynchronizer.cs" />