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 2 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
148 changes: 8 additions & 140 deletions Cql/CodeGeneration.NET/CSharpSourceCodeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,44 +198,21 @@ 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, $"partial static class {className}");
else
writer.WriteLine(indentLevel, $"public class {className}");
writer.WriteLine(indentLevel, $"public static class {className}");
DamienMosier marked this conversation as resolved.
Show resolved Hide resolved
writer.WriteLine(indentLevel, "{");
writer.WriteLine();
indentLevel += 1;
// Class
{
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))
{
Expand All @@ -247,62 +224,7 @@ private void writeMemoizedInstanceMethods(DefinitionDictionary<LambdaExpression>
}
}
}

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 +268,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,14 +277,6 @@ 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,
DamienMosier marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

@baseTwo baseTwo Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to WriteMemoizedInstanceMethod to WriteMethod. See review comment above first

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested the singleton approach and it produced similar results so either works

string cqlName,
LambdaExpression overload,
Expand All @@ -419,16 +303,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,20 +321,14 @@ 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"));
var func = expressionConverter.ConvertTopLevelFunctionDefinition(indentLevel, overload, methodName!, "public static");
writer.Write(func);
}
else
{
writer.WriteLine(indentLevel, $"[CqlDeclaration(\"{cqlName}\")]");
WriteTags(writer, indentLevel, tags);
writer.Write(expressionConverter.ConvertTopLevelFunctionDefinition(indentLevel, overload, methodName!, "public"));
writer.Write(expressionConverter.ConvertTopLevelFunctionDefinition(indentLevel, overload, methodName!, "public static"));
// writer.WriteLine();
}
}
Expand Down
19 changes: 11 additions & 8 deletions Cql/CodeGeneration.NET/ExpressionConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,11 @@ 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 = VariableNameGenerator.NormalizeIdentifier(dce.LibraryName);
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 = VariableNameGenerator.NormalizeIdentifier(fce.LibraryName);
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