Input

Ponet

MIT License Build Status NuGet Version Ponet on fuget.org

Ponet is a C# code generation library wrapping the Roslyn API.

For more information see ponet.aenea.dev.

Disclaimers

Ponet is an immature library. Therefore, there are many inconveniences as follows.

  • Unable to construct the intended SyntaxTree.
  • API is unstable.

If you are interested in Ponet and use it, I hope you can give us some feedback to help us develop it in the future.

Example

Hello World

Here's a Ponet code to generate Hello World class.

string message = "Hello World!";
MethodDeclarationSyntax mainMethod = MethodBuilder.Of("Main")
    .Modifier(Modifier.Static)
    .Returns(TypeName.Void)
    .CodeBlock(CodeBlockBuilder.Of()
        .Add($"Console.WriteLine({message:S});")
        .Build())
    .ToSyntaxNode();

CompilationUnitSyntax compilationUnitSyntax = CsharpFileBuilder.Of("HelloWorld")
    .Using("System")
    .Member(
        TypeBuilder.Of("Hello").Method(mainMethod)
            .ToClassSyntaxNode()
    ).ToSyntaxNode();

// Write to File.
compilationUnitSyntax.Save("./HelloWorld.cs");

Generated HelloWorld.cs contains:

using System;

namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Immutable Class

An example of an ImmutableClass for typical code generation is:

/*
 * Prepare properties which are public and Getter-only.
 */
PropertyDeclarationSyntax[] properties = new[]
{
    PropertyBuilder.Of(TypeName.String, "FirstName"),
    PropertyBuilder.Of(TypeName.String, "LastName"),
    PropertyBuilder.Of(TypeName.Int, "Age")
}.Select(builder => builder.Modifier(Modifier.Public).Getter().ToSyntaxNode()).ToArray();

/*
 * Prepare constructor to initialize properties.
 */
MethodBuilder constructorBuilder = MethodBuilder.Of("Person").Modifier(Modifier.Public);
CodeBlockBuilder constructorBody = CodeBlockBuilder.Of();

foreach (PropertyDeclarationSyntax property in properties)
{
    // add constructor parameter from PropertyDeclarationSyntax
    // Format Nlc extracts an lowercamelized identifier (lc means 'lower camel')
    constructorBuilder.Parameter(property.Type, $"{property:Nlc}");

    // add expression to initialize property
    // Format N extracts an identifier
    constructorBody.Add($"{property:N} = {property:Nlc};");
}

ConstructorDeclarationSyntax constructor =
    constructorBuilder.CodeBlock(constructorBody.Build()).ToConstructorNode();

/*
 * Finish creating the class
 */
ClassDeclarationSyntax personClass = TypeBuilder.Of("Person").Modifier(Modifier.Public)
    .AddMember(constructor)
    .Properties(properties)
    .ToClassSyntaxNode();

personClass represents

public class Person
{
    public Person(string firstName, string lastName, int age)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
    }

    public string FirstName { get; }

    public string LastName { get; }

    public int Age { get; }
}

Usage

Types(Class, Interface, Enum, Struct)

Ponet provides TypeBuilder for building Class, Interface, Enum and Struct. Complete the construction of the syntax tree by calling methods for each type.

Class

Class is built using TypeBuilder#ToClassSyntaxNode. See Example above for the actual code.

Interface

Use TypeBuilder#ToInterfaceSyntaxNode to build the Interface.

MethodDeclarationSyntax methodDeclarationSyntax = MethodBuilder.Of("Execute")
    .Parameter(TypeName.String, "text")
    .Returns(TypeName.Void)
    .ToSyntaxNode();
InterfaceDeclarationSyntax interfaceDeclarationSyntax = TypeBuilder.Of("ISomeService")
    .Modifier(Modifier.Public)
    .AddMember(methodDeclarationSyntax)
    .ToInterfaceSyntaxNode();

represents.

public interface ISomeService
{
    void Execute(string text);
}

Enum

Use TypeBuilder#ToEnumSyntaxNode to build the Enum.

EnumDeclarationSyntax enumDeclarationSyntax = TypeBuilder.Of("Lang")
    .Modifier(Modifier.Public)
    .EnumSymbol("Csharp")
    .EnumSymbol("Fsharp")
    .EnumSymbol("Java")
    .EnumSymbol("Kotlin")
    .ToEnumSyntaxNode();

represents.

public enum Lang
{
    Csharp,
    Fsharp,
    Java,
    Kotlin
}

Struct

// Constructor
ConstructorDeclarationSyntax constructor = MethodBuilder.Of("SomeStruct")
    .Parameter(TypeName.Int, "x")
    .Parameter(TypeName.Int, "y")
    .CodeBlock(
        CodeBlockBuilder.Of()
            .Add("X = x;")
            .Add("Y = y;")
            .Build())
    .ToConstructorNode();

// Struct
StructDeclarationSyntax structDeclarationSyntax = TypeBuilder.Of("SomeStruct")
    .AddMember(constructor)
    .AddMember(PropertyBuilder.Of(TypeName.Int, "X").Modifier(Modifier.Public).Getter().ToSyntaxNode())
    .AddMember(PropertyBuilder.Of(TypeName.Int, "Y").Modifier(Modifier.Public).Getter().ToSyntaxNode())
    .ToStructSyntaxNode()

structDeclarationSyntax represents

struct SomeStruct
{
    SomeStruct(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; }

    public int Y { get; }
}

Convert to String, Save to File

Provides extension methods for CompilationUnitSyntax to support syntax tree stringization and saving to a file.

CompilationUnitSyntax compilationUnitSyntax = ...;

// To String
string code = compilationUnitSyntax.toCode();

// Write to File
compilationUnitSyntax.Save("./generatedCode");

Custom StringFormat Specifier

When building a SyntaxNode, you often want to refer to the types and identifiers in the SyntaxNode already built.

To do this, Ponet provides its own format specifiers that can be used with interpolation.

  • the N, NUC, Nlc format specifier
    • result: identifier(method name, parameter name)
    • support: BaseTypeDeclarationSyntax, ParameterSyntax, VariableDeclaratorSyntax, etc
  • the T, TUC, Tlc
    • result: Type Name
    • support: FieldDeclarationSyntax, PropertyDeclarationSyntax, ParameterSyntax, etc
  • the S
    • result: escaped string
    • support: string only

UC stands for UpperCamel and lc for _lowerCamel. this changes the naming convention for the result of an same single-character format specifier.

The input and result combinations are shown in the following table.

Format Specifier from to
UC UpperCamel UpperCamel
UC lowerCamel LowerCamel
UC UPPER_UNDERSCORE UpperUnderscore
UC _lowerCamel LowerCamel
lc UpperCamel upperCamel
lc lowerCamel lowerCamel
lc UPPER_UNDERSCORE upperUnderscore
lc _lowerCamel lowerCamel