Generator (Experiment)
This commit is contained in:
parent
cdaed39301
commit
62ff63c0bb
|
@ -0,0 +1,16 @@
|
|||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen.Attributes;
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedTypeParameter")]
|
||||
[AttributeUsage(AttributeTargets.Struct, AllowMultiple = true)]
|
||||
public class Generate<T> : Attribute
|
||||
{
|
||||
public String[] Defines { get; }
|
||||
|
||||
public Generate(params String[] defines)
|
||||
{
|
||||
Defines = defines;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace InnovEnergy.Lib.SrcGen.Attributes;
|
||||
|
||||
public class NestProperties : Attribute
|
||||
{
|
||||
public String StructName { get; }
|
||||
|
||||
public NestProperties(String structName) => StructName = structName;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeRefactorings;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen;
|
||||
|
||||
|
||||
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(IeCodeRefactoringProvider))]
|
||||
public sealed class IeCodeRefactoringProvider : CodeRefactoringProvider
|
||||
{
|
||||
|
||||
// NOT WORKING
|
||||
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
|
||||
{
|
||||
// var root = await context
|
||||
// .Document
|
||||
// .GetSyntaxRootAsync(context.CancellationToken)
|
||||
// .ConfigureAwait(false);
|
||||
|
||||
await File.WriteAllTextAsync("/home/eef/sync/work/Code/innovenergy/git/csharp/Lib/SrcGen/test.txt", context.Span.ToString());
|
||||
|
||||
var action = CodeAction.Create("IeAction", async token =>
|
||||
{
|
||||
var text = await context.Document.GetTextAsync(token);
|
||||
|
||||
var modified = text.Replace(0, 0, "// " + context.Span + "\n");
|
||||
|
||||
return context.Document.WithText(modified);
|
||||
});
|
||||
|
||||
context.RegisterRefactoring(action);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen;
|
||||
|
||||
public readonly ref struct Rewriter<TRoot, TNode> where TRoot : SyntaxNode where TNode : SyntaxNode
|
||||
{
|
||||
internal readonly TRoot Root;
|
||||
internal readonly IEnumerable<TNode> Descendants;
|
||||
|
||||
internal Rewriter(TRoot root, IEnumerable<TNode> descendants)
|
||||
{
|
||||
Root = root;
|
||||
Descendants = descendants;
|
||||
}
|
||||
|
||||
public Rewriter<TRoot, TNode> Where(Func<TNode, Boolean> predicate)
|
||||
{
|
||||
return new Rewriter<TRoot, TNode>(Root, Descendants.Where(predicate));
|
||||
}
|
||||
|
||||
public Rewriter<TRoot, T> OfType<T>() where T : TNode
|
||||
{
|
||||
return new Rewriter<TRoot, T>(Root, Descendants.OfType<T>());
|
||||
}
|
||||
|
||||
public Rewriter<TRoot, TNode> HasAncestor<T>() where T : SyntaxNode
|
||||
{
|
||||
return new Rewriter<TRoot, TNode>(Root, Descendants.Where(d => d.Ancestors().OfType<T>().Any()));
|
||||
}
|
||||
|
||||
public Rewriter<TRoot, TNode> HasParent<T>() where T : SyntaxNode
|
||||
{
|
||||
return new Rewriter<TRoot, TNode>(Root, Descendants.Where(d => d.Parent is T));
|
||||
}
|
||||
|
||||
public Rewriter<TRoot, TNode> SelectNodes(Func<TNode, IEnumerable<TNode>> nodes)
|
||||
{
|
||||
return new Rewriter<TRoot, TNode>(Root, Descendants.SelectMany(nodes));
|
||||
}
|
||||
|
||||
public Rewriter<TRoot, T> SelectNode<T>(Func<TNode, T> node) where T: SyntaxNode
|
||||
{
|
||||
return new Rewriter<TRoot, T>(Root, Descendants.Select(node));
|
||||
}
|
||||
|
||||
public Rewriter<TRoot, T> GetAncestor<T>() where T : SyntaxNode
|
||||
{
|
||||
return SelectNode(n => n.Ancestors().OfType<T>().First());
|
||||
}
|
||||
|
||||
public TRoot Replace(SyntaxNode syntaxNode)
|
||||
{
|
||||
return Root.ReplaceNodes(Descendants, (_, _) => syntaxNode);
|
||||
}
|
||||
|
||||
public TRoot Replace(Func<TNode, SyntaxNode> map)
|
||||
{
|
||||
return Root.ReplaceNodes(Descendants, (_, n) => map(n));
|
||||
}
|
||||
|
||||
public TRoot Remove() => Remove(SyntaxRemoveOptions.KeepNoTrivia);
|
||||
|
||||
public TRoot Remove(SyntaxRemoveOptions options) => Root.RemoveNodes(Descendants, options)!;
|
||||
}
|
||||
|
||||
public static class Rewrite
|
||||
{
|
||||
public static Rewriter<R, SyntaxNode> EditNodes<R>(this R root) where R : SyntaxNode
|
||||
{
|
||||
return new Rewriter<R, SyntaxNode>(root, root.DescendantNodes());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
using InnovEnergy.Lib.Utils;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen;
|
||||
|
||||
public static class GenerateAttributes
|
||||
{
|
||||
public static async Task Generate(Project project, Compilation compilation)
|
||||
{
|
||||
var syntaxTrees = await project
|
||||
.Documents
|
||||
.Where(d => d.Project == project)
|
||||
.Where(d => d.SupportsSyntaxTree)
|
||||
.Select(async d => await d.GetSyntaxRootAsync())
|
||||
.WhenAll();
|
||||
|
||||
syntaxTrees.NotNull()
|
||||
.SelectMany(c => c.GetGenerateAttributes())
|
||||
.ForEach(GenerateFile);
|
||||
|
||||
String GenerateFile(AttributeSyntax attribute)
|
||||
{
|
||||
var derivedType = attribute.GetAncestor<StructDeclarationSyntax>();
|
||||
var defineArgs = attribute.ArgumentList?.Arguments ?? Enumerable.Empty<SyntaxNode>();
|
||||
var filePath = derivedType.SyntaxTree.FilePath;
|
||||
var derivedName = derivedType.Identifier.ValueText;
|
||||
var baseRef = attribute.GetGenericArgument();
|
||||
var baseType = compilation.GetDefinition<StructDeclarationSyntax>(baseRef);
|
||||
var baseName = baseType.Identifier.ValueText;
|
||||
var baseFile = baseType.GetAncestor<CompilationUnitSyntax>();
|
||||
var generatedPath = filePath.GetGeneratedPath(baseName);
|
||||
|
||||
var defines = defineArgs
|
||||
.Select(n => n.ToString())
|
||||
.Select(s => s.Replace("\"", "").ToUpper())
|
||||
.Select(s => $"#define {s}");
|
||||
|
||||
Console.WriteLine($"Generating {generatedPath}");
|
||||
|
||||
var code = GenerateCode();
|
||||
|
||||
var fileContents = Utils.AutoGeneratedMessage(nameof(GenerateAttributes))
|
||||
+ defines.JoinLines() + "\n"
|
||||
+ code;
|
||||
|
||||
File.WriteAllText(generatedPath, fileContents);
|
||||
|
||||
return generatedPath;
|
||||
|
||||
|
||||
String GenerateCode()
|
||||
{
|
||||
try
|
||||
{
|
||||
return baseFile
|
||||
.AddPartialModifier()
|
||||
.ReplaceIdentifiers(baseName, derivedName)
|
||||
.RemoveNotImplemented()
|
||||
.GetText()
|
||||
.ToString();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return $"Failed to generate source for {filePath}\n\n{e}"
|
||||
.SplitLines()
|
||||
.Select(l => $"// {l}")
|
||||
.JoinLines();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
using InnovEnergy.Lib.SrcGen.Trees;
|
||||
using InnovEnergy.Lib.Utils;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Sawmill;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen;
|
||||
|
||||
|
||||
|
||||
public static class NestProperties
|
||||
{
|
||||
private static readonly SyntaxToken PublicKeyword = Token(SyntaxKind.PublicKeyword);
|
||||
|
||||
public static async Task Generate(Project project, Compilation compilation)
|
||||
{
|
||||
var syntaxTrees = await project
|
||||
.Documents
|
||||
.Where(d => d.Project == project)
|
||||
.Where(d => d.SupportsSyntaxTree)
|
||||
.Select(async d => await d.GetSyntaxRootAsync())
|
||||
.WhenAll();
|
||||
|
||||
syntaxTrees.NotNull()
|
||||
.SelectMany(c => c.GetNestPropertiesAttributes())
|
||||
.ForEach(GenerateFile);
|
||||
|
||||
String GenerateFile(AttributeSyntax attribute)
|
||||
{
|
||||
var typeName = attribute.ArgumentList!.Arguments.Single().ToString().Replace("\"", "");
|
||||
var type = attribute.GetAncestor<TypeDeclarationSyntax>();
|
||||
var nameSpace = attribute.GetAncestor<NamespaceDeclarationSyntax>();
|
||||
var filePath = type.SyntaxTree.FilePath;
|
||||
var generatedPath = filePath.GetGeneratedPath(nameof(NestProperties));
|
||||
|
||||
var recordProperties = type
|
||||
.Members
|
||||
.OfType<PropertyDeclarationSyntax>()
|
||||
.Where(m => m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.PublicKeyword)))
|
||||
.ToList();
|
||||
|
||||
var propNames = recordProperties.Select(m => m.Identifier.ValueText);
|
||||
|
||||
var prefixTree = PrefixTree.Create(propNames);
|
||||
|
||||
prefixTree.WriteLine();
|
||||
Console.WriteLine("$$$$$$$$$$$$$");
|
||||
|
||||
|
||||
var wrappedTypeName = type.Identifier.ValueText;
|
||||
|
||||
var records = new List<RecordDeclarationSyntax>();
|
||||
|
||||
prefixTree.Fold<Tree<String>, MemberDeclarationSyntax>((memory, tree) =>
|
||||
{
|
||||
var path = tree
|
||||
.Ancestors
|
||||
.Prepend(tree)
|
||||
.Aggregate("", (a, b) => b.Node + a);
|
||||
|
||||
if (tree.IsLeaf)
|
||||
{
|
||||
var prop = recordProperties.First(p => p.Identifier.ValueText == path);
|
||||
|
||||
var writable = prop
|
||||
.DescendantNodes()
|
||||
.OfType<AccessorDeclarationSyntax>()
|
||||
.Any(d => d.IsKind(SetAccessorDeclaration)
|
||||
&& !d.DescendantTokens().Any(t => t.IsKind(PrivateKeyword)));
|
||||
|
||||
return CreateAccessorProperty
|
||||
(
|
||||
prop.Type,
|
||||
tree.Node,
|
||||
path,
|
||||
writable
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
var children = memory.ToArray();
|
||||
|
||||
var rec = CreateWrapperRecord(typeName + path, wrappedTypeName, children);
|
||||
records.Add(rec);
|
||||
|
||||
return CreateWrapperProperty(IdentifierName(typeName + path), Identifier(tree.Node));
|
||||
}
|
||||
});
|
||||
|
||||
//x.NormalizeWhitespace().GetText().WriteLine();
|
||||
|
||||
records.Reverse();
|
||||
records.ForEach(r => r.NormalizeWhitespace().WriteLine("\n"));
|
||||
|
||||
|
||||
|
||||
Console.WriteLine($"Generating {generatedPath}");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return "";
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static CompilationUnitSyntax CreateCompilationUnit(NameSyntax nameSpaceName, params MemberDeclarationSyntax[] members)
|
||||
{
|
||||
var nameSpace = NamespaceDeclaration(nameSpaceName)
|
||||
.WithMembers(members.Apply(List));
|
||||
|
||||
return CompilationUnit().WithMembers(nameSpace.Apply(SingletonList<MemberDeclarationSyntax>));
|
||||
}
|
||||
|
||||
private static RecordDeclarationSyntax CreateWrapperRecord(String structName,
|
||||
String wrappedTypeName,
|
||||
IEnumerable<MemberDeclarationSyntax> properties)
|
||||
{
|
||||
const String self = "Self";
|
||||
|
||||
var wrappedType = IdentifierName(wrappedTypeName);
|
||||
var recId = Identifier(structName);
|
||||
var modifiers = TokenList(Token(SyntaxKind.PublicKeyword), Token(ReadOnlyKeyword));
|
||||
|
||||
|
||||
|
||||
|
||||
//properties.Prepend<MemberDeclarationSyntax>(selfProperty).Prepend(constructor).Apply(List);
|
||||
|
||||
// var structMembers = new MemberDeclarationSyntax[] { selfProperty, constructor }
|
||||
// .Apply(List);
|
||||
|
||||
var recMembers = properties.Apply(List);
|
||||
|
||||
|
||||
var recParams = self
|
||||
.Apply(Identifier)
|
||||
.Apply(Parameter)
|
||||
.WithType(wrappedType)
|
||||
.Apply(SingletonSeparatedList)
|
||||
.Apply(ParameterList);
|
||||
|
||||
return RecordDeclaration(Token(RecordKeyword), recId)
|
||||
.WithModifiers(modifiers)
|
||||
.WithClassOrStructKeyword(Token(StructKeyword))
|
||||
.WithParameterList(recParams)
|
||||
.WithModifiers(modifiers)
|
||||
.WithOpenBraceToken(Token(OpenBraceToken))
|
||||
.WithMembers(recMembers)
|
||||
.WithCloseBraceToken(Token(CloseBraceToken));
|
||||
|
||||
//return /*StructDeclaration(structId)*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static PropertyDeclarationSyntax CreateAccessorProperty(TypeSyntax propertyTypeName,
|
||||
String propertyName,
|
||||
String parentPropertyName,
|
||||
Boolean writeable)
|
||||
{
|
||||
const String self = "Self";
|
||||
const String value = "value";
|
||||
|
||||
var propertyModifier = SyntaxKind
|
||||
.PublicKeyword
|
||||
.Apply(Token)
|
||||
.Apply(TokenList);
|
||||
|
||||
var semicolonToken = Token(SemicolonToken);
|
||||
|
||||
var accessExpression = MemberAccessExpression
|
||||
(
|
||||
SimpleMemberAccessExpression,
|
||||
IdentifierName(self),
|
||||
IdentifierName(parentPropertyName)
|
||||
);
|
||||
|
||||
var getterBody = ArrowExpressionClause(accessExpression);
|
||||
|
||||
var getter = GetAccessorDeclaration
|
||||
.Apply(AccessorDeclaration)
|
||||
.WithExpressionBody(getterBody)
|
||||
.WithSemicolonToken(semicolonToken);
|
||||
|
||||
var setterBody = AssignmentExpression(SimpleAssignmentExpression, accessExpression, IdentifierName(value))
|
||||
.Apply(ArrowExpressionClause);
|
||||
|
||||
var setter = SetAccessorDeclaration
|
||||
.Apply(AccessorDeclaration)
|
||||
.WithExpressionBody(setterBody)
|
||||
.WithSemicolonToken(semicolonToken);
|
||||
|
||||
var accessors = writeable
|
||||
? new[] { getter, setter }
|
||||
: new[] { getter };
|
||||
|
||||
var accessorsList = accessors
|
||||
.Apply(List)
|
||||
.Apply(AccessorList);
|
||||
|
||||
var property = PropertyDeclaration
|
||||
(
|
||||
type : propertyTypeName,
|
||||
identifier: Identifier(propertyName)
|
||||
);
|
||||
|
||||
return property
|
||||
.WithModifiers(propertyModifier)
|
||||
.WithAccessorList(accessorsList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static PropertyDeclarationSyntax CreateWrapperProperty(TypeSyntax type, SyntaxToken identifier)
|
||||
{
|
||||
var modifiers = SyntaxKind
|
||||
.PublicKeyword
|
||||
.Apply(Token)
|
||||
.Apply(TokenList);
|
||||
|
||||
var self = IdentifierName("Self")
|
||||
.Apply(Argument)
|
||||
.Apply(SingletonSeparatedList)
|
||||
.Apply(ArgumentList);
|
||||
|
||||
var body = ObjectCreationExpression(type)
|
||||
.WithArgumentList(self)
|
||||
.Apply(ArrowExpressionClause);
|
||||
|
||||
return PropertyDeclaration(type, identifier)
|
||||
.WithModifiers(modifiers)
|
||||
.WithExpressionBody(body)
|
||||
.WithSemicolonToken(Token(SemicolonToken));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
using InnovEnergy.Lib.SrcGen.Trees;
|
||||
using Microsoft.Build.Locator;
|
||||
using Microsoft.CodeAnalysis.MSBuild;
|
||||
using Sawmill;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen;
|
||||
|
||||
|
||||
|
||||
using S = IEnumerable<String>;
|
||||
|
||||
//public record Node(String Prefix, Node Children);
|
||||
|
||||
public static class Program
|
||||
{
|
||||
|
||||
private const String GeneratedCodeMsg = "// This file has been automatically generated, do not edit!\n\n";
|
||||
|
||||
public static async Task Main(String[] args)
|
||||
{
|
||||
//Nest();
|
||||
|
||||
var projectPath = args.FirstOrDefault()
|
||||
?? "/home/eef/sync/work/Code/innovenergy/git/csharp/Lib/Devices/Battery48TL/Battery48TL.csproj";
|
||||
|
||||
if (projectPath is null)
|
||||
throw new ArgumentException(nameof(projectPath));
|
||||
|
||||
MSBuildLocator.RegisterDefaults(); // WTF
|
||||
|
||||
using var workspace = MSBuildWorkspace.Create();
|
||||
await workspace.OpenProjectAsync(projectPath);
|
||||
|
||||
var solution = workspace.CurrentSolution;
|
||||
var project = solution.Projects.Single(p => p.FilePath == projectPath);
|
||||
var compilation = await project.GetCompilationAsync();
|
||||
|
||||
if (compilation is null)
|
||||
throw new Exception($"Project {projectPath} failed to build!");
|
||||
|
||||
await GenerateAttributes.Generate(project, compilation);
|
||||
await NestProperties.Generate(project, compilation);
|
||||
}
|
||||
|
||||
|
||||
private static void Nest()
|
||||
{
|
||||
var strings = new[]
|
||||
{
|
||||
"AcCurrent",
|
||||
"AcVoltage",
|
||||
"AcPowerReactive",
|
||||
"AcPowerActive",
|
||||
"DcCurrent",
|
||||
"DcVoltage",
|
||||
"Temperature",
|
||||
"Soc"
|
||||
};
|
||||
|
||||
|
||||
var tree1 = new ForwardTree<String>("", strings.Select(s => new ForwardTree<String>(s)).ToList());
|
||||
|
||||
static ForwardTree<String> GroupByPrefix(ForwardTree<String> tree)
|
||||
{
|
||||
var newChildren = tree
|
||||
.Children
|
||||
.Where(s => s.Node.Length > 0)
|
||||
.GroupBy(s => s.Node[..1], s => new ForwardTree<String>(s.Node[1..]))
|
||||
.Select(g => GroupByPrefix(new ForwardTree<String>(g.Key, g.ToList())))
|
||||
.ToList();
|
||||
|
||||
return newChildren.Count == 0
|
||||
? tree // leaf
|
||||
: new ForwardTree<String>(tree.Node, newChildren);
|
||||
}
|
||||
|
||||
|
||||
ForwardTree<String> ConcatUnaryNodes(ForwardTree<String> tree)
|
||||
{
|
||||
return tree.Children.Count == 1 && tree.Children[0] is var child
|
||||
? new ForwardTree<String>(tree.Node + child.Node, child.Children)
|
||||
: tree;
|
||||
}
|
||||
|
||||
var tree2 = tree1.Rewrite(GroupByPrefix);
|
||||
var tree3 = tree2.Rewrite(ConcatUnaryNodes);
|
||||
|
||||
|
||||
Console.WriteLine(tree1);
|
||||
Console.WriteLine("\n======\n");
|
||||
Console.WriteLine(tree2);
|
||||
Console.WriteLine("\n======\n");
|
||||
Console.WriteLine(tree3);
|
||||
}
|
||||
|
||||
|
||||
private static Func<ForwardTree<String>, ForwardTree<String>> RewriteTree(Func<ForwardTree<String>, ForwardTree<String>> rec)
|
||||
=> tree
|
||||
=> tree.RewriteIter(n =>
|
||||
{
|
||||
if (n.Children.Count == 0 || n.Children.SelectMany(c => c.Children).Any())
|
||||
return n;
|
||||
|
||||
var groups = n
|
||||
.Children
|
||||
.Where(s => s.Node.Length > 0)
|
||||
.GroupBy(s => s.Node[..1], s => rec(new ForwardTree<String>(s.Node[1..])))
|
||||
.Select(g => new ForwardTree<String>(g.Key, g.ToList()))
|
||||
.ToList();
|
||||
|
||||
if (groups.Count == 0)
|
||||
return n;
|
||||
|
||||
return new ForwardTree<String>(n.Node, groups);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// static IReadOnlyList<StringTree> GetChildren(IEnumerable<String> strings)
|
||||
// {
|
||||
// return strings
|
||||
// .Where(s => s.Length > 0)
|
||||
// .GroupBy(s => s[..1], s => s[1..])
|
||||
// .Select(l =>
|
||||
// {
|
||||
// var children = GetChildren(l);
|
||||
// return children.Count == 1
|
||||
// ? new StringTree(l.Key + children[0].Node, children[0].Children)
|
||||
// : new StringTree(l.Key, children);
|
||||
// })
|
||||
// .ToList();
|
||||
// }
|
||||
//var t2 = new Tree<(String prefix, String[] strings)>(stringNode, );
|
||||
|
||||
// var strs = Func<S, S> (Func<S, S> rec)
|
||||
// => S (ss)
|
||||
// => ss.Where(s => s.Length > 0)
|
||||
// .GroupBy(s => s[..1], s => s[1..])
|
||||
// .SelectMany(g=> !g.Any() ? new[] { g.Key } : rec(g))
|
||||
//
|
||||
// ;
|
||||
//
|
||||
// var g = Func<Int32, Int32> (Func<Int32, Int32> h)
|
||||
// => Int32 (Int32 m)
|
||||
// => m > 1 ? h(m - 1) + h(m - 2) : m;
|
||||
//
|
||||
// var fib = Combinator.Y(g);
|
||||
// var fib2 = Combinator.Y(strs);
|
||||
//
|
||||
// var y = fib(5);
|
||||
// var x = fib2(strings).ToList();
|
||||
//
|
|
@ -0,0 +1,182 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen;
|
||||
|
||||
public static class SyntaxUtils
|
||||
{
|
||||
// const String DebugFile = "/home/eef/sync/work/Code/innovenergy/git/csharp/Lib/SrcGen/Debug.txt";
|
||||
//
|
||||
// static SyntaxUtils() => File.WriteAllText(DebugFile, "");
|
||||
|
||||
public static Boolean HasExtendsAttribute(this TypeDeclarationSyntax td)
|
||||
{
|
||||
return td
|
||||
.GetAttributes()
|
||||
.Select(a => a.Name)
|
||||
.OfType<GenericNameSyntax>()
|
||||
.Any(n => n.Identifier.ValueText == "Extends");
|
||||
}
|
||||
|
||||
|
||||
public static T GetAncestor<T>(this SyntaxNode sn) where T: SyntaxNode
|
||||
{
|
||||
return sn
|
||||
.Ancestors()
|
||||
.OfType<T>()
|
||||
.First();
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<AttributeSyntax> GetGenerateAttributes(this SyntaxNode sn)
|
||||
{
|
||||
return sn
|
||||
.DescendantNodes()
|
||||
.Where(n => n is AttributeSyntax { Name : GenericNameSyntax { Identifier.ValueText: "Generate" } })
|
||||
.OfType<AttributeSyntax>();
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<AttributeSyntax> GetNestPropertiesAttributes(this SyntaxNode sn)
|
||||
{
|
||||
return sn
|
||||
.DescendantNodes()
|
||||
.Where(n => n is AttributeSyntax { Name : IdentifierNameSyntax { Identifier.ValueText: "NestProperties" } })
|
||||
.OfType<AttributeSyntax>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static IEnumerable<AttributeSyntax> GetAttributes(this MemberDeclarationSyntax member)
|
||||
{
|
||||
return member
|
||||
.AttributeLists
|
||||
.SelectMany(al => al.Attributes);
|
||||
}
|
||||
|
||||
// public static T Debug<T>(this T t, String? title = null)
|
||||
// {
|
||||
// var prefix = title is null ? "" : title + ": ";
|
||||
// var contents = t is null ? "<null>" : t.ToString();
|
||||
//
|
||||
// File.AppendAllText(DebugFile, prefix + contents + '\n');
|
||||
// return t;
|
||||
// }
|
||||
|
||||
public static IEnumerable<AttributeSyntax> GetAttributes(this SyntaxNode node, String attributeName)
|
||||
{
|
||||
return node
|
||||
.DescendantTokens()
|
||||
.Where(st => st.ValueText == attributeName)
|
||||
.Where(st => st.IsKind(SyntaxKind.IdentifierToken))
|
||||
.Select(st => st.Parent?.Parent)
|
||||
.OfType<AttributeSyntax>();
|
||||
}
|
||||
|
||||
public static IEnumerable<T> ChildNodes<T>(this SyntaxNode node) where T : SyntaxNode
|
||||
{
|
||||
return node.ChildNodes().OfType<T>();
|
||||
}
|
||||
|
||||
public static IEnumerable<T> DescendantNodes<T>(this SyntaxNode node) where T : SyntaxNode
|
||||
{
|
||||
return node.DescendantNodes().OfType<T>();
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public static T ReplaceIdentifiers<T>(this T root, String idToReplace, String replacementId) where T: SyntaxNode
|
||||
{
|
||||
var newIdentifier = SyntaxFactory.Identifier(replacementId);
|
||||
|
||||
return root
|
||||
.EditNodes()
|
||||
.OfType<IdentifierNameSyntax>()
|
||||
.Where(n => n.Identifier.ValueText == idToReplace)
|
||||
.Replace(n => n.WithIdentifier(newIdentifier).WithTriviaFrom(n))
|
||||
|
||||
.EditNodes()
|
||||
.OfType<ConstructorDeclarationSyntax>()
|
||||
.Where(n => n.Identifier.ValueText == idToReplace)
|
||||
.Replace(n => n.WithIdentifier(newIdentifier).WithTriviaFrom(n))
|
||||
|
||||
.EditNodes()
|
||||
.OfType<TypeDeclarationSyntax>()
|
||||
.Where(n => n.Identifier.ValueText == idToReplace)
|
||||
.Replace(n => n.WithIdentifier(newIdentifier.WithTrailingTrivia(NewLine)));
|
||||
}
|
||||
|
||||
|
||||
private static readonly SyntaxTrivia NewLine = SyntaxFactory.SyntaxTrivia(SyntaxKind.EndOfLineTrivia, "\n");
|
||||
private static readonly SyntaxTrivia WhiteSpace = SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
|
||||
|
||||
private static readonly SyntaxToken PartialKeyword = SyntaxFactory
|
||||
.Token(SyntaxKind.PartialKeyword)
|
||||
.WithTrailingTrivia(WhiteSpace);
|
||||
|
||||
public static CompilationUnitSyntax AddPartialModifier(this CompilationUnitSyntax baseFile)
|
||||
{
|
||||
return baseFile
|
||||
.EditNodes()
|
||||
.OfType<StructDeclarationSyntax>()
|
||||
.Where(s => !s.Modifiers.Contains(PartialKeyword))
|
||||
.Replace(s => s.AddModifiers(PartialKeyword));
|
||||
}
|
||||
|
||||
|
||||
public static T GetDefinition<T>(this Compilation compilation, TypeSyntax baseTypeDeclaration) where T : SyntaxNode
|
||||
{
|
||||
return compilation
|
||||
.GetDefinitions(baseTypeDeclaration)
|
||||
.OfType<T>()
|
||||
.Single();
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetDefinitions<T>(this Compilation compilation, TypeSyntax baseTypeDeclaration) where T : SyntaxNode
|
||||
{
|
||||
return compilation
|
||||
.GetDefinitions(baseTypeDeclaration)
|
||||
.OfType<T>();
|
||||
}
|
||||
|
||||
public static IEnumerable<SyntaxNode> GetDefinitions(this Compilation compilation, TypeSyntax baseTypeDeclaration)
|
||||
{
|
||||
return compilation
|
||||
.GetSemanticModel(baseTypeDeclaration.SyntaxTree)
|
||||
.GetTypeInfo(baseTypeDeclaration)
|
||||
.Type?
|
||||
.DeclaringSyntaxReferences
|
||||
.Select(r => r.GetSyntax())
|
||||
?? Enumerable.Empty<SyntaxNode>();
|
||||
}
|
||||
|
||||
public static IEnumerable<TypeSyntax> GetGenericArguments(this AttributeSyntax attribute) => attribute.Name switch
|
||||
{
|
||||
GenericNameSyntax gns => gns.TypeArgumentList.Arguments,
|
||||
_ => Enumerable.Empty<TypeSyntax>()
|
||||
};
|
||||
|
||||
public static TypeSyntax GetGenericArgument(this AttributeSyntax attribute) => attribute.GetGenericArguments().Single();
|
||||
|
||||
public static CompilationUnitSyntax RemoveNotImplemented(this CompilationUnitSyntax baseFile)
|
||||
{
|
||||
return baseFile
|
||||
.EditNodes()
|
||||
.OfType<IdentifierNameSyntax>()
|
||||
.Where(i => i.Identifier.ValueText == nameof(NotImplementedException))
|
||||
.HasAncestor<ThrowExpressionSyntax>()
|
||||
.GetAncestor<MemberDeclarationSyntax>()
|
||||
.Remove();
|
||||
}
|
||||
|
||||
public static String GetGeneratedPath(this String path, String postfix = "generated")
|
||||
{
|
||||
var ext = Path.GetExtension(path);
|
||||
var name = Path.GetFileNameWithoutExtension(path);
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
|
||||
return $"{dir}/{name}.{postfix}{ext}";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using InnovEnergy.Lib.Utils;
|
||||
using Sawmill;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen.Trees;
|
||||
|
||||
|
||||
public class ForwardTree<T> : IRewritable<ForwardTree<T>> where T : notnull
|
||||
{
|
||||
public T Node { get; }
|
||||
public IReadOnlyList<ForwardTree<T>> Children { get; }
|
||||
public Boolean IsLeaf => Children.Count == 0;
|
||||
|
||||
public ForwardTree(T node) : this(node, Array.Empty<ForwardTree<T>>())
|
||||
{
|
||||
}
|
||||
|
||||
public ForwardTree(T node, IReadOnlyList<ForwardTree<T>> children)
|
||||
{
|
||||
Node = node;
|
||||
Children = children;
|
||||
}
|
||||
|
||||
public ForwardTree<T> WithNode(T node) => new ForwardTree<T>(node, Children);
|
||||
public ForwardTree<T> WithNode(Func<T, T> makeNode) => new ForwardTree<T>(makeNode(Node), Children);
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
var node = Node.ToString()!;
|
||||
return Children.Aggregate(node, (s, c) => s + "\n" + c.ToString().Indent(node.Length));
|
||||
}
|
||||
|
||||
Int32 IRewritable<ForwardTree<T>>.CountChildren() => Children.Count;
|
||||
|
||||
void IRewritable<ForwardTree<T>>.GetChildren(Span<ForwardTree<T>> childrenReceiver)
|
||||
{
|
||||
for (var i = 0; i < Children.Count; i++)
|
||||
childrenReceiver[i] = Children[i];
|
||||
}
|
||||
|
||||
ForwardTree<T> IRewritable<ForwardTree<T>>.SetChildren(ReadOnlySpan<ForwardTree<T>> newChildren)
|
||||
{
|
||||
return new ForwardTree<T>(Node, newChildren.ToArray());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using Sawmill;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen.Trees;
|
||||
|
||||
using T = ForwardTree<String>;
|
||||
|
||||
public static class PrefixTree
|
||||
{
|
||||
public static Tree<String> Create(IEnumerable<String> strings, String root = "")
|
||||
{
|
||||
var children = strings.Select(s => new T(s)).ToList();
|
||||
var tree = new T(root, children);
|
||||
|
||||
var forwardTree = tree
|
||||
.Rewrite(GroupByPrefix)
|
||||
.Rewrite(ConcatUnaryNodes);
|
||||
|
||||
return new Tree<String>(forwardTree);
|
||||
}
|
||||
|
||||
private static T GroupByPrefix(T tree)
|
||||
{
|
||||
var newChildren = tree
|
||||
.Children
|
||||
.Where(s => s.Node.Length > 0)
|
||||
.GroupBy(s => s.Node[..1], s => new T(s.Node[1..]))
|
||||
.Select(g => GroupByPrefix(new T(g.Key, g.ToList())))
|
||||
.ToList();
|
||||
|
||||
return newChildren.Count == 0
|
||||
? tree // leaf
|
||||
: new T(tree.Node, newChildren);
|
||||
}
|
||||
|
||||
private static T ConcatUnaryNodes(ForwardTree<String> tree)
|
||||
{
|
||||
return tree.Children.Count == 1 && tree.Children[0] is var child
|
||||
? new T(tree.Node + child.Node, child.Children)
|
||||
: tree;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using InnovEnergy.Lib.Utils;
|
||||
using Sawmill;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen.Trees;
|
||||
|
||||
public class Tree<T> :IRewritable<Tree<T>> where T : notnull
|
||||
{
|
||||
private readonly ForwardTree<T> _Tree;
|
||||
public Tree<T>? Parent { get; }
|
||||
|
||||
public Boolean IsRoot => Parent is null;
|
||||
public Boolean IsLeaf => !Children.Any();
|
||||
|
||||
public Tree(ForwardTree<T> tree, Tree<T>? parent = default)
|
||||
{
|
||||
_Tree = tree;
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
public T Node => _Tree.Node;
|
||||
|
||||
public IEnumerable<Tree<T>> Children => _Tree
|
||||
.Children
|
||||
.Select(c => new Tree<T>(c, this));
|
||||
|
||||
public IEnumerable<Tree<T>> Ancestors => Parent.Unfold(t => t.Parent);
|
||||
|
||||
public override String ToString() => _Tree.ToString();
|
||||
|
||||
public Tree<T> Root => this.Unfold(t => t.Parent).Last();
|
||||
|
||||
Int32 IRewritable<Tree<T>>.CountChildren() => _Tree.Children.Count;
|
||||
|
||||
void IRewritable<Tree<T>>.GetChildren(Span<Tree<T>> childrenReceiver)
|
||||
{
|
||||
var i = 0;
|
||||
foreach (var child in Children)
|
||||
childrenReceiver[i++] = child;
|
||||
}
|
||||
|
||||
Tree<T> IRewritable<Tree<T>>.SetChildren(ReadOnlySpan<Tree<T>> newChildren)
|
||||
{
|
||||
var forwardTree = new ForwardTree<T>(Node, _Tree.Children.ToArray());
|
||||
|
||||
return new Tree<T>(forwardTree, Parent);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
using InnovEnergy.Lib.Utils;
|
||||
|
||||
namespace InnovEnergy.Lib.SrcGen.Trees;
|
||||
|
||||
public readonly struct TreeStruct<T> where T : notnull
|
||||
{
|
||||
[Obsolete] public TreeStruct() => throw new Exception("Forbidden");
|
||||
|
||||
public TreeStruct(T node, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
Node = node;
|
||||
_GetChildren = getChildren;
|
||||
}
|
||||
|
||||
public IEnumerable<TreeStruct<T>> Children
|
||||
{
|
||||
get
|
||||
{
|
||||
var getChildren = _GetChildren;
|
||||
return _GetChildren(Node).Select(c => new TreeStruct<T>(c, getChildren));
|
||||
}
|
||||
}
|
||||
|
||||
public T Node { get; }
|
||||
|
||||
private readonly Func<T, IEnumerable<T>> _GetChildren;
|
||||
|
||||
public IEnumerable<T> TraverseDepthFirstPostOrder() => TreeTraversal.TraverseDepthFirstPostOrder(Node, _GetChildren);
|
||||
public IEnumerable<T> TraverseDepthFirstPreOrder() => TreeTraversal.TraverseDepthFirstPreOrder(Node, _GetChildren);
|
||||
public IEnumerable<T> TraverseBreadthFirst() => TreeTraversal.TraverseBreadthFirst(Node, _GetChildren);
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace InnovEnergy.Lib.SrcGen;
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
public static String AutoGeneratedMessage(String name)
|
||||
{
|
||||
return $"// This file has been automatically generated by {name}, do not edit!\n\n";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue