Share via


Arbeta med syntax

Syntaxträdet är en grundläggande oföränderlig datastruktur som exponeras av kompilator-API:erna. Dessa träd representerar källkodens lexikala och syntaktiska struktur. De har två viktiga syften:

  • För att verktyg – till exempel en IDE, tillägg, kodanalysverktyg och refaktoriseringar – ska kunna se och bearbeta den syntaktiska strukturen för källkod i en användares projekt.
  • För att aktivera verktyg – till exempel refaktoriseringar och en IDE – för att skapa, ändra och ordna om källkoden på ett naturligt sätt utan att behöva använda direkttextredigeringar. Genom att skapa och manipulera träd kan verktyg enkelt skapa och ordna om källkoden.

Syntaxträd

Syntaxträd är den primära strukturen som används för kompilering, kodanalys, bindning, refaktorisering, IDE-funktioner och kodgenerering. Ingen del av källkoden förstås utan att den först identifieras och kategoriseras i ett av många välkända strukturella språkelement.

Kommentar

RoslynQuoter är ett verktyg med öppen källkod som visar API-anropen för syntaxfabriken som används för att konstruera ett programs syntaxträd. Om du vill testa live läser du http://roslynquoter.azurewebsites.net.

Syntaxträd har tre nyckelattribut:

  • De innehåller all källinformation i fullständig återgivning. Fullständig återgivning innebär att syntaxträdet innehåller all information som finns i källtexten, varje grammatisk konstruktion, varje lexikal token och allt annat däremellan, inklusive blanksteg, kommentarer och förprocessordirektiv. Till exempel representeras varje literal som nämns i källan exakt som den skrevs. Syntaxträd samlar också in fel i källkoden när programmet är ofullständigt eller felaktigt genom att representera överhoppade eller saknade token.
  • De kan producera exakt den text som de parsades från. Från valfri syntaxnod är det möjligt att hämta textrepresentationen av underträdet rotat på noden. Den här möjligheten innebär att syntaxträd kan användas som ett sätt att konstruera och redigera källtext. Genom att skapa ett träd som du underförstått har skapat motsvarande text, och genom att göra ett nytt träd av ändringar i ett befintligt träd, har du effektivt redigerat texten.
  • De är oföränderliga och trådsäkra. När ett träd har hämtats är det en ögonblicksbild av kodens aktuella tillstånd och ändras aldrig. Detta gör att flera användare kan interagera med samma syntaxträd samtidigt i olika trådar utan att låsa eller duplicera. Eftersom träden är oföränderliga och inga ändringar kan göras direkt i ett träd, hjälper fabriksmetoder till att skapa och ändra syntaxträd genom att skapa ytterligare ögonblicksbilder av trädet. Träden är effektiva på det sätt som de återanvänder underliggande noder, så en ny version kan återskapas snabbt och med lite extra minne.

Ett syntaxträd är bokstavligen en träddatastruktur, där icke-terminala strukturella element är överordnade andra element. Varje syntaxträd består av noder, token och trivia.

Syntaxnoder

Syntaxnoder är ett av de primära elementen i syntaxträd. Dessa noder representerar syntaktiska konstruktioner som deklarationer, instruktioner, satser och uttryck. Varje kategori av syntaxnoder representeras av en separat klass som härleds från Microsoft.CodeAnalysis.SyntaxNode. Uppsättningen nodklasser är inte utökningsbar.

Alla syntaxnoder är icke-terminalnoder i syntaxträdet, vilket innebär att de alltid har andra noder och token som underordnade. Som underordnad en annan nod har varje nod en överordnad nod som kan nås via SyntaxNode.Parent egenskapen. Eftersom noder och träd är oföränderliga ändras aldrig den överordnade noden. Roten i trädet har en null överordnad.

Varje nod har en SyntaxNode.ChildNodes() metod som returnerar en lista över underordnade noder i sekventiell ordning baserat på deras position i källtexten. Den här listan innehåller inte token. Varje nod har också metoder för att undersöka underordnade objekt, till exempel DescendantNodes, DescendantTokenseller DescendantTrivia – som representerar en lista över alla noder, token eller trivia som finns i underträdet som rotas av noden.

Dessutom exponerar varje underklass för syntaxnoder samma underordnade objekt genom starkt skrivna egenskaper. En nodklass har till exempel BinaryExpressionSyntax tre ytterligare egenskaper som är specifika för binära operatorer: Left, OperatorTokenoch Right. Typen av Left och Right är ExpressionSyntax, och typen av OperatorToken är SyntaxToken.

Vissa syntaxnoder har valfria underordnade. Till exempel har en IfStatementSyntax en valfri ElseClauseSyntax. Om det underordnade objektet inte finns returnerar egenskapen null.

Syntaxtoken

Syntaxtoken är terminalerna för språk grammatik, som representerar de minsta syntaktiska fragmenten i koden. De är aldrig föräldrar till andra noder eller token. Syntaxtoken består av nyckelord, identifierare, literaler och skiljetecken.

Av effektivitetsskäl SyntaxToken är typen en CLR-värdetyp. Till skillnad från syntaxnoder finns det därför bara en struktur för alla typer av token med en blandning av egenskaper som har betydelse beroende på vilken typ av token som representeras.

Till exempel representerar en heltalsliteraltoken ett numeriskt värde. Förutom den råa källtexten som token sträcker sig över har literaltoken en Value egenskap som anger det exakta avkodade heltalsvärdet. Den här egenskapen skrivs som Object eftersom den kan vara en av många primitiva typer.

