martedì 13 dicembre 2011

Roslyn CTP analisi del codice

In ultima rata di C # corner , ho dimostrato come utilizzare le API di scripting con il recentemente rilasciato Roslyn CTP. In questa edizione mi dimostrano come utilizzare la Roslyn Compiler API per analizzare la sintassi e la semantica del codice così come compilarlo.
Tradizionalmente, il compilatore C # è stato un po 'una scatola nera: va in codice come testo, e un eseguibile o una libreria viene fuori. La riflessione si può prendere molto lontano a scoprire la struttura di un'applicazione in esecuzione o di montaggio, ma ha i suoi limiti. Con Roslyn si può letteralmente leggere un file C # o soluzione di Visual Studio, analizzare il codice, modificarlo e compilare una nuova assemblea.
L'analisi sintattica
Un albero di sintassi è la rappresentazione completa di un pezzo di codice analizzato. Esso contiene i nodi sintassi, gettoni e curiosità.
  • Nodi sintassi rappresentano espressioni, affermazioni e dichiarazioni
  • Gettoni sintassi rappresentano le parole chiave, identificatori, e gli operatori
  • Trivia sintassi rappresentano spazi, caratteri di fine linea, commenti e altri dati irrilevanti dell'albero di analisi
Per analizzare un semplice programma come testo in un albero di sintassi che è possibile utilizzare il metodo ParseCompilationUnit fabbrica in classe SyntaxTree.
SyntaxTree albero = SyntaxTree
  . ParseCompilationUnit (@ "using System;
                                         utilizzando System.Collections.Generic;
                                         using System.Linq;
                                         utilizzando System.Text;
                                         
                                         namespace VSMRoslynDemo
                                         {
                                             classe Program
                                             {
                                                 static void Main (string [] args)
                                                 {
                                                     Console.WriteLine ("" Ciao !"");
                                                                        }
                                                                    }
                                                                } ");
Con l'albero di sintassi è ora possibile iniziare l'analisi della struttura completa del programma. Per esempio, per trovare tutte le istruzioni utilizzare il programma è possibile eseguire il seguente frammento:


 IEnumerable <UsingDirectiveSyntax> using = 
tree.Root.DescendentNodes () OfType <UsingDirectiveSyntax> ().;
Una volta che hai il utilizzando le istruzioni, è possibile recuperare gli spazi dei nomi facilmente utilizzando LINQ.
 Namespace <string> IEnumerable = 
(da u in using selezionare u.Name.GetFullText ());
Allo stesso modo, per recuperare tutte le dichiarazioni di metodo sarebbe:
IEnumerable <MethodDeclarationSyntax> metodi = 
tree.Root.DescendentNodes () OfType <MethodDeclarationSyntax> ().;
Le strutture UsingDirectiveSyntax e MethodDeclarationSyntax sono ogni derivati ​​del SyntaxNode e contenere tutte le informazioni necessarie a descrivere pienamente l'utilizzo e l'espressione metodo rispettivamente.
Analisi semantica
Mentre attraversa un SyntaxNode è un esercizio interessante in sé, ci sono altri mezzi per determinare la semantica del codice. La classe Compilation viene utilizzato per analizzare e compilare alberi di sintassi di uno o molti.
Per creare un oggetto di compilazione, il metodo factory Create sulla classe Compilation viene utilizzato. Per esempio, per creare una nuova compilation chiamata "TestProgram.exe" utilizzando l'albero di sintassi creato in precedenza, si dovrebbe eseguire:
Compilazione compilazione = Compilation.Create ("TestProgram.exe",
syntaxTrees: new [] {} albero,
Opzioni: CompilationOptions nuovo (assemblyKind: AssemblyKind.ConsoleApplication),
Riferimenti: new [] {
                AssemblyFileReference nuovo (typeof (oggetto). Assembly.Location),
                AssemblyFileReference nuovo (typeof (IQueryable <>). Assembly.Location)
});
Una raccolta contiene tutte le informazioni necessarie per compilare un programma. Questo include tutti i file sorgenti necessari, i riferimenti di montaggio e le opzioni di compilazione. I riferimenti utilizzati per la compilazione sono memorizzati nella proprietà riferimenti e le opzioni del compilatore vengono memorizzate nella proprietà Opzioni. Ho dichiarato la compilazione TestProgram.exe come un'applicazione console con riferimenti a entrambi mscorlib.dll e System.Core.dll.
Un oggetto di compilazione consente anche per il recupero di un SemanticModel di un SyntaxTree dato attraverso il metodo GetSemanticModel:
SemanticModel semanticModel = compilation.GetSemanticModel (albero);
Con un oggetto SemanticModel, si può facilmente recuperare simboli in tutto il progetto. Un simbolo rappresenta sia un tipo, namespace, variabile o metodo. I simboli possono essere recuperati da una struttura SyntaxNode dato.
Ad esempio, per recuperare tutti i tipi definiti nello spazio dei nomi System.Collections.Generic, devi prima scaricare il SemanticInfo per il nodo NameSyntax che rappresenta lo spazio dei nomi e quindi recuperare il NamespaceSymbol attraverso il SemanticModel:
 CompilationUnitSyntax root = (CompilationUnitSyntax) tree.Root;
            NameSyntax SystemName = root.Usings [1] Nome.;
            SystemNameSemantics SemanticInfo = semanticModel.GetSemanticInfo (SystemName);
            NamespaceSymbol = systemNameSymbol (NamespaceSymbol) systemNameSemantics.Symbol;
 IEnumerable <NamedTypeSymbol> typeSymbols = systemNameSymbol.GetTypeMembers ();
È quindi possibile utilizzare la raccolta NamedTypeSmbol per recuperare tutti i metodi accessibile all'utente per tipo, per esempio:
Console.WriteLine (String.Format ("Tipi definiti in {0}", systemNameSymbol.ToDisplayString ()));
            foreach (var typeSymbol in typeSymbols)
            {
                Console.WriteLine (typeSymbol.Name);
 
                foreach (var membro in typeSymbol.GetMembers ())
                {
                    if (member.CanBeReferencedByName)
                    {
                        Console.WriteLine ("\ t" + member.ToDisplayString ());

[Clicca sull'immagine per ingrandirla.]
Figura 1. Estratto della produzione di tipi e membri System.Collection.Generic utilizzando Rosyln.
Compilazione Un esempio di compilazione può anche essere usato per emettere un assembly compilato o eseguibile in un flusso o file. Il programma di prova di cui l'albero della sintassi può essere compilato in un eseguibile utilizzando il metodo Emit sull'oggetto Compilation, come segue:
utilizzando (var file = nuovo FileStream ("TestProgram.exe", FileMode.Create))
            {
                var result = compilation.Emit (file);
            }
[Clicca sull'immagine per ingrandirla.]
Figura 2. Esecuzione TestProgram.exe compilati dall'applicazione host.
Strumento utile
Ho appena dimostrato come Rosyln può essere utilizzato per analizzare e compilare il codice. Queste due caratteristiche, insieme alla sua capacità di script di hosting, dovrebbe risultare molto utile agli sviluppatori di strumenti e sviluppatori di applicazioni simili. Se vuoi saperne di più su Roslyn, come ad esempio come creare un refactoring personalizzati, si prega di lasciare un commento qui sotto.

Nessun commento:

Posta un commento