Práce se syntaxí
Strom syntaxe je základní neměnná datová struktura vystavená rozhraními API kompilátoru. Tyto stromy představují lexikální a syntaktickou strukturu zdrojového kódu. Slouží dvěma důležitým účelům:
- Pokud chcete povolit nástroje , jako je integrované vývojové prostředí (IDE), doplňky, nástroje pro analýzu kódu a refaktoringy, můžete zobrazit a zpracovat syntaktickou strukturu zdrojového kódu v projektu uživatele.
- Pokud chcete povolit nástroje , jako jsou refaktoringy a integrované vývojové prostředí (IDE), můžete vytvářet, upravovat a měnit uspořádání zdrojového kódu přirozeným způsobem, aniž byste museli používat přímé úpravy textu. Díky vytváření stromů a manipulaci s nimi můžou nástroje snadno vytvářet a měnit uspořádání zdrojového kódu.
Stromy syntaxe
Stromy syntaxe jsou primární strukturou používanou k kompilaci, analýze kódu, vazbě, refaktoringu, funkcím ide a generování kódu. Žádná část zdrojového kódu není srozumitelná, aniž by byla nejprve identifikována a kategorizována do některého z mnoha známých prvků strukturálního jazyka.
Poznámka:
RoslynQuoter je opensourcový nástroj, který zobrazuje volání rozhraní API pro vytváření syntaxe používané k vytvoření stromu syntaxe programu. Pokud si to chcete vyzkoušet živě, podívejte http://roslynquoter.azurewebsites.netse.
Stromy syntaxe mají tři klíčové atributy:
- Všechny informace o zdroji uchovávají v plné věrnosti. Úplná věrnost znamená, že strom syntaxe obsahuje všechny informace nalezené ve zdrojovém textu, každý gramatický konstruktor, každý lexikální token a vše ostatní mezi, včetně prázdných znaků, komentářů a direktiv preprocesoru. Například každý literál uvedený ve zdroji je reprezentován přesně tak, jak byl zadán. Stromy syntaxe také zaznamenávají chyby ve zdrojovém kódu, když je program neúplný nebo poškozený reprezentací přeskočených nebo chybějících tokenů.
- Mohou vytvořit přesný text, ze kterého byly analyzovány. Z libovolného uzlu syntaxe je možné získat textovou reprezentaci podstromu kořenového adresáře v daném uzlu. Tato schopnost znamená, že stromy syntaxe lze použít jako způsob, jak vytvořit a upravit zdrojový text. Vytvořením stromu, který máte, implikací, vytvořením ekvivalentního textu a vytvořením nového stromu ze změn existujícího stromu jste text efektivně upravili.
- Jsou neměnné a bezpečné pro přístup z více vláken. Po získání stromu se jedná o snímek aktuálního stavu kódu a nikdy se nezmění. To umožňuje více uživatelům pracovat se stejným stromem syntaxe najednou v různých vláknech bez uzamčení nebo duplikace. Vzhledem k tomu, že stromy jsou neměnné a žádné úpravy nelze provést přímo ve stromu, metody továrny pomáhají vytvářet a upravovat stromy syntaxe vytvořením dalších snímků stromu. Stromy jsou efektivní tak, jak opakovaně používají podkladové uzly, takže novou verzi je možné rychle vytvořit a s malou pamětí navíc.
Strom syntaxe je doslova datová struktura stromu, kde jiné než terminálové strukturální prvky nadřazené jiným prvkům. Každý strom syntaxe se skládá z uzlů, tokenů a trivií.
Uzly syntaxe
Uzly syntaxe jsou jedním z primárních prvků stromů syntaxe. Tyto uzly představují syntaktické konstrukce, jako jsou deklarace, příkazy, klauzule a výrazy. Každá kategorie uzlů syntaxe je reprezentována samostatnou třídou odvozenou od Microsoft.CodeAnalysis.SyntaxNode. Sada tříd uzlů není rozšiřitelná.
Všechny uzly syntaxe nejsou terminálové uzly ve stromu syntaxe, což znamená, že vždy mají jiné uzly a tokeny jako podřízené položky. Jako podřízený uzel jiného uzlu má každý uzel nadřazený uzel, ke kterému je možné přistupovat prostřednictvím SyntaxNode.Parent vlastnosti. Vzhledem k tomu, že uzly a stromy jsou neměnné, nadřazený uzel se nikdy nezmění. Kořen stromu má nadřazenou hodnotu null.
Každý uzel má metodu SyntaxNode.ChildNodes() , která vrací seznam podřízených uzlů v sekvenčním pořadí na základě jejich pozice ve zdrojovém textu. Tento seznam neobsahuje tokeny. Každý uzel má také metody pro zkoumání potomků, například DescendantNodes, DescendantTokensnebo DescendantTrivia – představující seznam všech uzlů, tokenů nebo trivií, které existují v podstromu kořenovém kořenem daného uzlu.
Kromě toho každá podtřída uzlu syntaxe zveřejňuje všechny stejné podřízené položky prostřednictvím vlastností silného typu. Třída uzlu má například BinaryExpressionSyntax tři další vlastnosti specifické pro binární operátory: Left, OperatorTokena Right. Typ Left a Right je ExpressionSyntaxa typ OperatorToken je SyntaxToken.
Některé uzly syntaxe mají volitelné podřízené položky. Například má IfStatementSyntax volitelnou položku ElseClauseSyntax. Pokud podřízená položka není přítomna, vrátí vlastnost hodnotu null.
Tokeny syntaxe
Tokeny syntaxe jsou terminály gramatiky jazyka, které představují nejmenší syntaktické fragmenty kódu. Nikdy nejsou nadřazené jiným uzlům ani tokenům. Tokeny syntaxe se skládají z klíčových slov, identifikátorů, literálů a interpunkce.
Pro účely efektivity SyntaxToken je typ hodnoty CLR. Proto na rozdíl od uzlů syntaxe existuje pouze jedna struktura pro všechny druhy tokenů s kombinací vlastností, které mají význam v závislosti na typu tokenu, který je reprezentován.
Například celočíselný literál představuje číselnou hodnotu. Kromě nezpracovaného zdrojového textu, který token zahrnuje, má literál token Value vlastnost, která vám řekne přesnou dekódovanou celočíselnou hodnotu. Tato vlastnost je zadána, protože Object může být jedním z mnoha primitivních typů.
Vlastnost ValueText vám řekne stejné informace jako Value vlastnost, ale tato vlastnost je vždy zadána jako String. Identifikátor ve zdrojovém textu jazyka C# může obsahovat řídicí znaky Unicode, ale syntaxe samotné řídicí sekvence se nepovažuje za součást názvu identifikátoru. I když nezpracovaný text rozložený tokenem zahrnuje řídicí sekvenci, ValueText vlastnost ne. Místo toho obsahuje znaky Unicode identifikované řídicím znakem. Pokud například zdrojový text obsahuje identifikátor zapsaný jako \u03C0
, ValueText vrátí π
vlastnost tohoto tokenu .
Syntaxe trivia
Syntaxe trivia představuje části zdrojového textu, které jsou z velké části nevýznamné pro normální pochopení kódu, jako jsou prázdné znaky, komentáře a direktivy preprocesoru. Stejně jako tokeny syntaxe jsou trivia typy hodnot. Jeden Microsoft.CodeAnalysis.SyntaxTrivia typ se používá k popisu všech druhů trivií.
Vzhledem k tomu, že trivia není součástí normální syntaxe jazyka a může se objevit kdekoli mezi dvěma tokeny, nejsou zahrnuty do stromu syntaxe jako podřízený uzel. Vzhledem k tomu, že jsou důležité při implementaci funkce, jako je refaktoring a zachování úplné věrnosti se zdrojovým textem, existují jako součást stromu syntaxe.
Ke triviím můžete přistupovat kontrolou tokenů SyntaxToken.LeadingTrivia nebo SyntaxToken.TrailingTrivia kolekcí. Při analýze zdrojového textu jsou sekvence trivií přidružené k tokenům. Obecně platí, že token vlastní jakoukoli trivii za ním na stejném řádku až do dalšího tokenu. Jakákoli trivia za tímto řádkem je přidružena k následujícímu tokenu. První token ve zdrojovém souboru získá veškerou počáteční trivii a poslední posloupnost trivií v souboru se vysadí na token konce souboru, který má jinak nulovou šířku.
Na rozdíl od uzlů syntaxe a tokenů syntaxe nemají syntaxe nadřazené prvky. Vzhledem k tomu, že jsou součástí stromu a každý je přidružený k jednomu tokenu, můžete získat přístup k tokenu, který je přidružený k použití SyntaxTrivia.Token vlastnosti.
Rozsahy
Každý uzel, token nebo trivia zná svou pozici ve zdrojovém textu a počet znaků, ze které se skládá. Pozice textu je reprezentována jako 32bitové celé číslo, což je index založený na char
nule. Objekt TextSpan je počáteční pozice a počet znaků, které jsou reprezentovány jako celá čísla. Pokud TextSpan má nulovou délku, odkazuje na umístění mezi dvěma znaky.
Každý uzel má dvě TextSpan vlastnosti: Span a FullSpan.
Vlastnost Span je rozsah textu od začátku prvního tokenu v podstromu uzlu na konec posledního tokenu. Toto rozpětí neobsahuje žádnou úvodní nebo koncovou trivii.
Vlastnost FullSpan je rozsah textu, který zahrnuje normální rozsah uzlu a rozsah všech úvodních nebo koncových trivií.
Příklad:
if (x > 3)
{
|| // this is bad
|throw new Exception("Not right.");| // better exception?||
}
Uzel příkazu uvnitř bloku má rozsah označený jedním svislým pruhem (|). Obsahuje znaky throw new Exception("Not right.");
. Celé rozpětí je označeno dvojitými svislými pruhy (||). Obsahuje stejné znaky jako rozpětí a znaky spojené s úvodní a koncovou trivií.
Druhů
Každý uzel, token nebo trivia má SyntaxNode.RawKind vlastnost typu System.Int32, která identifikuje přesný prvek syntaxe reprezentovaný. Tuto hodnotu lze přetypovat do výčtu specifického jazyka. Každý jazyk, C# nebo Visual Basic, má jeden SyntaxKind
výčet (Microsoft.CodeAnalysis.CSharp.SyntaxKind a Microsoft.CodeAnalysis.VisualBasic.SyntaxKindv uvedeném pořadí), který obsahuje seznam všech možných uzlů, tokenů a trivií v gramatikě. Tento převod lze provést automaticky přístupem k CSharpExtensions.Kind metodám rozšíření nebo VisualBasicExtensions.Kind metodám rozšíření.
Tato RawKind vlastnost umožňuje snadnou nejednoznačnost typů uzlů syntaxe, které sdílejí stejnou třídu uzlů. U tokenů a trivií je tato vlastnost jediným způsobem, jak odlišit jeden typ prvku od jiného.
Například jedna BinaryExpressionSyntax třída má Left, OperatorTokena Right jako děti. Vlastnost Kind rozlišuje, zda se jedná o AddExpressionuzel , SubtractExpressionnebo MultiplyExpression druh syntaxe.
Tip
Doporučujeme zkontrolovat typy použití IsKind (pro jazyk C#) nebo IsKind (pro metody rozšíření VB).
Chyby
I když zdrojový text obsahuje chyby syntaxe, zobrazí se úplný strom syntaxe, který je do zdroje zaokrouhlený. Když analyzátor narazí na kód, který neodpovídá definované syntaxi jazyka, použije k vytvoření stromu syntaxe jednu ze dvou technik:
Pokud analyzátor očekává určitý druh tokenu, ale nenajde ho, může do stromu syntaxe vložit chybějící token do požadovaného umístění tokenu. Chybějící token představuje skutečný token, který byl očekáváno, ale má prázdné rozpětí a jeho SyntaxNode.IsMissing vlastnost vrátí
true
.Analyzátor může tokeny přeskočit, dokud nenajde, kde může pokračovat v analýze. V tomto případě jsou přeskočené tokeny připojeny jako trivia uzel s druhem SkippedTokensTrivia.