Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Commit

Permalink
Add extensible directive abstractions
Browse files Browse the repository at this point in the history
- Based generic directive implementation off of descriptors.
- Added parsing logic to consume descriptors and parse content that's expected.
- Added parsing errors to automagically detect unexpected directive pieces.
- Updated visitor implementations to understand the directive bits.
- Added a builder abstraction to easily create descriptors. Had to maintain the ability to manually construct a descriptor to enable convenient serialization/deserialization.
- Added tests/comparers to verify correctness of parsing.

#853
  • Loading branch information
NTaylorMullen committed Nov 24, 2016
1 parent 522f6e9 commit 518378f
Show file tree
Hide file tree
Showing 22 changed files with 1,222 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,30 @@ public override void VisitImportSpan(AddImportChunkGenerator chunkGenerator, Spa
Namespace.Children.Insert(i, @using);
}

public override void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
{
Builder.Add(new DirectiveTokenIRNode()
{
Content = span.Content,
Descriptor = chunkGenerator.Descriptor,
SourceLocation = span.Start,
});
}

public override void VisitStartDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block)
{
Builder.Push(new DirectiveIRNode()
{
Name = chunkGenerator.Descriptor.Name,
Descriptor = chunkGenerator.Descriptor,
});
}

public override void VisitEndDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block)
{
Builder.Pop();
}

