Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
L'albero della sintassi è una struttura di dati non modificabile fondamentale esposta dalle API del compilatore. Questi alberi rappresentano la struttura lessicale e sintattica del codice sorgente. Servono due scopi importanti:
- Per consentire agli strumenti, ad esempio un IDE, componenti aggiuntivi, strumenti di analisi del codice e refactoring, di visualizzare ed elaborare la struttura sintattica del codice sorgente nel progetto di un utente.
- Per abilitare strumenti, ad esempio refactoring e un IDE, per creare, modificare e ridisporre il codice sorgente in modo naturale senza dover usare modifiche di testo dirette. Creando e modificando alberi, gli strumenti possono creare e ridisporre facilmente il codice sorgente.
Alberi della sintassi
Gli alberi della sintassi sono la struttura principale usata per la compilazione, l'analisi del codice, l'associazione, il refactoring, le funzionalità dell'IDE e la generazione di codice. Nessuna parte del codice sorgente viene riconosciuta senza prima essere identificata e categorizzata in uno dei molti elementi del linguaggio strutturale noti.
Annotazioni
RoslynQuoter è uno strumento open source che mostra le chiamate API factory della sintassi usate per costruire l'albero della sintassi di un programma. Per provarlo in tempo reale, vedere http://roslynquoter.azurewebsites.net.
Gli alberi della sintassi hanno tre attributi chiave:
- Contengono tutte le informazioni di origine in piena fedeltà. La massima fedeltà significa che l'albero della sintassi contiene ogni informazione presente nel testo di origine, ogni costrutto grammaticale, ogni token lessicale e tutto il resto tra, inclusi spazi vuoti, commenti e direttive del preprocessore. Ad esempio, ogni valore letterale menzionato nell'origine è rappresentato esattamente come è stato tipizzato. Gli alberi della sintassi acquisiscono anche gli errori nel codice sorgente quando il programma è incompleto o malformato, rappresentando token mancati o omessi.
- Possono produrre il testo esatto da cui sono stati analizzati. Da qualsiasi nodo sintattico, è possibile ottenere la rappresentazione testuale del sottoalbero radicato in tale nodo. Questa capacità significa che gli alberi della sintassi possono essere usati come modo per costruire e modificare il testo di origine. Creando un albero, di conseguenza, hai creato il testo equivalente e, creando un nuovo albero dalle modifiche a un albero esistente, hai modificato il testo in modo efficace.
- Sono non modificabili e thread-safe. Dopo aver ottenuto un albero, si tratta di uno snapshot dello stato corrente del codice e non cambia mai. Ciò consente a più utenti di interagire con lo stesso albero della sintassi contemporaneamente in thread diversi senza blocco o duplicazione. Poiché gli alberi non sono modificabili e non è possibile apportare modifiche direttamente a un albero, i metodi factory consentono di creare e modificare alberi della sintassi creando snapshot aggiuntivi dell'albero. Gli alberi sono efficienti nel modo in cui riutilizzano i nodi sottostanti, quindi una nuova versione può essere ricompilata rapidamente e con poca memoria aggiuntiva.
Un albero della sintassi è letteralmente una struttura di dati ad albero, in cui gli elementi strutturali non terminal sono padre di altri elementi. Ogni albero della sintassi è costituito da nodi, token e trivialità.
Nodi della sintassi
I nodi della sintassi sono uno degli elementi principali degli alberi della sintassi. Questi nodi rappresentano costrutti sintattici, ad esempio dichiarazioni, istruzioni, clausole ed espressioni. Ogni categoria di nodi della sintassi è rappresentata da una classe separata derivata da Microsoft.CodeAnalysis.SyntaxNode. Il set di classi di nodi non è estendibile.
Tutti i nodi sintattici sono nodi non terminali nell'albero della sintassi, il che significa che hanno sempre altri nodi e token come elementi figli. Come figlio di un altro nodo, ogni nodo ha un nodo padre accessibile tramite la SyntaxNode.Parent proprietà . Poiché i nodi e gli alberi non sono modificabili, l'elemento padre di un nodo non cambia mai. La radice dell'albero non ha alcun genitore.
Ogni nodo ha un SyntaxNode.ChildNodes() metodo, che restituisce un elenco di nodi figlio in ordine sequenziale in base alla relativa posizione nel testo di origine. Questo elenco non contiene token. Ogni nodo include anche metodi per esaminare Discendenti, ad esempio DescendantNodes, DescendantTokenso DescendantTrivia , che rappresentano un elenco di tutti i nodi, i token o i trivia esistenti nel sottoalbero rooted da tale nodo.
Inoltre, ogni sottoclasse di nodo della sintassi espone tutti i figli stessi tramite proprietà fortemente tipizzate. Ad esempio, una BinaryExpressionSyntax classe node ha tre proprietà aggiuntive specifiche per gli operatori binari: Left, OperatorTokene Right. Il tipo di Left e Right è ExpressionSyntaxe il tipo di OperatorToken è SyntaxToken.
Alcuni nodi della sintassi hanno figli facoltativi. Ad esempio, un oggetto IfStatementSyntax ha un oggetto facoltativo ElseClauseSyntax. Se l'elemento figlio non è presente, la proprietà restituisce Null.
Token di sintassi
I token di sintassi sono i terminali della grammatica del linguaggio, che rappresentano i frammenti sintattici più piccoli del codice. Non sono mai elementi padre di altri nodi o token. I token di sintassi sono costituiti da parole chiave, identificatori, valori letterali e punteggiatura.
Ai fini dell'efficienza, il SyntaxToken tipo è un tipo di valore CLR. Pertanto, a differenza dei nodi della sintassi, esiste una sola struttura per tutti i tipi di token con una combinazione di proprietà che hanno un significato a seconda del tipo di token rappresentato.
Ad esempio, un token letterale integer rappresenta un valore numerico. Oltre al testo sorgente grezzo, il token letterale ha una proprietà Value che indica il valore numerico intero decodificato esatto. Questa proprietà viene tipizzata come Object perché può essere uno dei molti tipi primitivi.
La ValueText proprietà indica le stesse informazioni della Value proprietà, ma questa proprietà viene sempre digitata come String. Un identificatore nel testo di origine C# può includere caratteri di escape Unicode, ma la sintassi della sequenza di escape stessa non viene considerata parte del nome dell'identificatore. Pertanto, anche se il testo non elaborato esteso dal token include la sequenza di escape, la proprietà ValueText non lo fa. Include invece i caratteri Unicode identificati dall'escape. Ad esempio, se il testo di origine contiene un identificatore scritto come \u03C0
, la ValueText proprietà per questo token restituirà π
.
Sintassi dettagliata
La sintassi dettagliata rappresenta le parti del testo di origine che sono in gran parte insignificanti per la normale comprensione del codice, ad esempio spazi vuoti, commenti e direttive del preprocessore. Come i token di sintassi, i trivia sono tipi di valore. Il singolo Microsoft.CodeAnalysis.SyntaxTrivia tipo viene usato per descrivere tutti i tipi di curiosità.
Poiché i trivia non fanno parte della sintassi normale del linguaggio e possono essere visualizzati ovunque tra due token, non vengono inclusi nell'albero della sintassi come elemento figlio di un nodo. Tuttavia, poiché sono importanti quando si implementa una funzionalità come il refactoring e per mantenere la massima fedeltà con il testo di origine, esistono come parte dell'albero della sintassi.
È possibile accedere alle curiosità esaminando le raccolte di SyntaxToken.LeadingTrivia o SyntaxToken.TrailingTrivia token. Quando viene analizzato il testo di origine, le sequenze di elementi semplici vengono associate ai token. In generale, un token è proprietario di tutti i dettagli dopo di esso nella stessa riga fino al token successivo. Qualsiasi curiosità dopo tale riga è associata al token seguente. Il primo token nel file di origine ottiene tutte le informazioni iniziali e l'ultimo insieme di informazioni nel file viene aggiunto al token della fine del file, che altrimenti avrebbe larghezza zero.
A differenza dei nodi di sintassi e dei token, le informazioni sui dettagli di sintassi non hanno elementi padre. Tuttavia, poiché fanno parte dell'albero e ognuno è associato a un singolo token, è possibile accedere al token a cui è associato usando la SyntaxTrivia.Token proprietà .
Intervalli
Ogni nodo, token o curiosità conosce la sua posizione all'interno del testo di origine e il numero di caratteri di cui è costituito. Una posizione di testo è rappresentata come un intero a 32 bit, che è un indice che parte da zero char
. Un TextSpan oggetto è la posizione iniziale e un conteggio di caratteri, entrambi rappresentati come numeri interi. Se TextSpan ha una lunghezza zero, fa riferimento a una posizione tra due caratteri.
Ogni nodo ha due TextSpan proprietà: Span e FullSpan.
La Span proprietà è l'intervallo di testo dall'inizio del primo token nel sottoalbero del nodo alla fine dell'ultimo token. Questo intervallo non include alcun dettaglio non essenziale iniziale o finale.
La FullSpan proprietà è l'intervallo di testo che include l'intervallo normale del nodo, oltre all'intervallo di tutti i trivia iniziali o finali.
Per esempio:
if (x > 3)
{
|| // this is bad
|throw new Exception("Not right.");| // better exception?||
}
Il nodo dell'istruzione all'interno del blocco ha un intervallo indicato dalle singole barre verticali (|). Include i caratteri throw new Exception("Not right.");
. L'intervallo completo è indicato dalle barre verticali doppie (||). Include gli stessi caratteri dell'intervallo e i caratteri associati ai caratteri non essenziali precedenti e successivi.
Tipi
Ogni nodo, token o curiosità ha una SyntaxNode.RawKind proprietà di tipo System.Int32, che identifica l'elemento di sintassi esatto rappresentato. È possibile eseguire il cast di questo valore a un'enumerazione specifica del linguaggio. Ogni linguaggio, C# o Visual Basic, ha una singola SyntaxKind
enumerazione (Microsoft.CodeAnalysis.CSharp.SyntaxKind e Microsoft.CodeAnalysis.VisualBasic.SyntaxKind, rispettivamente) che elenca tutti i possibili nodi, token e elementi trivia nella grammatica. Questa conversione può essere eseguita automaticamente accedendo al metodo di estensione CSharpExtensions.Kind o al metodo di estensione VisualBasicExtensions.Kind.
La RawKind proprietà consente di disambiguare facilmente i tipi di nodo della sintassi che condividono la stessa classe del nodo. Per i token e i trivia, questa proprietà è l'unico modo per distinguere un tipo di elemento da un altro.
Ad esempio, una singola BinaryExpressionSyntax classe ha Left, OperatorTokene Right come elementi figlio. La Kind proprietà distingue se si tratta di un AddExpressionnodo di sintassi , SubtractExpressiono MultiplyExpression .
Suggerimento
È consigliabile controllare i tipi usando IsKind (per C#) o IsKind (per VB) metodi di estensione.
Errori
Anche quando il testo di origine contiene errori di sintassi, viene esposto un albero di sintassi completo che può essere convertito di nuovo alla forma originale. Quando il parser rileva codice non conforme alla sintassi definita del linguaggio, usa una delle due tecniche per creare un albero della sintassi:
Se il parser prevede un particolare tipo di token ma non lo trova, potrebbe inserire un token mancante nell'albero della sintassi nella posizione prevista dal token. Un token mancante rappresenta il token effettivo previsto, ma ha un intervallo vuoto e la relativa SyntaxNode.IsMissing proprietà restituisce
true
.Il parser può ignorare i token finché non ne trova uno in cui può continuare l'analisi. In questo caso, i token ignorati vengono collegati come nodo trivia con il tipo SkippedTokensTrivia.