Exemple #1
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());
        }
Exemple #2
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>()));
            }
        }
Exemple #3
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)));
        }
Exemple #4
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))
            });
        }