private class ContainerRazorIRNode : RazorIRNode
{
private SourceLocation? _location;
Expand Down
16 changes: 16 additions & 0 deletions src/Microsoft.AspNetCore.Razor.Evolution/DirectiveDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright(c) .NET Foundation.All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;

namespace Microsoft.AspNetCore.Razor.Evolution
{
public class DirectiveDescriptor
{
public string Name { get; set; }

public DirectiveDescriptorKind Kind { get; set; }

public IReadOnlyList<DirectiveTokenDescriptor> Tokens { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright(c) .NET Foundation.All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;

namespace Microsoft.AspNetCore.Razor.Evolution
{
public static class DirectiveDescriptorBuilder
{
public static IDirectiveDescriptorBuilder Create(string name)
{
return new DefaultDirectiveDescriptorBuilder(name, DirectiveDescriptorKind.SingleLine);
}

public static IDirectiveDescriptorBuilder CreateRazorBlock(string name)
{
return new DefaultDirectiveDescriptorBuilder(name, DirectiveDescriptorKind.RazorBlock);
}

public static IDirectiveDescriptorBuilder CreateCodeBlock(string name)
{
return new DefaultDirectiveDescriptorBuilder(name, DirectiveDescriptorKind.CodeBlock);
}

private class DefaultDirectiveDescriptorBuilder : IDirectiveDescriptorBuilder
{
private readonly List<DirectiveTokenDescriptor> _tokenDescriptors;
private readonly string _name;
private readonly DirectiveDescriptorKind _type;

public DefaultDirectiveDescriptorBuilder(string name, DirectiveDescriptorKind type)
{
_name = name;
_type = type;
_tokenDescriptors = new List<DirectiveTokenDescriptor>();
}

public IDirectiveDescriptorBuilder AddType()
{
var descriptor = new DirectiveTokenDescriptor()
{
Kind = DirectiveTokenKind.Type
};
_tokenDescriptors.Add(descriptor);

return this;
}

public IDirectiveDescriptorBuilder AddMember()
{
var descriptor = new DirectiveTokenDescriptor()
{
Kind = DirectiveTokenKind.Member
};
_tokenDescriptors.Add(descriptor);

return this;
}

public IDirectiveDescriptorBuilder AddString()
{
var descriptor = new DirectiveTokenDescriptor()
{
Kind = DirectiveTokenKind.String
};
_tokenDescriptors.Add(descriptor);

return this;
}

public IDirectiveDescriptorBuilder AddLiteral(string literal)
{
var descriptor = new DirectiveTokenDescriptor()
{
Kind = DirectiveTokenKind.Literal,
Value = literal,
};
_tokenDescriptors.Add(descriptor);

return this;
}

public DirectiveDescriptor Build()
{
var descriptor = new DirectiveDescriptor
{
Name = _name,
Kind = _type,
Tokens = _tokenDescriptors,
};

return descriptor;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Internal;

namespace Microsoft.AspNetCore.Razor.Evolution
{
internal class DirectiveDescriptorComparer : IEqualityComparer<DirectiveDescriptor>
{
public static readonly DirectiveDescriptorComparer Default = new DirectiveDescriptorComparer();

protected DirectiveDescriptorComparer()
{
}

public bool Equals(DirectiveDescriptor descriptorX, DirectiveDescriptor descriptorY)
{
if (descriptorX == descriptorY)
{
return true;
}

return descriptorX != null &&
string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.Ordinal) &&
descriptorX.Kind == descriptorY.Kind &&
Enumerable.SequenceEqual(
descriptorX.Tokens,
descriptorY.Tokens,
DirectiveTokenDescriptorComparer.Default);
}

public int GetHashCode(DirectiveDescriptor descriptor)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}

var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.Kind);

return hashCodeCombiner.CombinedHash;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright(c) .NET Foundation.All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNetCore.Razor.Evolution
{
public enum DirectiveDescriptorKind
{
SingleLine,
RazorBlock,
CodeBlock
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright(c) .NET Foundation.All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNetCore.Razor.Evolution
{
public class DirectiveTokenDescriptor
{
public DirectiveTokenKind Kind { get; set; }

public string Value { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Internal;

namespace Microsoft.AspNetCore.Razor.Evolution
{
internal class DirectiveTokenDescriptorComparer : IEqualityComparer<DirectiveTokenDescriptor>
{
public static readonly DirectiveTokenDescriptorComparer Default = new DirectiveTokenDescriptorComparer();

protected DirectiveTokenDescriptorComparer()
{
}

public bool Equals(DirectiveTokenDescriptor descriptorX, DirectiveTokenDescriptor descriptorY)
{
if (descriptorX == descriptorY)
{
return true;
}

return descriptorX != null &&
string.Equals(descriptorX.Value, descriptorY.Value, StringComparison.Ordinal) &&
descriptorX.Kind == descriptorY.Kind;
}

public int GetHashCode(DirectiveTokenDescriptor descriptor)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}

var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(descriptor.Value, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.Kind);

return hashCodeCombiner.CombinedHash;
}
}
}
13 changes: 13 additions & 0 deletions src/Microsoft.AspNetCore.Razor.Evolution/DirectiveTokenKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright(c) .NET Foundation.All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNetCore.Razor.Evolution
{
public enum DirectiveTokenKind
{
Type,
Member,
String,
Literal
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright(c) .NET Foundation.All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNetCore.Razor.Evolution
{
public interface IDirectiveDescriptorBuilder
{
IDirectiveDescriptorBuilder AddType();

IDirectiveDescriptorBuilder AddMember();

IDirectiveDescriptorBuilder AddString();

IDirectiveDescriptorBuilder AddLiteral(string literal);

DirectiveDescriptor Build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright(c) .NET Foundation.All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;

namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class DirectiveIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();

public override RazorIRNode Parent { get; set; }

internal override SourceLocation SourceLocation { get; set; }

public string Name { get; set; }

public IEnumerable<DirectiveTokenIRNode> Tokens => Children.OfType<DirectiveTokenIRNode>();

public DirectiveDescriptor Descriptor { get; set; }

public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitDirective(this);
}

public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitDirective(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright(c) .NET Foundation.All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;

namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class DirectiveTokenIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = EmptyArray;

public override RazorIRNode Parent { get; set; }

internal override SourceLocation SourceLocation { get; set; }

public string Content { get; set; }

public DirectiveTokenDescriptor Descriptor { get; set; }

public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitDirectiveToken(this);
}

public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitDirectiveToken(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ public virtual void VisitDefault(RazorIRNode node)
{
}

public virtual void VisitDirectiveToken(DirectiveTokenIRNode node)
{
VisitDefault(node);
}

public virtual void VisitDirective(DirectiveIRNode node)
{
VisitDefault(node);
}

public virtual void VisitTemplate(TemplateIRNode node)
{
VisitDefault(node);
Expand Down
Loading

0 comments on commit 518378f

Please sign in to comment.