Esempio n. 1
0
        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)));
        }
Esempio n. 2
0
        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)));
            }
Esempio n. 3
0
        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)));
        }
Esempio n. 4
0
        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))));
        }
Esempio n. 5
0
        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);
        }
Esempio n. 6
0
        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);
        }
Esempio n. 7
0
        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);
        }
Esempio n. 8
0
        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);
        }
Esempio n. 9
0
        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);
            }
        }
Esempio n. 10
0
        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());
        }
Esempio n. 11
0
        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)));
        }
Esempio n. 12
0
        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>()));
            }
        }
Esempio n. 13
0
        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))
            });
        }