Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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:
- Aby nástroje - jako integrované vývojové prostředí (IDE), doplňky, nástroje pro analýzu kódu a refaktoringy - mohly vidět a zpracovat syntaktickou strukturu zdrojového kódu v projektu uživatele.
- K povolení nástrojů, jako jsou refaktoringy a integrované vývojové prostředí (IDE), k vytváření, úpravám a změnám uspořádání zdrojového kódu přirozeným způsobem, bez nutnosti používání přímých textových úprav. 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 se na http://roslynquoter.azurewebsites.net.
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 zpracovány. Z libovolného uzlu syntaxe je možné získat textovou reprezentaci podstromu kořenícího v tomto uzlu. Tato schopnost znamená, že stromy syntaxe lze použít jako způsob, jak vytvořit a upravit zdrojový text. Tím, že vytvoříte strom, zprostředkovaně vytvoříte ekvivalentní text, a když z úprav existujícího stromu vytvoříte nový strom, účinně upravíte text.
- Jsou neměnné a bezpečné při použití ve více vláknech. 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é, nelze je přímo upravovat. Tovární metody pomáhají vytvářet a modifikovat syntaxové stromy tím, že vytvářejí další snímky. 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.
Syntaktický strom je datová struktura stromu, kde neterminální strukturní prvky jsou nadřazeny jiným prvkům. Každý strom syntaxe se skládá z uzlů, tokenů a trivií.
Syntaktické uzly
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 jsou neterminálové uzly v syntaktickém stromu, což znamená, že vždy mají jiné uzly a tokeny jako potomky. 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é, rodič uzlu 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, DescendantTokens nebo DescendantTrivia – které představují seznam všech uzlů, tokenů nebo dalších nepatrných prvků existujících v podstromu s kořenem v daném 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 potomek není přítomen, vrátí vlastnost hodnotu null.
Syntaktické tokeny
Tokeny syntaxe jsou terminály gramatiky jazyka, které představují nejmenší syntaktické fragmenty kódu. Nikdy nejsou rodiči jiných uzlů ani tokenů. Tokeny syntaxe se skládají z klíčových slov, identifikátorů, literálů a interpunkce.
Typ SyntaxToken je kvůli efektivitě hodnotovým typem 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ě surového zdrojového textu, který token zahrnuje, má doslovný token vlastnost Value, která vám poskytuje přesnou dekódovanou celočíselnou hodnotu. Tato vlastnost je typu Object, protože 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. Ačkoli surový text zahrnutý tokenem obsahuje řídicí sekvenci, vlastnost ValueText ji neobsahuje. Místo toho obsahuje znaky Unicode identifikované escapovací sekvencí. Pokud například zdrojový text obsahuje identifikátor zapsaný jako \u03C0, potom vlastnost pro tento token ValueText vrátí π.
Syntax zajímavosti
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 nejsou součástí normální syntaxe jazyka a mohou se objevit kdekoli mezi dvěma tokeny, nejsou zahrnuta do stromu syntaxe jako potomek uzlu. 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.
K trivii můžete přistupovat kontrolou kolekcí tokenu SyntaxToken.LeadingTrivia nebo SyntaxToken.TrailingTrivia. 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í syntaxtrivia žádné nadřazené uzly. 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ý pomocí vlastnosti SyntaxTrivia.Token.
Rozpětí
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í.
Napří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í.
Druhy
Každý uzel, token nebo trivia má SyntaxNode.RawKind vlastnost typu System.Int32, která identifikuje přesný prvek syntaxe reprezentovaný. Tuto hodnotu lze převést na výčet specifický pro jazyk. 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 metodám rozšíření CSharpExtensions.Kind nebo VisualBasicExtensions.Kind.
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.
Návod
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, objeví se úplný strom syntaxe, který je možné převést zpět do původního zdroje. 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án, ale má prázdný rozsah 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 uzel typu "trivia" s označením SkippedTokensTrivia.