mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Introduce a Roslyn source generator that emits the visitor boilerplate for the C# AST from [DecompilerAstNode]-tagged node declarations: the IAstVisitor interface, the AcceptVisitor overloads, the pattern-placeholder nodes, and the initial DoMatch support. AccessorKind lets an accessor's keyword be chosen independently of its role, an early step toward shedding the NRefactory role model.pull/3807/head
126 changed files with 1044 additions and 157 deletions
@ -0,0 +1,383 @@
@@ -0,0 +1,383 @@
|
||||
// Copyright (c) 2026 Siegfried Pammer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System.Collections; |
||||
using System.Collections.Immutable; |
||||
using System.Text; |
||||
|
||||
using Microsoft.CodeAnalysis; |
||||
using Microsoft.CodeAnalysis.CSharp; |
||||
using Microsoft.CodeAnalysis.CSharp.Syntax; |
||||
using Microsoft.CodeAnalysis.Text; |
||||
|
||||
namespace ICSharpCode.Decompiler.Generators; |
||||
|
||||
[Generator] |
||||
internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator |
||||
{ |
||||
record AstNodeAdditions(string NodeName, bool NeedsAcceptImpls, bool NeedsVisitor, bool NeedsNullNode, bool NeedsPatternPlaceholder, int NullNodeBaseCtorParamCount, bool IsTypeNode, string VisitMethodName, string VisitMethodParamType, EquatableArray<(string Member, string TypeName, bool RecursiveMatch, bool MatchAny)>? MembersToMatch); |
||||
|
||||
AstNodeAdditions GetAstNodeAdditions(GeneratorAttributeSyntaxContext context, CancellationToken ct) |
||||
{ |
||||
var targetSymbol = (INamedTypeSymbol)context.TargetSymbol; |
||||
var attribute = context.Attributes.SingleOrDefault(ad => ad.AttributeClass?.Name == "DecompilerAstNodeAttribute")!; |
||||
var (visitMethodName, paramTypeName) = targetSymbol.Name switch { |
||||
"ErrorExpression" => ("ErrorNode", "AstNode"), |
||||
string s when s.Contains("AstType") => (s.Replace("AstType", "Type"), s), |
||||
_ => (targetSymbol.Name, targetSymbol.Name), |
||||
}; |
||||
|
||||
List<(string Member, string TypeName, bool RecursiveMatch, bool MatchAny)>? membersToMatch = null; |
||||
|
||||
if (!targetSymbol.MemberNames.Contains("DoMatch")) |
||||
{ |
||||
membersToMatch = new(); |
||||
|
||||
var astNodeType = (INamedTypeSymbol)context.SemanticModel.GetSpeculativeSymbolInfo(context.TargetNode.Span.Start, SyntaxFactory.ParseTypeName("AstNode"), SpeculativeBindingOption.BindAsTypeOrNamespace).Symbol!; |
||||
|
||||
if (targetSymbol.BaseType!.MemberNames.Contains("MatchAttributesAndModifiers")) |
||||
membersToMatch.Add(("MatchAttributesAndModifiers", null!, false, false)); |
||||
|
||||
foreach (var m in targetSymbol.GetMembers()) |
||||
{ |
||||
if (m is not IPropertySymbol property || property.IsIndexer || property.IsOverride) |
||||
continue; |
||||
if (property.GetAttributes().Any(a => a.AttributeClass?.Name == "ExcludeFromMatchAttribute")) |
||||
continue; |
||||
if (property.Type.MetadataName is "CSharpTokenNode" or "TextLocation") |
||||
continue; |
||||
switch (property.Type) |
||||
{ |
||||
case INamedTypeSymbol named when named.IsDerivedFrom(astNodeType) || named.MetadataName == "AstNodeCollection`1": |
||||
membersToMatch.Add((property.Name, named.Name, true, false)); |
||||
break; |
||||
case INamedTypeSymbol { TypeKind: TypeKind.Enum } named when named.GetMembers().Any(_ => _.Name == "Any"): |
||||
membersToMatch.Add((property.Name, named.Name, false, true)); |
||||
break; |
||||
default: |
||||
membersToMatch.Add((property.Name, property.Type.Name, false, false)); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return new(targetSymbol.Name, !targetSymbol.MemberNames.Contains("AcceptVisitor"), |
||||
NeedsVisitor: !targetSymbol.IsAbstract && targetSymbol.BaseType!.IsAbstract, |
||||
NeedsNullNode: (bool)attribute.ConstructorArguments[0].Value!, |
||||
NeedsPatternPlaceholder: (bool)attribute.ConstructorArguments[1].Value!, |
||||
NullNodeBaseCtorParamCount: targetSymbol.InstanceConstructors.Min(m => m.Parameters.Length), |
||||
IsTypeNode: targetSymbol.Name == "AstType" || targetSymbol.BaseType?.Name == "AstType", |
||||
visitMethodName, paramTypeName, membersToMatch?.ToEquatableArray()); |
||||
} |
||||
|
||||
void WriteGeneratedMembers(SourceProductionContext context, AstNodeAdditions source) |
||||
{ |
||||
var builder = new StringBuilder(); |
||||
|
||||
builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;"); |
||||
builder.AppendLine(); |
||||
|
||||
if (source.NeedsPatternPlaceholder) |
||||
{ |
||||
builder.AppendLine("using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;"); |
||||
} |
||||
|
||||
builder.AppendLine(); |
||||
|
||||
builder.AppendLine("#nullable enable"); |
||||
builder.AppendLine(); |
||||
|
||||
builder.AppendLine($"partial class {source.NodeName}"); |
||||
builder.AppendLine("{"); |
||||
|
||||
if (source.NeedsNullNode) |
||||
{ |
||||
bool needsNew = source.NodeName != "AstNode"; |
||||
|
||||
builder.AppendLine($" {(needsNew ? "new " : "")}public static readonly {source.NodeName} Null = new Null{source.NodeName}();"); |
||||
|
||||
builder.AppendLine($@"
|
||||
sealed class Null{source.NodeName} : {source.NodeName} |
||||
{{ |
||||
public override NodeType NodeType => NodeType.Unknown; |
||||
|
||||
public override bool IsNull => true; |
||||
|
||||
public override void AcceptVisitor(IAstVisitor visitor) |
||||
{{ |
||||
visitor.VisitNullNode(this); |
||||
}} |
||||
|
||||
public override T AcceptVisitor<T>(IAstVisitor<T> visitor) |
||||
{{ |
||||
return visitor.VisitNullNode(this); |
||||
}} |
||||
|
||||
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) |
||||
{{ |
||||
return visitor.VisitNullNode(this, data); |
||||
}} |
||||
|
||||
protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match) |
||||
{{ |
||||
return other == null || other.IsNull; |
||||
}}");
|
||||
|
||||
if (source.NullNodeBaseCtorParamCount > 0) |
||||
{ |
||||
builder.AppendLine($@"
|
||||
|
||||
public Null{source.NodeName}() : base({string.Join(", ", Enumerable.Repeat("default", source.NullNodeBaseCtorParamCount))}) {{ }}");
|
||||
} |
||||
|
||||
builder.AppendLine($@"
|
||||
}} |
||||
|
||||
");
|
||||
|
||||
} |
||||
|
||||
if (source.NeedsPatternPlaceholder) |
||||
{ |
||||
builder.Append( |
||||
$@" public static implicit operator {source.NodeName}?(PatternMatching.Pattern? pattern)
|
||||
{{ |
||||
return pattern != null ? new PatternPlaceholder(pattern) : null; |
||||
}} |
||||
|
||||
sealed class PatternPlaceholder : {source.NodeName}, INode |
||||
{{ |
||||
readonly PatternMatching.Pattern child; |
||||
|
||||
public PatternPlaceholder(PatternMatching.Pattern child) |
||||
{{ |
||||
this.child = child; |
||||
}} |
||||
|
||||
public override NodeType NodeType {{ |
||||
get {{ return NodeType.Pattern; }} |
||||
}} |
||||
|
||||
public override void AcceptVisitor(IAstVisitor visitor) |
||||
{{ |
||||
visitor.VisitPatternPlaceholder(this, child); |
||||
}} |
||||
|
||||
public override T AcceptVisitor<T>(IAstVisitor<T> visitor) |
||||
{{ |
||||
return visitor.VisitPatternPlaceholder(this, child); |
||||
}} |
||||
|
||||
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) |
||||
{{ |
||||
return visitor.VisitPatternPlaceholder(this, child, data); |
||||
}} |
||||
|
||||
protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match) |
||||
{{ |
||||
return child.DoMatch(other, match); |
||||
}} |
||||
|
||||
bool PatternMatching.INode.DoMatchCollection(Role? role, PatternMatching.INode? pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) |
||||
{{ |
||||
return child.DoMatchCollection(role, pos, match, backtrackingInfo); |
||||
}} |
||||
}} |
||||
"
|
||||
); |
||||
} |
||||
|
||||
if (source.NeedsAcceptImpls && source.NeedsVisitor) |
||||
{ |
||||
builder.Append($@" public override void AcceptVisitor(IAstVisitor visitor)
|
||||
{{ |
||||
visitor.Visit{source.VisitMethodName}(this); |
||||
}} |
||||
|
||||
public override T AcceptVisitor<T>(IAstVisitor<T> visitor) |
||||
{{ |
||||
return visitor.Visit{source.VisitMethodName}(this); |
||||
}} |
||||
|
||||
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) |
||||
{{ |
||||
return visitor.Visit{source.VisitMethodName}(this, data); |
||||
}} |
||||
");
|
||||
} |
||||
|
||||
if (source.MembersToMatch != null) |
||||
{ |
||||
builder.Append($@" protected internal override bool DoMatch(AstNode? other, PatternMatching.Match match)
|
||||
{{ |
||||
return other is {source.NodeName} o && !o.IsNull");
|
||||
|
||||
foreach (var (member, typeName, recursive, hasAny) in source.MembersToMatch) |
||||
{ |
||||
if (member == "MatchAttributesAndModifiers") |
||||
{ |
||||
builder.Append($"\r\n\t\t\t&& this.MatchAttributesAndModifiers(o, match)"); |
||||
} |
||||
else if (recursive) |
||||
{ |
||||
builder.Append($"\r\n\t\t\t&& this.{member}.DoMatch(o.{member}, match)"); |
||||
} |
||||
else if (hasAny) |
||||
{ |
||||
builder.Append($"\r\n\t\t\t&& (this.{member} == {typeName}.Any || this.{member} == o.{member})"); |
||||
} |
||||
else if (typeName == "String") |
||||
{ |
||||
builder.Append($"\r\n\t\t\t&& MatchString(this.{member}, o.{member})"); |
||||
} |
||||
else |
||||
{ |
||||
builder.Append($"\r\n\t\t\t&& this.{member} == o.{member}"); |
||||
} |
||||
} |
||||
|
||||
builder.Append(@";
|
||||
} |
||||
");
|
||||
} |
||||
|
||||
builder.AppendLine("}"); |
||||
|
||||
context.AddSource(source.NodeName + ".g.cs", SourceText.From(builder.ToString(), Encoding.UTF8)); |
||||
} |
||||
|
||||
void WriteVisitors(SourceProductionContext context, ImmutableArray<AstNodeAdditions> source) |
||||
{ |
||||
var builder = new StringBuilder(); |
||||
|
||||
builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;"); |
||||
|
||||
source = source |
||||
.Concat([new("NullNode", false, true, false, false, 0, false, "NullNode", "AstNode", null), new("PatternPlaceholder", false, true, false, false, 0, false, "PatternPlaceholder", "AstNode", null)]) |
||||
.ToImmutableArray(); |
||||
|
||||
WriteInterface("IAstVisitor", "void", ""); |
||||
WriteInterface("IAstVisitor<out S>", "S", ""); |
||||
WriteInterface("IAstVisitor<in T, out S>", "S", ", T data"); |
||||
|
||||
context.AddSource("IAstVisitor.g.cs", SourceText.From(builder.ToString(), Encoding.UTF8)); |
||||
|
||||
void WriteInterface(string name, string ret, string param) |
||||
{ |
||||
builder.AppendLine($"public interface {name}"); |
||||
builder.AppendLine("{"); |
||||
|
||||
foreach (var type in source.OrderBy(t => t.VisitMethodName)) |
||||
{ |
||||
if (!type.NeedsVisitor) |
||||
continue; |
||||
|
||||
string extParams, paramName; |
||||
if (type.VisitMethodName == "PatternPlaceholder") |
||||
{ |
||||
paramName = "placeholder"; |
||||
extParams = ", PatternMatching.Pattern pattern" + param; |
||||
} |
||||
else |
||||
{ |
||||
paramName = char.ToLowerInvariant(type.VisitMethodName[0]) + type.VisitMethodName.Substring(1); |
||||
extParams = param; |
||||
} |
||||
|
||||
builder.AppendLine($"\t{ret} Visit{type.VisitMethodName}({type.VisitMethodParamType} {paramName}{extParams});"); |
||||
} |
||||
|
||||
builder.AppendLine("}"); |
||||
} |
||||
} |
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context) |
||||
{ |
||||
var astNodeAdditions = context.SyntaxProvider.ForAttributeWithMetadataName( |
||||
"ICSharpCode.Decompiler.CSharp.Syntax.DecompilerAstNodeAttribute", |
||||
(n, ct) => n is ClassDeclarationSyntax, |
||||
GetAstNodeAdditions); |
||||
|
||||
var visitorMembers = astNodeAdditions.Collect(); |
||||
|
||||
context |
||||
.RegisterPostInitializationOutput(i => i.AddSource("DecompilerSyntaxTreeGeneratorAttributes.g.cs", @"
|
||||
|
||||
using System; |
||||
|
||||
namespace Microsoft.CodeAnalysis |
||||
{ |
||||
internal sealed partial class EmbeddedAttribute : global::System.Attribute |
||||
{ |
||||
} |
||||
} |
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Syntax |
||||
{ |
||||
[global::Microsoft.CodeAnalysis.EmbeddedAttribute] |
||||
sealed class DecompilerAstNodeAttribute : global::System.Attribute |
||||
{ |
||||
public DecompilerAstNodeAttribute(bool hasNullNode = false, bool hasPatternPlaceholder = false) { } |
||||
} |
||||
|
||||
[global::Microsoft.CodeAnalysis.EmbeddedAttribute] |
||||
sealed class ExcludeFromMatchAttribute : global::System.Attribute |
||||
{ |
||||
} |
||||
} |
||||
|
||||
"));
|
||||
|
||||
context.RegisterSourceOutput(astNodeAdditions, WriteGeneratedMembers); |
||||
context.RegisterSourceOutput(visitorMembers, WriteVisitors); |
||||
} |
||||
} |
||||
|
||||
readonly struct EquatableArray<T> : IEquatable<EquatableArray<T>>, IEnumerable<T> |
||||
where T : IEquatable<T> |
||||
{ |
||||
readonly T[] array; |
||||
|
||||
public EquatableArray(T[] array) |
||||
{ |
||||
this.array = array ?? throw new ArgumentNullException(nameof(array)); |
||||
} |
||||
|
||||
public bool Equals(EquatableArray<T> other) |
||||
{ |
||||
return other.array.AsSpan().SequenceEqual(this.array); |
||||
} |
||||
|
||||
public IEnumerator<T> GetEnumerator() |
||||
{ |
||||
return ((IEnumerable<T>)array).GetEnumerator(); |
||||
} |
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() |
||||
{ |
||||
return array.GetEnumerator(); |
||||
} |
||||
} |
||||
|
||||
static class EquatableArrayExtensions |
||||
{ |
||||
public static EquatableArray<T> ToEquatableArray<T>(this List<T> array) where T : IEquatable<T> |
||||
{ |
||||
return new EquatableArray<T>(array.ToArray()); |
||||
} |
||||
} |
||||
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<ImplicitUsings>enable</ImplicitUsings> |
||||
<Nullable>enable</Nullable> |
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> |
||||
<LangVersion>12</LangVersion> |
||||
<!-- Opt out of the repo's central package management for this project: a source generator must |
||||
reference a Roslyn no newer than the running compiler, but the repo's $(RoslynVersion) tracks |
||||
the (newer) preview SDK compiler. Pin to a stable lower version any supported host can load. |
||||
The stable package lives on nuget.org; the local NuGet.config reaches it past the repo-root |
||||
source mapping that otherwise routes Microsoft.CodeAnalysis.* to the dotnet-tools feed. --> |
||||
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
||||
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
namespace System.Runtime.CompilerServices; |
||||
|
||||
class IsExternalInit { } |
||||
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<configuration> |
||||
<!-- The repo-root NuGet.config maps Microsoft.CodeAnalysis.* exclusively to the dotnet-tools feed, |
||||
which carries only prerelease Roslyn builds. This source generator deliberately references a |
||||
stable Roslyn (see the csproj), which lives on nuget.org. Clearing the inherited sources and |
||||
mapping here lets this project restore that stable package; it has no other dependencies. --> |
||||
<packageSources> |
||||
<clear /> |
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> |
||||
</packageSources> |
||||
<packageSourceMapping> |
||||
<clear /> |
||||
<packageSource key="nuget.org"> |
||||
<package pattern="*" /> |
||||
</packageSource> |
||||
</packageSourceMapping> |
||||
</configuration> |
||||
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
using Microsoft.CodeAnalysis; |
||||
|
||||
namespace ICSharpCode.Decompiler.Generators; |
||||
|
||||
public static class RoslynHelpers |
||||
{ |
||||
public static IEnumerable<INamedTypeSymbol> GetTopLevelTypes(this IAssemblySymbol assembly) |
||||
{ |
||||
foreach (var ns in TreeTraversal.PreOrder(assembly.GlobalNamespace, ns => ns.GetNamespaceMembers())) |
||||
{ |
||||
foreach (var t in ns.GetTypeMembers()) |
||||
{ |
||||
yield return t; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static bool IsDerivedFrom(this INamedTypeSymbol type, INamedTypeSymbol baseType) |
||||
{ |
||||
INamedTypeSymbol? t = type; |
||||
|
||||
while (t != null) |
||||
{ |
||||
if (SymbolEqualityComparer.Default.Equals(t, baseType)) |
||||
return true; |
||||
|
||||
t = t.BaseType; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
} |
||||
@ -0,0 +1,125 @@
@@ -0,0 +1,125 @@
|
||||
#nullable enable |
||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
namespace ICSharpCode.Decompiler.Generators; |
||||
|
||||
/// <summary>
|
||||
/// Static helper methods for traversing trees.
|
||||
/// </summary>
|
||||
internal static class TreeTraversal |
||||
{ |
||||
/// <summary>
|
||||
/// Converts a tree data structure into a flat list by traversing it in pre-order.
|
||||
/// </summary>
|
||||
/// <param name="root">The root element of the tree.</param>
|
||||
/// <param name="recursion">The function that gets the children of an element.</param>
|
||||
/// <returns>Iterator that enumerates the tree structure in pre-order.</returns>
|
||||
public static IEnumerable<T> PreOrder<T>(T root, Func<T, IEnumerable<T>?> recursion) |
||||
{ |
||||
return PreOrder(new T[] { root }, recursion); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Converts a tree data structure into a flat list by traversing it in pre-order.
|
||||
/// </summary>
|
||||
/// <param name="input">The root elements of the forest.</param>
|
||||
/// <param name="recursion">The function that gets the children of an element.</param>
|
||||
/// <returns>Iterator that enumerates the tree structure in pre-order.</returns>
|
||||
public static IEnumerable<T> PreOrder<T>(IEnumerable<T> input, Func<T, IEnumerable<T>?> recursion) |
||||
{ |
||||
Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>(); |
||||
try |
||||
{ |
||||
stack.Push(input.GetEnumerator()); |
||||
while (stack.Count > 0) |
||||
{ |
||||
while (stack.Peek().MoveNext()) |
||||
{ |
||||
T element = stack.Peek().Current; |
||||
yield return element; |
||||
IEnumerable<T>? children = recursion(element); |
||||
if (children != null) |
||||
{ |
||||
stack.Push(children.GetEnumerator()); |
||||
} |
||||
} |
||||
stack.Pop().Dispose(); |
||||
} |
||||
} |
||||
finally |
||||
{ |
||||
while (stack.Count > 0) |
||||
{ |
||||
stack.Pop().Dispose(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Converts a tree data structure into a flat list by traversing it in post-order.
|
||||
/// </summary>
|
||||
/// <param name="root">The root element of the tree.</param>
|
||||
/// <param name="recursion">The function that gets the children of an element.</param>
|
||||
/// <returns>Iterator that enumerates the tree structure in post-order.</returns>
|
||||
public static IEnumerable<T> PostOrder<T>(T root, Func<T, IEnumerable<T>?> recursion) |
||||
{ |
||||
return PostOrder(new T[] { root }, recursion); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Converts a tree data structure into a flat list by traversing it in post-order.
|
||||
/// </summary>
|
||||
/// <param name="input">The root elements of the forest.</param>
|
||||
/// <param name="recursion">The function that gets the children of an element.</param>
|
||||
/// <returns>Iterator that enumerates the tree structure in post-order.</returns>
|
||||
public static IEnumerable<T> PostOrder<T>(IEnumerable<T> input, Func<T, IEnumerable<T>?> recursion) |
||||
{ |
||||
Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>(); |
||||
try |
||||
{ |
||||
stack.Push(input.GetEnumerator()); |
||||
while (stack.Count > 0) |
||||
{ |
||||
while (stack.Peek().MoveNext()) |
||||
{ |
||||
T element = stack.Peek().Current; |
||||
IEnumerable<T>? children = recursion(element); |
||||
if (children != null) |
||||
{ |
||||
stack.Push(children.GetEnumerator()); |
||||
} |
||||
else |
||||
{ |
||||
yield return element; |
||||
} |
||||
} |
||||
stack.Pop().Dispose(); |
||||
if (stack.Count > 0) |
||||
yield return stack.Peek().Current; |
||||
} |
||||
} |
||||
finally |
||||
{ |
||||
while (stack.Count > 0) |
||||
{ |
||||
stack.Pop().Dispose(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
{ |
||||
"version": 1, |
||||
"dependencies": { |
||||
".NETStandard,Version=v2.0": { |
||||
"Microsoft.CodeAnalysis.CSharp": { |
||||
"type": "Direct", |
||||
"requested": "[5.0.0, )", |
||||
"resolved": "5.0.0", |
||||
"contentHash": "5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==", |
||||
"dependencies": { |
||||
"Microsoft.CodeAnalysis.Analyzers": "3.11.0", |
||||
"Microsoft.CodeAnalysis.Common": "[5.0.0]", |
||||
"System.Buffers": "4.6.0", |
||||
"System.Collections.Immutable": "9.0.0", |
||||
"System.Memory": "4.6.0", |
||||
"System.Numerics.Vectors": "4.6.0", |
||||
"System.Reflection.Metadata": "9.0.0", |
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.0", |
||||
"System.Text.Encoding.CodePages": "8.0.0", |
||||
"System.Threading.Tasks.Extensions": "4.6.0" |
||||
} |
||||
}, |
||||
"NETStandard.Library": { |
||||
"type": "Direct", |
||||
"requested": "[2.0.3, )", |
||||
"resolved": "2.0.3", |
||||
"contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", |
||||
"dependencies": { |
||||
"Microsoft.NETCore.Platforms": "1.1.0" |
||||
} |
||||
}, |
||||
"Microsoft.CodeAnalysis.Analyzers": { |
||||
"type": "Transitive", |
||||
"resolved": "3.11.0", |
||||
"contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" |
||||
}, |
||||
"Microsoft.CodeAnalysis.Common": { |
||||
"type": "Transitive", |
||||
"resolved": "5.0.0", |
||||
"contentHash": "ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==", |
||||
"dependencies": { |
||||
"Microsoft.CodeAnalysis.Analyzers": "3.11.0", |
||||
"System.Buffers": "4.6.0", |
||||
"System.Collections.Immutable": "9.0.0", |
||||
"System.Memory": "4.6.0", |
||||
"System.Numerics.Vectors": "4.6.0", |
||||
"System.Reflection.Metadata": "9.0.0", |
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.0", |
||||
"System.Text.Encoding.CodePages": "8.0.0", |
||||
"System.Threading.Tasks.Extensions": "4.6.0" |
||||
} |
||||
}, |
||||
"Microsoft.NETCore.Platforms": { |
||||
"type": "Transitive", |
||||
"resolved": "1.1.0", |
||||
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" |
||||
}, |
||||
"System.Buffers": { |
||||
"type": "Transitive", |
||||
"resolved": "4.6.0", |
||||
"contentHash": "lN6tZi7Q46zFzAbRYXTIvfXcyvQQgxnY7Xm6C6xQ9784dEL1amjM6S6Iw4ZpsvesAKnRVsM4scrDQaDqSClkjA==" |
||||
}, |
||||
"System.Collections.Immutable": { |
||||
"type": "Transitive", |
||||
"resolved": "9.0.0", |
||||
"contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==", |
||||
"dependencies": { |
||||
"System.Memory": "4.5.5", |
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0" |
||||
} |
||||
}, |
||||
"System.Memory": { |
||||
"type": "Transitive", |
||||
"resolved": "4.6.0", |
||||
"contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg==", |
||||
"dependencies": { |
||||
"System.Buffers": "4.6.0", |
||||
"System.Numerics.Vectors": "4.6.0", |
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.0" |
||||
} |
||||
}, |
||||
"System.Numerics.Vectors": { |
||||
"type": "Transitive", |
||||
"resolved": "4.6.0", |
||||
"contentHash": "t+SoieZsRuEyiw/J+qXUbolyO219tKQQI0+2/YI+Qv7YdGValA6WiuokrNKqjrTNsy5ABWU11bdKOzUdheteXg==" |
||||
}, |
||||
"System.Reflection.Metadata": { |
||||
"type": "Transitive", |
||||
"resolved": "9.0.0", |
||||
"contentHash": "ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ==", |
||||
"dependencies": { |
||||
"System.Collections.Immutable": "9.0.0", |
||||
"System.Memory": "4.5.5" |
||||
} |
||||
}, |
||||
"System.Runtime.CompilerServices.Unsafe": { |
||||
"type": "Transitive", |
||||
"resolved": "6.1.0", |
||||
"contentHash": "5o/HZxx6RVqYlhKSq8/zronDkALJZUT2Vz0hx43f0gwe8mwlM0y2nYlqdBwLMzr262Bwvpikeb/yEwkAa5PADg==" |
||||
}, |
||||
"System.Text.Encoding.CodePages": { |
||||
"type": "Transitive", |
||||
"resolved": "8.0.0", |
||||
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==", |
||||
"dependencies": { |
||||
"System.Memory": "4.5.5", |
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0" |
||||
} |
||||
}, |
||||
"System.Threading.Tasks.Extensions": { |
||||
"type": "Transitive", |
||||
"resolved": "4.6.0", |
||||
"contentHash": "I5G6Y8jb0xRtGUC9Lahy7FUvlYlnGMMkbuKAQBy8Jb7Y6Yn8OlBEiUOY0PqZ0hy6Ua8poVA1ui1tAIiXNxGdsg==", |
||||
"dependencies": { |
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.0" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2017 Siegfried Pammer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Syntax |
||||
{ |
||||
class DecompilerAstNodeAttribute : System.Attribute |
||||
{ |
||||
} |
||||
} |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue