public Task <SyntaxList <MemberDeclarationSyntax> > GenerateAsync(TransformationContext context, IProgress <Diagnostic> progress, CancellationToken cancellationToken) { var results = SyntaxFactory.List <MemberDeclarationSyntax>(); // Our generator is applied to any class that our attribute is applied to. var applyToClass = (ClassDeclarationSyntax)context.ProcessingNode; // Apply a suffix to the name of a copy of the class. var partialClass = SyntaxFactory.ClassDeclaration($"{applyToClass.Identifier}") .WithModifiers( SyntaxFactory.TokenList( SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.PartialKeyword))); var returnType = CodeGenUtil.TypeFromClass(applyToClass); var fields = applyToClass.Members.Where(m => m is FieldDeclarationSyntax) .Select(m => m as FieldDeclarationSyntax) .Where(m => m.Modifiers.Any(SyntaxKind.PublicKeyword)) .Where(m => m.Modifiers.Any(SyntaxKind.ReadOnlyKeyword)) .Where(m => !m.Modifiers.Any(SyntaxKind.StaticKeyword)) .ToList(); partialClass = CodeGenUtil.AddWith(partialClass, returnType, fields); return(Task.FromResult <SyntaxList <MemberDeclarationSyntax> >(results.Add(partialClass))); }
public Task <SyntaxList <MemberDeclarationSyntax> > GenerateAsync( TransformationContext context, IProgress <Diagnostic> progress, CancellationToken cancellationToken) { if (context.ProcessingNode is TypeDeclarationSyntax applyTo && (applyTo is ClassDeclarationSyntax || (applyTo is StructDeclarationSyntax))) { var record = CodeGenUtil.MakeCaseType( context, applyTo.Identifier, applyTo.Members, applyTo.TypeParameterList, applyTo.Modifiers, applyTo.ConstraintClauses, applyTo.Identifier, null, CodeGenUtil.GetState(context, progress, AllowedType.ClassOrStruct, "Record code-gen").Fields, BaseSpec.None, caseIsClass: applyTo is ClassDeclarationSyntax, caseIsPartial: true, -1); return(Task.FromResult(List <MemberDeclarationSyntax>().Add(record))); }
public Task <SyntaxList <MemberDeclarationSyntax> > GenerateAsync(TransformationContext context, IProgress <Diagnostic> progress, CancellationToken cancellationToken) { var(partialClass, returnType, fields) = CodeGenUtil.GetState(context); partialClass = CodeGenUtil.AddWith(context, partialClass, returnType, fields); return(Task.FromResult(SyntaxFactory.List <MemberDeclarationSyntax>().Add(partialClass))); }
static MemberDeclarationSyntax MakeConstructor(ClassDeclarationSyntax @class, TypeSyntax returnType, MethodDeclarationSyntax method) { var assignments = method.ParameterList .Parameters .Select(p => ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(CodeGenUtil.MakeFirstCharUpper(p.Identifier.Text)), IdentifierName(p.Identifier.Text)))); return(ConstructorDeclaration(Identifier(method.Identifier.Text)) .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) .WithParameterList(method.ParameterList) .WithBody(Block(List(assignments)))); }
static MemberDeclarationSyntax MakeField(TypeSyntax returnType, ParameterSyntax p) { var fieldName = CodeGenUtil.MakeFirstCharUpper(p.Identifier.Text); var field = FieldDeclaration( VariableDeclaration(p.Type) .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(fieldName))))) .WithModifiers( TokenList( new[] { Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.ReadOnlyKeyword) })) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));; return(field); }
MemberDeclarationSyntax MakeCaseCtorFunction(InterfaceDeclarationSyntax applyTo, TypeSyntax returnType, MethodDeclarationSyntax method) { var typeParamList = applyTo.TypeParameterList; if (method.TypeParameterList != null) { typeParamList = typeParamList.AddParameters(method.TypeParameterList.Parameters.ToArray()); } var thisType = ParseTypeName($"{method.Identifier.Text}{typeParamList}"); var args = CodeGenUtil.Interleave( method.ParameterList .Parameters .Select(p => (SyntaxNodeOrToken)Argument(IdentifierName(p.Identifier.Text))) .ToArray(), Token(SyntaxKind.CommaToken)); var @case = MethodDeclaration(returnType, method.Identifier) .WithModifiers( TokenList( new[] { Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword) })) .WithParameterList(method.ParameterList) .WithExpressionBody( ArrowExpressionClause( ObjectCreationExpression(thisType) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>(args))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); if (typeParamList != null) { @case = @case.WithTypeParameterList(typeParamList); } return(@case); }
public static (ClassDeclarationSyntax PartialClass, TypeSyntax ReturnType, List <FieldDeclarationSyntax> Fields) GetState(TransformationContext context) { // Our generator is applied to any class that our attribute is applied to. var applyToClass = (ClassDeclarationSyntax)context.ProcessingNode; var classModifiers = SyntaxFactory.TokenList( Enumerable.Concat( applyToClass.Modifiers .Where(t => !t.IsKind(SyntaxKind.PartialKeyword)).AsEnumerable(), new[] { SyntaxFactory.Token(SyntaxKind.PartialKeyword) })); // Apply a suffix to the name of a copy of the class. var partialClass = SyntaxFactory.ClassDeclaration($"{applyToClass.Identifier}") .WithModifiers(classModifiers); if (applyToClass.TypeParameterList != null) { partialClass = partialClass.WithTypeParameterList(applyToClass.TypeParameterList); } if (applyToClass.ConstraintClauses != null) { partialClass = partialClass.WithConstraintClauses(applyToClass.ConstraintClauses); } var returnType = CodeGenUtil.TypeFromClass(applyToClass); var fields = applyToClass.Members .Where(m => m is FieldDeclarationSyntax) .Select(m => m as FieldDeclarationSyntax) .Where(f => f.Declaration.Variables.Count > 0) .Where(f => FirstCharIsUpper(f.Declaration.Variables[0].Identifier.ToString())) .Where(f => f.Modifiers.Any(SyntaxKind.PublicKeyword)) .Where(f => f.Modifiers.Any(SyntaxKind.ReadOnlyKeyword)) .Where(f => !f.Modifiers.Any(SyntaxKind.StaticKeyword)) .ToList(); return(partialClass, returnType, fields); }
public static (ClassDeclarationSyntax PartialClass, TypeSyntax ReturnType, List <(SyntaxToken Identifier, TypeSyntax Type, SyntaxTokenList Modifiers)> Fields) GetState(TransformationContext context) { // Our generator is applied to any class that our attribute is applied to. var applyToClass = (ClassDeclarationSyntax)context.ProcessingNode; var classModifiers = SyntaxFactory.TokenList( Enumerable.Concat( applyToClass.Modifiers .Where(t => !t.IsKind(SyntaxKind.PartialKeyword)).AsEnumerable(), new[] { SyntaxFactory.Token(SyntaxKind.PartialKeyword) })); // Apply a suffix to the name of a copy of the class. var partialClass = SyntaxFactory.ClassDeclaration($"{applyToClass.Identifier}") .WithModifiers(classModifiers); if (applyToClass.TypeParameterList != null) { partialClass = partialClass.WithTypeParameterList(applyToClass.TypeParameterList); } if (applyToClass.ConstraintClauses != null) { partialClass = partialClass.WithConstraintClauses(applyToClass.ConstraintClauses); } var returnType = CodeGenUtil.TypeFromClass(applyToClass); var indexedMembers = applyToClass.Members.Select((m, i) => (m, i)); var fields = indexedMembers.Where(m => m.m is FieldDeclarationSyntax) .Select(m => (f: m.m as FieldDeclarationSyntax, m.i)) .Where(m => m.f.Declaration.Variables.Count > 0) .Where(m => FirstCharIsUpper(m.f.Declaration.Variables[0].Identifier.ToString())) .Where(m => m.f.Modifiers.Any(SyntaxKind.PublicKeyword)) .Where(m => m.f.Modifiers.Any(SyntaxKind.ReadOnlyKeyword)) .Where(m => !m.f.Modifiers.Any(SyntaxKind.StaticKeyword)) .Select(m => ( m.f.Declaration.Variables[0].Identifier, m.f.Declaration.Type, m.f.Modifiers, m.i )); var properties = indexedMembers.Where(m => m.m is PropertyDeclarationSyntax) .Select(m => (p: m.m as PropertyDeclarationSyntax, m.i)) .Where(m => FirstCharIsUpper(m.p.Identifier.ToString())) .Where(m => m.p.Modifiers.Any(SyntaxKind.PublicKeyword)) .Where(m => !m.p.Modifiers.Any(SyntaxKind.StaticKeyword)) .Where(m => m.p.AccessorList.Accessors.Count == 1) .Where(m => m.p.AccessorList.Accessors[0].Kind() == SyntaxKind.GetAccessorDeclaration) .Where(m => m.p.AccessorList.Accessors[0].ExpressionBody == null) .Where(m => m.p.AccessorList.Accessors[0].Body == null) .Where(m => m.p.Initializer == null) .Select(m => ( m.p.Identifier, m.p.Type, m.p.Modifiers, m.i )); var members = fields.Concat(properties) .OrderBy(m => m.i) // Preserve the order between properties and fields. .Select(m => (m.Identifier, m.Type, m.Modifiers)) .ToList(); return(partialClass, returnType, members); }
static MemberDeclarationSyntax MakeCaseCtorFunction( SyntaxToken applyToIdentifier, TypeParameterListSyntax applyToTypeParams, SyntaxList <TypeParameterConstraintClauseSyntax> applyToConstraints, TypeSyntax returnType, MethodDeclarationSyntax method, MethodDeclarationSyntax pure) { bool isPure = method.AttributeLists .SelectMany(a => a.Attributes) .Any(a => a.Name.ToString() == "Pure"); if (isPure) { var typeParamList = applyToTypeParams; if (method.TypeParameterList != null) { typeParamList = typeParamList.AddParameters(method.TypeParameterList.Parameters.ToArray()); } var thisType = ParseTypeName($"{method.Identifier.Text}{typeParamList}"); var paramIdents = method.ParameterList .Parameters .Select(p => (SyntaxNodeOrToken)Argument(IdentifierName(p.Identifier.Text))) .ToArray(); var args = CodeGenUtil.Interleave( paramIdents, Token(SyntaxKind.CommaToken)); var @case = MethodDeclaration(returnType, method.Identifier) .WithModifiers( TokenList( new[] { Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword) })) .WithParameterList( isPure ? method.ParameterList : method.ParameterList.WithParameters(method.ParameterList.Parameters.RemoveAt(method.ParameterList.Parameters.Count - 1))) .WithConstraintClauses(applyToConstraints) .WithExpressionBody( ArrowExpressionClause( ObjectCreationExpression(thisType) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>(args))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); if (typeParamList != null) { @case = @case.WithTypeParameterList(typeParamList); } return(@case); } else { var nextType = ((GenericNameSyntax)((QualifiedNameSyntax)method.ParameterList.Parameters.Last().Type).Right) .TypeArgumentList .Arguments .First(); var thisType = ParseTypeName($"{method.Identifier.Text}<{nextType}>"); returnType = ParseTypeName($"{applyToIdentifier}<{nextType}>"); var paramIdents = method.ParameterList .Parameters .Select(p => (SyntaxNodeOrToken)Argument(IdentifierName(p.Identifier.Text))) .ToArray(); paramIdents[paramIdents.Length - 1] = Argument(IdentifierName(pure.Identifier.Text)); var args = CodeGenUtil.Interleave( paramIdents, Token(SyntaxKind.CommaToken)); var @case = MethodDeclaration(returnType, method.Identifier) .WithModifiers( TokenList( new[] { Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword) })) .WithParameterList( method.ParameterList.WithParameters(method.ParameterList.Parameters.RemoveAt(method.ParameterList.Parameters.Count - 1))) .WithConstraintClauses(applyToConstraints) .WithExpressionBody( ArrowExpressionClause( ObjectCreationExpression(thisType) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>(args))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)); return(@case); } }
static MethodDeclarationSyntax MakeMapFunction( SyntaxToken applyToIdentifier, SyntaxList <TypeParameterConstraintClauseSyntax> applyToConstraints, MethodDeclarationSyntax[] applyToMembers, TypeParameterListSyntax applyToTypeParams, MethodDeclarationSyntax pure) { var genA = applyToTypeParams.Parameters.First().ToString(); var genB = CodeGenUtil.NextGenName(genA); var genC = CodeGenUtil.NextGenName(genB); var typeA = MakeTypeName(applyToIdentifier.Text, genA); var typeB = MakeTypeName(applyToIdentifier.Text, genB); var typeC = MakeTypeName(applyToIdentifier.Text, genC); var mapFuncType = ParseTypeName($"System.Func<{genA}, {genB}>"); var pureTypeA = MakeTypeName(pure.Identifier.Text, genA); var pureTypeB = MakeTypeName(pure.Identifier.Text, genB); var mapFunc = InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("v"), IdentifierName("Next"))) .WithArgumentList(ArgumentList(SingletonSeparatedList <ArgumentSyntax>(Argument(IdentifierName("n"))))), IdentifierName("Map"))) .WithArgumentList( ArgumentList( SingletonSeparatedList <ArgumentSyntax>( Argument( IdentifierName("f"))))); var pureFunc = new SyntaxNodeOrToken[] { SwitchExpressionArm( DeclarationPattern( pureTypeA, SingleVariableDesignation(Identifier("v"))), ObjectCreationExpression(pureTypeB) .WithArgumentList( ArgumentList( SingletonSeparatedList <ArgumentSyntax>( Argument( InvocationExpression(IdentifierName("f")) .WithArgumentList( ArgumentList( SingletonSeparatedList <ArgumentSyntax>( Argument( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("v"), IdentifierName(CodeGenUtil.MakeFirstCharUpper(pure.ParameterList.Parameters.First().Identifier.Text)))))))))))), Token(SyntaxKind.CommaToken) }; var termimalFuncs = applyToMembers .Where(m => m != pure && m.AttributeLists != null && m.AttributeLists.SelectMany(a => a.Attributes).Any(a => a.Name.ToString() == "Pure")) .SelectMany(m => new SyntaxNodeOrToken[] { SwitchExpressionArm( DeclarationPattern( ParseTypeName($"{m.Identifier.Text}<{genA}>"), SingleVariableDesignation(Identifier("v"))), ObjectCreationExpression(MakeTypeName(m.Identifier.Text, genB)) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( m.ParameterList .Parameters .Select(p => Argument( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("v"), IdentifierName(CodeGenUtil.MakeFirstCharUpper(p.Identifier))))))))), Token(SyntaxKind.CommaToken) }); var freeFuncs = applyToMembers .Where(m => m.AttributeLists == null || !m.AttributeLists.SelectMany(a => a.Attributes).Any(a => a.Name.ToString() == "Pure")) .SelectMany(m => new SyntaxNodeOrToken[] { SwitchExpressionArm( DeclarationPattern( ParseTypeName($"{m.Identifier.Text}<{genA}>"), SingleVariableDesignation(Identifier("v"))), ObjectCreationExpression(MakeTypeName(m.Identifier.Text, genB)) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( Enumerable.Concat( m.ParameterList .Parameters .Take(m.ParameterList.Parameters.Count - 1) .SelectMany(p => new SyntaxNodeOrToken[] { Argument( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("v"), IdentifierName(CodeGenUtil.MakeFirstCharUpper(p.Identifier.Text)))), Token(SyntaxKind.CommaToken) }), new SyntaxNodeOrToken [1] { Argument(SimpleLambdaExpression(Parameter(Identifier("n")), mapFunc)) }))))), Token(SyntaxKind.CommaToken) }); var tokens = new List <SyntaxNodeOrToken>(); tokens.AddRange(pureFunc); tokens.AddRange(termimalFuncs); tokens.AddRange(freeFuncs); tokens.Add( SwitchExpressionArm( DiscardPattern(), ThrowExpression( ObjectCreationExpression( QualifiedName( IdentifierName("System"), IdentifierName("NotSupportedException"))) .WithArgumentList(ArgumentList())))); return(MethodDeclaration(typeB, Identifier("Map")) .WithModifiers( TokenList(new[] { Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword) })) .WithTypeParameterList( TypeParameterList( SeparatedList <TypeParameterSyntax>( new SyntaxNodeOrToken[] { TypeParameter( Identifier(genA)), Token(SyntaxKind.CommaToken), TypeParameter( Identifier(genB)) }))) .WithParameterList( ParameterList( SeparatedList <ParameterSyntax>( new SyntaxNodeOrToken[] { Parameter( Identifier("ma")) .WithModifiers( TokenList( Token(SyntaxKind.ThisKeyword))) .WithType(typeA), Token(SyntaxKind.CommaToken), Parameter( Identifier("f")) .WithType(mapFuncType) }))) .WithExpressionBody( ArrowExpressionClause( SwitchExpression( IdentifierName("ma")) .WithArms(SeparatedList <SwitchExpressionArmSyntax>(tokens)))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)) .NormalizeWhitespace()); }
static MethodDeclarationSyntax MakeMapExtension( SyntaxToken applyToIdentifier, SyntaxList <TypeParameterConstraintClauseSyntax> applyToConstraints, MethodDeclarationSyntax[] applyToMembers, TypeParameterListSyntax applyToTypeParams) { var genA = applyToTypeParams.Parameters.First().ToString(); var genB = CodeGenUtil.NextGenName(genA); var typeA = MakeTypeName(applyToIdentifier.Text, genA); var typeB = MakeTypeName(applyToIdentifier.Text, genB); var mapFuncType = ParseTypeName($"System.Func<{genA}, {genB}>"); return(MethodDeclaration( typeB, Identifier("Map")) .WithModifiers( TokenList( new[] { Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword) })) .WithTypeParameterList( TypeParameterList( SeparatedList <TypeParameterSyntax>( new SyntaxNodeOrToken[] { TypeParameter( Identifier(genA)), Token(SyntaxKind.CommaToken), TypeParameter( Identifier(genB)) }))) .WithParameterList( ParameterList( SeparatedList <ParameterSyntax>( new SyntaxNodeOrToken[] { Parameter( Identifier("ma")) .WithModifiers( TokenList( Token(SyntaxKind.ThisKeyword))) .WithType(typeA), Token(SyntaxKind.CommaToken), Parameter(Identifier("f")).WithType(mapFuncType) }))) .WithExpressionBody( ArrowExpressionClause( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, typeA, IdentifierName("Map"))) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( new SyntaxNodeOrToken[] { Argument( IdentifierName("ma")), Token(SyntaxKind.CommaToken), Argument( IdentifierName("f")) }))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken))); }
public Task <SyntaxList <MemberDeclarationSyntax> > GenerateAsync( TransformationContext context, IProgress <Diagnostic> progress, CancellationToken cancellationToken) { try { if (context.ProcessingNode is InterfaceDeclarationSyntax applyTo) { if (applyTo.TypeParameterList.Parameters.Count > 1) { CodeGenUtil.ReportError($"Free monads must have only one generic argument", "Free monad Code-Gen", context.ProcessingNode, progress); return(Task.FromResult(List <MemberDeclarationSyntax>())); } if (applyTo.TypeParameterList.Parameters.Count == 0) { CodeGenUtil.ReportError($"Free monads must a generic argument", "Free monad Code-Gen", context.ProcessingNode, progress); return(Task.FromResult(List <MemberDeclarationSyntax>())); } var genA = applyTo.TypeParameterList.Parameters.First().ToString(); var genB = CodeGenUtil.NextGenName(genA); var genC = CodeGenUtil.NextGenName(genB); var thisType = ParseTypeName($"{applyTo.Identifier.Text}{applyTo.TypeParameterList}"); var typeA = MakeTypeName(applyTo.Identifier.Text, genA); var typeB = MakeTypeName(applyTo.Identifier.Text, genB); var mapFunc = ParseTypeName($"System.Func<{genA}, {genB}>"); var mapIsStatic = applyTo.Members .OfType <MethodDeclarationSyntax>() .Where(m => m.Identifier.Text == "Map") .Where(m => m.ParameterList.Parameters.Count == 2) .Where(m => m.Modifiers.Any(mo => mo.IsKind(SyntaxKind.StaticKeyword))) .Where(m => m.Modifiers.Any(mo => mo.IsKind(SyntaxKind.PublicKeyword))) // .Where(m => m.ReturnType.IsEquivalentTo(typeB)) // .Where(m => m.ParameterList.Parameters[0].Type.IsEquivalentTo(typeA)) // .Where(m => m.ParameterList.Parameters[1].Type.IsEquivalentTo(mapFunc)) .Any(); var caseMethods = applyTo.Members .OfType <MethodDeclarationSyntax>() .Where(m => !m.Modifiers.Any(mo => mo.IsKind(SyntaxKind.StaticKeyword))) .Select(m => MakeFree(m, thisType)) .ToArray(); var firstPure = caseMethods.Where(HasPureAttr) .Where(m => m.ParameterList.Parameters.Count == 1) .Where(m => m.ReturnType.IsEquivalentTo(typeA)) .Where(m => m.ParameterList.Parameters.First().Type.ToString() == genA) .FirstOrDefault(); if (firstPure == null) { CodeGenUtil.ReportError($"Type can't be made into a free monad because no method in the interface has [Pure] attribute that takes a single argument of type '{genA}' and returns a '{genA}'", "Free monad Code-Gen", context.ProcessingNode, progress); return(Task.FromResult(List <MemberDeclarationSyntax>())); } var caseRes = caseMethods .Zip(Enumerable.Range(1, int.MaxValue), (m, i) => (m, i)) .Select(m => CodeGenUtil.MakeCaseType( context, progress, applyTo.Identifier, applyTo.Members, applyTo.TypeParameterList, applyTo.Modifiers, applyTo.ConstraintClauses, m.m.Identifier, m.m.TypeParameterList, m.m.ParameterList .Parameters .Select(p => (p.Identifier, p.Type, p.Modifiers, p.AttributeLists)) .ToList(), BaseSpec.Interface, caseIsClass: true, caseIsPartial: false, includeWithAndLenses: false, m.i)) .ToList(); var ok = caseRes.All(x => x.Success); var cases = caseRes.Select(c => c.Type); var staticCtorClass = MakeStaticClass( applyTo.Identifier, caseMethods, applyTo.TypeParameterList, applyTo.ConstraintClauses, firstPure, mapIsStatic); if (ok) { return(Task.FromResult(List <MemberDeclarationSyntax>().AddRange(cases).Add(staticCtorClass))); } else { return(Task.FromResult(List <MemberDeclarationSyntax>())); } } else { CodeGenUtil.ReportError($"Type can't be made into a free monad. It must be an interface", "Free monad Code-Gen", context.ProcessingNode, progress); return(Task.FromResult(List <MemberDeclarationSyntax>())); } } catch (Exception e) { CodeGenUtil.ReportError(e.Message, "Free monad Code-Gen", context.ProcessingNode, progress); foreach (var line in e.StackTrace.Split('\n')) { CodeGenUtil.ReportError(line, "Free monad Code-Gen", context.ProcessingNode, progress); } return(Task.FromResult(List <MemberDeclarationSyntax>())); } }
static MemberDeclarationSyntax[] AddMonadDefaults( SyntaxToken applyToIdentifier, MethodDeclarationSyntax[] applyToMembers, TypeParameterListSyntax applyToTypeParams, SyntaxList <TypeParameterConstraintClauseSyntax> applyToConstraints) { var genA = applyToTypeParams.Parameters.First().ToString(); var genB = CodeGenUtil.NextGenName(genA); var genC = CodeGenUtil.NextGenName(genB); var typeA = MakeTypeName(applyToIdentifier.Text, genA); var liftedTypeA = MakeTypeName(applyToIdentifier.Text, typeA.ToString()); var typeB = MakeTypeName(applyToIdentifier.Text, genB); var typeC = MakeTypeName(applyToIdentifier.Text, genC); var bindFuncType = ParseTypeName($"System.Func<{genA}, {typeB}>"); var mapFuncType = ParseTypeName($"System.Func<{genA}, {genB}>"); var projFuncType = ParseTypeName($"System.Func<{genA}, {genB}, {genC}>"); var comma = Token(SyntaxKind.CommaToken); var typeParamA = TypeParameter(Identifier(genA)); var typeParamB = TypeParameter(Identifier(genB)); var typeParamC = TypeParameter(Identifier(genC)); var typeParamsA = TypeParameterList(SeparatedList <TypeParameterSyntax>(new SyntaxNodeOrToken[] { typeParamA })); var typeParamsAB = TypeParameterList( SeparatedList <TypeParameterSyntax>( new SyntaxNodeOrToken[] { typeParamA, comma, typeParamB })); var typeParamsABC = TypeParameterList( SeparatedList <TypeParameterSyntax>( new SyntaxNodeOrToken[] { typeParamA, comma, typeParamB, comma, typeParamC })); var pubStatMods = TokenList(new[] { Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword) }); var thisMA = Parameter(Identifier("ma")) .WithModifiers(TokenList(Token(SyntaxKind.ThisKeyword))) .WithType(typeA); var thisMMA = Parameter(Identifier("mma")) .WithModifiers(TokenList(Token(SyntaxKind.ThisKeyword))) .WithType(liftedTypeA); return(new MemberDeclarationSyntax[] { MethodDeclaration( typeB, Identifier("Select")) .WithModifiers(pubStatMods) .WithTypeParameterList(typeParamsAB) .WithParameterList( ParameterList( SeparatedList <ParameterSyntax>( new SyntaxNodeOrToken[] { thisMA, comma, Parameter( Identifier("f")) .WithType(mapFuncType) }))) .WithExpressionBody( ArrowExpressionClause( InvocationExpression( IdentifierName("Map")) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( new SyntaxNodeOrToken[] { Argument( IdentifierName("ma")), comma, Argument( IdentifierName("f")) }))))) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), MethodDeclaration(typeB, Identifier("SelectMany")) .WithModifiers(pubStatMods) .WithTypeParameterList(typeParamsAB) .WithParameterList( ParameterList( SeparatedList <ParameterSyntax>( new SyntaxNodeOrToken[] { thisMA, comma, Parameter(Identifier("f")).WithType(bindFuncType) }))) .WithExpressionBody( ArrowExpressionClause( InvocationExpression( IdentifierName("Bind")) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( new SyntaxNodeOrToken[] { Argument( IdentifierName("ma")), comma, Argument( IdentifierName("f")) }))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)), MethodDeclaration(typeC, Identifier("SelectMany")) .WithModifiers(pubStatMods) .WithTypeParameterList(typeParamsABC) .WithParameterList( ParameterList( SeparatedList <ParameterSyntax>( new SyntaxNodeOrToken[] { thisMA, comma, Parameter(Identifier("bind")).WithType(bindFuncType), comma, Parameter(Identifier("project")).WithType(projFuncType) }))) .WithExpressionBody( ArrowExpressionClause( InvocationExpression( IdentifierName("Bind")) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( new SyntaxNodeOrToken[] { Argument( IdentifierName("ma")), comma, Argument( SimpleLambdaExpression( Parameter( Identifier("a")), InvocationExpression( IdentifierName("Map")) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( new SyntaxNodeOrToken[] { Argument( InvocationExpression( IdentifierName("bind")) .WithArgumentList( ArgumentList( SingletonSeparatedList <ArgumentSyntax>( Argument( IdentifierName("a")))))), comma, Argument( SimpleLambdaExpression( Parameter( Identifier("b")), InvocationExpression( IdentifierName("project")) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( new SyntaxNodeOrToken[] { Argument( IdentifierName("a")), comma, Argument( IdentifierName("b")) }))))) }))))) }))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)), MethodDeclaration(typeA, Identifier("Flatten")) .WithModifiers(pubStatMods) .WithTypeParameterList(typeParamsA) .WithParameterList(ParameterList(SingletonSeparatedList <ParameterSyntax>(thisMMA))) .WithExpressionBody( ArrowExpressionClause( InvocationExpression( IdentifierName("Bind")) .WithArgumentList( ArgumentList( SeparatedList <ArgumentSyntax>( new SyntaxNodeOrToken[] { Argument( IdentifierName("mma")), comma, Argument( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("LanguageExt"), IdentifierName("Prelude")), IdentifierName("identity"))) }))))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)) }); }