2015-01-08 25 views
8

Staramy się dowiedzieć, jak wygenerować kod z Roslyn. Nie mówię o czymś takim, jak CSharpSyntaxTree.ParseText, które zajmie kilka ciągów i przekształci je w AST. Zamiast tego chciałbym zbudować mój model jakoś tak (pseudo kod):Generowanie kodu semantycznego z roslyn

  1. Tworzenie file jako jednostki kompilacji
  2. Dodaj klasa MyClass do file
  3. metody Add DoSomething do MyClass
  4. Set ciało DoSomething w podobny sposób, jak System.Linq.Expressions

Niedawno odkryliśmy Microsoft.CodeAnalysis.CSharp.SyntaxFactory, i wydawało się obiecujące. Oczywiście musimy sami dodawać ciekawostki.

Po zbudowaniu drzewa o numerze SyntaxFactory.CompilationUnit() i dodaniu niektórych elementów tam i z powrotem, wynik ToFullString() jest po prostu garstką tekstu, który nie jest ani czytelny, ani kompilowany (np. Brakujące nawiasy klamrowe). Czy brakuje nam czegoś podczas generowania tekstu z modelu?

EDIT:

Podczas korzystania obszarów roboczych, można ustawić opcje wpływające na zachowanie białe znaki:

public string Generate (CompilationNode rootNode) 
{ 
    var cw = new CustomWorkspace(); 
    cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true); 
    var formattedCode = Formatter.Format (CreateFile(rootNode), cw); 
    return formattedCode.ToFullString(); 
} 

To już daje lepszy wynik. Czy ktoś może to potwierdzić jako dobre rozwiązanie, czy raczej hack?

Jeden problem pozostaje. Chcemy wygenerować auto-własność, która obecnie używa SF.AccessorDeclaration, ale pomija średnik przy konwersji do pełnego łańcucha.

+0

Twoim celem jest emitowanie kodu, prawda? nie skompilować? – i3arnon

+0

Tak, ale bez wyraźnego określania ciekawostek. Mam też trochę doświadczenia z użyciem ReSharper SDK, który zasadniczo ma to samo pole zastosowania. Oni * mają * fabryki, które nie znają drobiazgów, i drukuje cały wygenerowany kod w sposób czysty i umożliwiający kompilację. Przeszedłem kilka komentarzy innych użytkowników sugerujących użycie CodeDOM, i że Roslyn prawdopodobnie nie nadaje się do tego, ale wydawałoby się, że to największy niepowodzenie w historii :) – Matthias

+0

Cóż ... Roslyn, będący kompilatorem, jest przeznaczony do spożywania kodu, nie produkuj go. – i3arnon

Odpowiedz

10

Zasadniczo trzeba dodać definicje bloków, a następnie Roslyn obsługuje ciekawostki dla ciebie tak długo jak używasz formater (jak napisałem)

Oto przykład prostego klasy, który jest generowany poprawnie bez sam konieczności podać dowolny Ciekawostki

var consoleWriteLine = Syntax.MemberAccessExpression(
     SyntaxKind.SimpleMemberAccessExpression, 
     Syntax.IdentifierName("Console"), 
     name: Syntax.IdentifierName("WriteLine")); 

    var arguments = Syntax.ArgumentList (
     Syntax.SeparatedList (
      new[] 
      { 
       Syntax.Argument (
        Syntax.LiteralExpression (
         SyntaxKind.StringLiteralExpression, 
         Syntax.Literal (@"""Goodbye everyone!""", "Goodbye everyone!"))) 
      })); 

    var consoleWriteLineStatement = Syntax.ExpressionStatement (Syntax.InvocationExpression (consoleWriteLine, arguments)); 

    var voidType = Syntax.ParseTypeName ("void"); 
    var method = Syntax.MethodDeclaration (voidType, "Method").WithBody (Syntax.Block(consoleWriteLineStatement)); 

    var intType = Syntax.ParseTypeName ("int"); 
    var getterBody = Syntax.ReturnStatement (Syntax.DefaultExpression (intType)); 
    var getter = Syntax.AccessorDeclaration (SyntaxKind.GetAccessorDeclaration, Syntax.Block (getterBody)); 
    var property = Syntax.PropertyDeclaration (intType, "Property").WithAccessorList (Syntax.AccessorList (Syntax.SingletonList (getter))); 

    var @class = Syntax.ClassDeclaration ("MyClass").WithMembers (Syntax.List (new MemberDeclarationSyntax[] { method, property })); 

    var cw = new CustomWorkspace(); 
    cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true); 
    var formattedCode = Formatter.Format (@class, cw); 

    Console.WriteLine (formattedCode.ToFullString()); 

Uwaga: Składnia = Microsoft.CodeAnalysis.CSharp.SyntaxFactory

ta generuje następujące cl nazwa użytkownika:

class MyClass 
{ 
    void Method() 
    { 
     Console.WriteLine("Goodbye everyone!"); 
    } 

    int Property 
    { 
     get 
     { 
      return default(int); 
     } 
    } 
} 

Wydaje się być w porządku.