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()); }
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 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))); }
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)) }); }