Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

547 Static classes and libraries on 1.0 #548

Merged
merged 4 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 8 additions & 138 deletions Cql/CodeGeneration.NET/CSharpSourceCodeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,111 +198,36 @@ private void writeClass(DefinitionDictionary<LambdaExpression> definitions,
writer.WriteLine(indentLevel, $"[CqlLibrary(\"{libraryAttribute}\", \"{versionAttribute}\")]");
var className = VariableNameGenerator.NormalizeIdentifier(libraryName);
if (PartialClass)
writer.WriteLine(indentLevel, $"partial class {className}");
writer.WriteLine(indentLevel, $"public partial class {className}");
else
writer.WriteLine(indentLevel, $"public class {className}");
writer.WriteLine(indentLevel, "{");
writer.WriteLine();
indentLevel += 1;
// Class
{
writer.WriteLine(indentLevel, $"public static {className} Instance {{ get; }} = new();");
writer.WriteLine();

writer.WriteLine(indentLevel, $"{AccessModifierString(ContextAccessModifier)} CqlContext context;");
writer.WriteLine();
writeCachedValues(definitions, libraryName, writer, indentLevel);

// Write constructor
writer.WriteLine(indentLevel, $"public {className}(CqlContext context)");
writer.WriteLine(indentLevel, "{");
{
indentLevel += 1;

writer.WriteLine(indentLevel, "this.context = context ?? throw new ArgumentNullException(\"context\");");
writer.WriteLine();

writeDependencies(dependencyGraph, libraryNameToClassName, libraryName, writer, indentLevel);
writer.WriteLine();
writeCachedValueNames(definitions, libraryName, writer, indentLevel);
indentLevel -= 1;
}
writer.WriteLine(indentLevel, "}");

WriteLibraryMembers(writer, dependencyGraph, libraryName, libraryNameToClassName!, indentLevel);
writeMemoizedInstanceMethods(definitions, libraryName, writer, indentLevel);
writeMethods(definitions, libraryName, writer, indentLevel);
indentLevel -= 1;
writer.WriteLine(indentLevel, "}");
}
}

private void writeMemoizedInstanceMethods(DefinitionDictionary<LambdaExpression> definitions, string libraryName, StreamWriter writer, int indentLevel)
private void writeMethods(DefinitionDictionary<LambdaExpression> definitions, string libraryName, StreamWriter writer, int indentLevel)
{
foreach (var kvp in definitions.DefinitionsForLibrary(libraryName))
{
foreach (var overload in kvp.Value)
{
definitions.TryGetTags(libraryName, kvp.Key, overload.Signature, out var tags);
WriteMemoizedInstanceMethod(libraryName, writer, indentLevel, kvp.Key, overload.T, tags);
writeMethod(libraryName, writer, indentLevel, kvp.Key, overload.T, tags);
writer.WriteLine();
}
}
}

private void writeCachedValueNames(DefinitionDictionary<LambdaExpression> definitions, string libraryName, StreamWriter writer, int indentLevel)
{
foreach (var kvp in definitions.DefinitionsForLibrary(libraryName))
{
foreach (var overload in kvp.Value)
{
if (isDefinition(overload.Item2))
{
var methodName = VariableNameGenerator.NormalizeIdentifier(kvp.Key);
var cachedValueName = DefinitionCacheKeyForMethod(methodName!);
var returnType = ExpressionConverter.PrettyTypeName(overload.Item2.ReturnType);
var privateMethodName = PrivateMethodNameFor(methodName!);
writer.WriteLine(indentLevel, $"{cachedValueName} = new Lazy<{returnType}>(this.{privateMethodName});");
}
}
}
}

private static void writeDependencies(DirectedGraph dependencyGraph, Func<string?, string?> libraryNameToClassName, string libraryName, StreamWriter writer, int indentLevel)
{
var node = dependencyGraph.Nodes[libraryName];
var requiredLibraries = node.ForwardEdges?
.Select(edge => edge.ToId)
.Except(new[] { dependencyGraph.EndNode.NodeId })
.Distinct();
foreach (var dependentLibrary in requiredLibraries!)
{
var typeName = libraryNameToClassName!(dependentLibrary);
var memberName = typeName;
writer.WriteLine(indentLevel, $"{memberName} = new {typeName}(context);");
}
}

private void writeCachedValues(DefinitionDictionary<LambdaExpression> definitions, string libraryName, StreamWriter writer, int indentLevel)
{
writer.WriteLine(indentLevel, "#region Cached values");
writer.WriteLine();
var accessModifier = AccessModifierString(DefinesAccessModifier);
foreach (var kvp in definitions.DefinitionsForLibrary(libraryName))
{
foreach (var overload in kvp.Value)
{
if (isDefinition(overload.T))
{
var methodName = VariableNameGenerator.NormalizeIdentifier(kvp.Key);
var cachedValueName = DefinitionCacheKeyForMethod(methodName!);
var returnType = ExpressionConverter.PrettyTypeName(overload.T.ReturnType);
writer.WriteLine(indentLevel, $"{accessModifier} Lazy<{returnType}> {cachedValueName};");
}
}
}
writer.WriteLine();
writer.WriteLine(indentLevel, "#endregion");
}


