Created
August 27, 2025 23:08
-
-
Save DanielShuey/32d2e4f785fcd5b9adcd0fb1b7a611f3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Diagnostics; | |
| using System.Linq; | |
| using System.Reflection; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| using Microsoft.CodeAnalysis; | |
| using Microsoft.CodeAnalysis.CSharp; | |
| using Microsoft.CodeAnalysis.CSharp.Syntax; | |
| namespace Namespace { | |
| public static class DelegateAllRewriter { | |
| private static List<SyntaxNode> Search(this SyntaxNode root, SyntaxKind kind, int levels) { | |
| return new NodeSearch(root, kind).Call(levels); | |
| } | |
| private static SyntaxNode Get(this SyntaxNode root, SyntaxKind kind, int levels) { | |
| return new NodeSearch(root, kind).Call(levels) [0]; | |
| } | |
| public static CSharpCompilation Call(CSharpCompilation compilation) { | |
| return new Compiler(compilation).Call(); | |
| // TODO: Visitor pattern get attributes (instead of manual) | |
| } | |
| private class Compiler { | |
| public CSharpCompilation compilation; | |
| public Dictionary<string, SyntaxNode> classes = new Dictionary<string, SyntaxNode>(); | |
| public Compiler(CSharpCompilation compilation) { | |
| this.compilation = compilation; | |
| } | |
| public CSharpCompilation Call() { | |
| foreach (var old_tree in compilation.SyntaxTrees) { | |
| compilation = compilation.ReplaceSyntaxTree(old_tree, new SyntaxTreeHandler(this, old_tree).Call()); | |
| } | |
| return compilation; | |
| } | |
| public void MapClassesToDict(IEnumerable<SyntaxNode> class_nodes, SemanticModel semantic) => | |
| class_nodes.Each(x => classes[semantic.GetDeclaredSymbol(x).Name] = x); | |
| } | |
| private class SyntaxTreeHandler { | |
| private Compiler compiler; | |
| private CSharpCompilation compilation => compiler.compilation; | |
| private Dictionary<string, SyntaxNode> classes => compiler.classes; | |
| private SemanticModel semantic; | |
| private SyntaxTree tree; | |
| private SyntaxNode root; | |
| public SyntaxTreeHandler(Compiler compiler, SyntaxTree tree) { | |
| this.compiler = compiler; | |
| this.tree = tree; | |
| semantic = compilation.GetSemanticModel(tree, ignoreAccessibility : true); | |
| root = tree.GetCompilationUnitRoot(); | |
| } | |
| private SyntaxNode WriteDelegatesFor(SyntaxNode to_class, SyntaxNode for_field) { | |
| var field_name = for_field.Get(SyntaxKind.VariableDeclarator, 1).ToString(); | |
| var field_type = for_field.Get(SyntaxKind.IdentifierName, 1).ToString(); | |
| var method_data = new ClassParser().Tap(x => x.Visit(classes[field_type])).Data; | |
| var new_nodes = method_data.Select(x => { | |
| var member_access = SyntaxFactory.MemberAccessExpression( | |
| SyntaxKind.SimpleMemberAccessExpression, | |
| SyntaxFactory.IdentifierName(field_name), | |
| SyntaxFactory.IdentifierName(x.Identifier.ToString()) | |
| ); | |
| var arguments = SyntaxFactory.ArgumentList( | |
| SyntaxFactory.SeparatedList( | |
| x.Parameters.Parameters.Select(p => | |
| SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Identifier.ToString())) | |
| ) | |
| ) | |
| ); | |
| var method_call = SyntaxFactory.InvocationExpression(member_access, arguments); | |
| var arrow_exp = SyntaxFactory.ArrowExpressionClause(method_call); | |
| return SyntaxFactory.MethodDeclaration(x.ReturnType, x.Identifier) | |
| .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) | |
| .WithParameterList(x.Parameters) | |
| .WithReturnType(x.ReturnType) | |
| .WithExpressionBody(arrow_exp) | |
| .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); | |
| }); | |
| return to_class.InsertNodesAfter(to_class.ChildNodes().Last(), new_nodes); | |
| } | |
| public SyntaxTree Call() { | |
| var class_nodes = root.Search(SyntaxKind.ClassDeclaration, 1); | |
| compiler.MapClassesToDict(class_nodes, semantic); | |
| foreach (var c in class_nodes) { | |
| foreach (var f in c.Search(SyntaxKind.FieldDeclaration, 0)) | |
| foreach (var a in f.Search(SyntaxKind.Attribute, 1)) { | |
| if (a.ToString() == "DelegateAll") | |
| root = root.ReplaceNode(c, WriteDelegatesFor(c, f)); | |
| } | |
| } | |
| return tree.WithRootAndOptions(root, RoslynCompiler.ParseOptions); | |
| } | |
| } | |
| private class ClassParser : CSharpSyntaxRewriter { | |
| public List<MethodData> Data = new List<MethodData>(); | |
| public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) { | |
| if (node.Modifiers.Any(x => x.ToString().Equals("public"))) { | |
| Data.Add(new MethodData { | |
| Identifier = node.Identifier, | |
| Parameters = node.ParameterList, | |
| ReturnType = node.ReturnType, | |
| Modifiers = node.Modifiers | |
| }); | |
| } | |
| return base.VisitMethodDeclaration(node); | |
| } | |
| } | |
| private struct MethodData { | |
| public SyntaxToken Identifier; | |
| public ParameterListSyntax Parameters; | |
| public TypeSyntax ReturnType; | |
| public SyntaxTokenList Modifiers; | |
| } | |
| private class NodeSearch { | |
| private int max_level; | |
| private SyntaxNode root; | |
| private SyntaxKind kind; | |
| private List<SyntaxNode> nodes; | |
| public NodeSearch(SyntaxNode root, SyntaxKind kind) { | |
| this.kind = kind; | |
| this.root = root; | |
| } | |
| private void Traverse(SyntaxNode n, int current_level) { | |
| if (current_level > max_level) return; | |
| foreach (var x in n.ChildNodes()) { | |
| if (x.IsKind(kind)) nodes.Add(x); | |
| Traverse(x, current_level + 1); | |
| } | |
| } | |
| public List<SyntaxNode> Call(int levels) { | |
| max_level = levels; | |
| nodes = new List<SyntaxNode>(); | |
| Traverse(root, 0); | |
| return nodes; | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment