Browse Source

More work on parsing style selectors.

pull/145/head
Steven Kirk 11 years ago
parent
commit
193ec29824
  1. 51
      Tests/Perspex.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs
  2. 21
      src/Markup/Perspex.Markup.Xaml/Parsers/SelectorGrammar.cs
  3. 83
      src/Markup/Perspex.Markup.Xaml/Parsers/SelectorParser.cs
  4. 1
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

51
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());
}
}
}

21
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
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
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" />

Loading…
Cancel
Save