private void writeTupleTypes(IEnumerable<Type> tupleTypes, Func<string, Stream> libraryNameToStream, bool closeStream)
{
if (tupleTypes.Any())
Expand Down Expand Up @@ -346,36 +271,6 @@ private static bool isDefinition(LambdaExpression overload) =>
overload.Parameters.Count == 1
&& overload.Parameters[0].Type == typeof(CqlContext);

private void WriteLibraryMembers(TextWriter writer,
DirectedGraph dependencyGraph,
string libraryName,
Func<string, string> libraryNameToClassName,
int indent)
{
var node = dependencyGraph.Nodes[libraryName];
var requiredLibraries = node.ForwardEdges?
.Select(edge => edge.ToId)
.Except(new[] { dependencyGraph.EndNode.NodeId })
.Distinct();
if (requiredLibraries != null)
{

writer.WriteLine(indent, "#region Dependencies");
writer.WriteLine();

foreach (var dependentLibrary in requiredLibraries)
{
var typeName = libraryNameToClassName(dependentLibrary);
var memberName = typeName;
writer.WriteLine(indent, $"public {typeName} {memberName} {{ get; }}");
}

writer.WriteLine();
writer.WriteLine(indent, "#endregion");
writer.WriteLine();
}
}

private IList<DirectedGraphNode> DetermineBuildOrder(DirectedGraph minimalGraph)
{
var sorted = minimalGraph.TopologicalSort()
Expand All @@ -385,15 +280,7 @@ private IList<DirectedGraphNode> DetermineBuildOrder(DirectedGraph minimalGraph)
return sorted;
}

private string DefinitionCacheKeyForMethod(string methodName)
{
if (methodName[0] == '@')
return "__" + methodName.Substring(1);
else return "__" + methodName;
}
private string PrivateMethodNameFor(string methodName) => methodName + "_Value";

private void WriteMemoizedInstanceMethod(string libraryName, TextWriter writer, int indentLevel,
private void writeMethod(string libraryName, TextWriter writer, int indentLevel,
string cqlName,
LambdaExpression overload,
ILookup<string, string>? tags)
Expand All @@ -419,16 +306,6 @@ private void WriteMemoizedInstanceMethod(string libraryName, TextWriter writer,

if (isDef)
{
// Definitions, which are CQL expressions without parameter, can be memoized,
// so we generate a "generator" function (name ending in _Value) and a
// getter function, which just calls triggers the lazy to invoke this
// first _Value method.
var cachedValueName = DefinitionCacheKeyForMethod(methodName!);
var privateMethodName = PrivateMethodNameFor(methodName!);

var func = expressionConverter.ConvertTopLevelFunctionDefinition(indentLevel, overload, privateMethodName, "private");
writer.Write(func);
writer.WriteLine();
writer.WriteLine(indentLevel, $"[CqlDeclaration(\"{cqlName}\")]");
WriteTags(writer, indentLevel, tags);

Expand All @@ -447,14 +324,7 @@ private void WriteMemoizedInstanceMethod(string libraryName, TextWriter writer,
}
}

var lazyType = typeof(Lazy<>).MakeGenericType(visitedBody.Type);
var valueFunc =
Expression.Lambda(
Expression.MakeMemberAccess(
Expression.Parameter(lazyType, cachedValueName),
lazyType.GetMember("Value").Single()));

writer.Write(expressionConverter.ConvertTopLevelFunctionDefinition(indentLevel, valueFunc, methodName!, "public"));
writer.Write(expressionConverter.ConvertTopLevelFunctionDefinition(indentLevel, overload, methodName!, "public"));
}
else
{
Expand Down
17 changes: 10 additions & 7 deletions Cql/CodeGeneration.NET/ExpressionConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,10 @@ private string convertDefinitionCallExpression(int indent, string leadingIndentS
var sb = new StringBuilder();
sb.Append(leadingIndentString);

var target = dce.LibraryName == LibraryName ? "this" :
VariableNameGenerator.NormalizeIdentifier(dce.LibraryName);
var target = dce.LibraryName == LibraryName ? "this" : $"{VariableNameGenerator.NormalizeIdentifier(dce.LibraryName)}.Instance";
var csFunctionName = VariableNameGenerator.NormalizeIdentifier(dce.DefinitionName);

sb.Append(CultureInfo.InvariantCulture, $"{target}.{csFunctionName}()");
sb.Append(CultureInfo.InvariantCulture, $"{target}.{csFunctionName}(context)");

return sb.ToString();
}
Expand All @@ -80,12 +79,11 @@ private string convertFunctionCallExpression(int indent, string leadingIndentStr
var sb = new StringBuilder();
sb.Append(leadingIndentString);

var target = fce.LibraryName == LibraryName ? "this" :
VariableNameGenerator.NormalizeIdentifier(fce.LibraryName);
var target = fce.LibraryName == LibraryName ? "this" : $"{VariableNameGenerator.NormalizeIdentifier(fce.LibraryName)}.Instance";
var csFunctionName = VariableNameGenerator.NormalizeIdentifier(fce.FunctionName);

sb.Append(CultureInfo.InvariantCulture, $"{target}.{csFunctionName}");
sb.Append(convertArguments(indent, fce.Arguments.Skip(1))); // skip cqlContext
sb.Append(convertArguments(indent, fce.Arguments));

return sb.ToString();
}
Expand Down Expand Up @@ -417,7 +415,12 @@ private string convertLambdaExpression(int indent, string leadingIndentString, L
var lambdaSb = new StringBuilder();
lambdaSb.Append(leadingIndentString);

var lambdaParameters = $"({string.Join(", ", lambda.Parameters.Select(p => $"{PrettyTypeName(p.Type)} {escapeKeywords(p.Name!)}"))})";
var parameters = lambda.Parameters.Select(p => $"{PrettyTypeName(p.Type)} {escapeKeywords(p.Name!)}").ToList();
// inserts the context parameter in the start of the lambda expression
if (indent == 1)
parameters.Insert(0, "CqlContext context");

var lambdaParameters = $"({string.Join(", ", parameters)})";
lambdaSb.Append(lambdaParameters);

if (lambda.Body is BlockExpression)
Expand Down