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