Egenskapen ValueText anger samma information som egenskapen Value , men den här egenskapen skrivs alltid som String. En identifierare i C#-källtext kan innehålla Unicode-escape-tecken, men syntaxen för själva escape-sekvensen anses inte vara en del av identifierarnamnet. Så även om den råa text som sträcker sig över token inkluderar escape-sekvensen ValueText , gör inte egenskapen det. I stället innehåller den De Unicode-tecken som identifieras av escape-objektet. Om källtexten till exempel innehåller en identifierare som skrivs som \u03C0ValueText returneras πegenskapen för den här token .

Syntaxtrivia

Syntaxtrivia representerar de delar av källtexten som till stor del är obetydliga för normal förståelse av koden, till exempel tomt utrymme, kommentarer och förprocessordirektiv. Precis som syntaxtoken är trivia värdetyper. Den enda Microsoft.CodeAnalysis.SyntaxTrivia typen används för att beskriva alla typer av trivia.

Eftersom trivia inte ingår i den normala språksyntaxen och kan visas var som helst mellan två token, ingår de inte i syntaxträdet som underordnad till en nod. Men eftersom de är viktiga när du implementerar en funktion som refaktorisering och för att upprätthålla fullständig återgivning med källtexten, finns de som en del av syntaxträdet.

Du kan komma åt trivia genom att inspektera en tokens SyntaxToken.LeadingTrivia eller SyntaxToken.TrailingTrivia samlingar. När källtexten parsas associeras sekvenser med trivia med token. I allmänhet äger en token alla trivia efter den på samma rad upp till nästa token. Eventuella trivia efter den raden är associerade med följande token. Den första token i källfilen hämtar alla inledande trivia och den sista sekvensen med trivia i filen fästs på filslutstoken, som annars har noll bredd.

Till skillnad från syntaxnoder och token har syntaxtrivia inte överordnade. Men eftersom de är en del av trädet och var och en är associerad med en enda token kan du komma åt den token som den är associerad med med hjälp av SyntaxTrivia.Token egenskapen .

Spänner över

Varje nod, token eller trivia känner till dess position i källtexten och antalet tecken som den består av. En textposition representeras som ett 32-bitars heltal, vilket är ett nollbaserat char index. Ett TextSpan objekt är startpositionen och antalet tecken, som båda representeras som heltal. Om TextSpan har en nolllängd refererar den till en plats mellan två tecken.

Varje nod har två TextSpan egenskaper: Span och FullSpan.

Egenskapen Span är textintervallet från början av den första token i nodens underträd till slutet av den sista token. Det här intervallet omfattar inte några inledande eller avslutande trivia.

Egenskapen FullSpan är det textintervall som innehåller nodens normala spann, plus intervallet för inledande eller avslutande trivia.

Till exempel:

      if (x > 3)
      {
||        // this is bad
          |throw new Exception("Not right.");|  // better exception?||
      }

Instruktionsnoden i blocket har ett intervall som anges av de enskilda lodräta staplarna (|). Den innehåller tecknen throw new Exception("Not right.");. Det fullständiga intervallet indikeras av de dubbla lodräta staplarna (||). Den innehåller samma tecken som spannet och de tecken som är associerade med inledande och avslutande trivia.

Typer

Varje nod, token eller trivia har en SyntaxNode.RawKind egenskap av typen System.Int32, som identifierar det exakta syntaxelementet som representeras. Det här värdet kan omvandlas till en språkspecifik uppräkning. Varje språk, C# eller Visual Basic, har en enda SyntaxKind uppräkning (Microsoft.CodeAnalysis.CSharp.SyntaxKindMicrosoft.CodeAnalysis.VisualBasic.SyntaxKindrespektive) som visar alla möjliga noder, token och trivia-element i grammatiken. Den här konverteringen kan utföras automatiskt genom åtkomst till tilläggsmetoderna CSharpExtensions.Kind eller VisualBasicExtensions.Kind .

Egenskapen RawKind möjliggör enkel tvetydighet av syntaxnodtyper som delar samma nodklass. För token och trivia är den här egenskapen det enda sättet att skilja en typ av element från en annan.

En enskild BinaryExpressionSyntax klass har Lefttill exempel , OperatorTokenoch Right som underordnade. Egenskapen Kind skiljer på om det är en AddExpression, SubtractExpressioneller MultiplyExpression en typ av syntaxnod.

Dricks

Vi rekommenderar att du kontrollerar typer med hjälp av IsKind (för C#) eller IsKind (för VB) tilläggsmetoder.

Fel

Även om källtexten innehåller syntaxfel exponeras ett fullständigt syntaxträd som kan utlösas för källan. När parsern stöter på kod som inte överensstämmer med språkets definierade syntax använder den en av två tekniker för att skapa ett syntaxträd:

  • Om parsern förväntar sig en viss typ av token men inte hittar den kan den infoga en token som saknas i syntaxträdet på den plats där token förväntades. En token som saknas representerar den faktiska token som förväntades, men den har ett tomt intervall och dess SyntaxNode.IsMissing egenskap returnerar true.

  • Parsern kan hoppa över token tills den hittar en där den kan fortsätta parsa. I det här fallet kopplas de överhoppade token som en trivia-nod med typen SkippedTokensTrivia.