Roslyn – syntaktické stromy

Ondřej Janáček       6. 7. 2014       C#, VB.NET, .NET       6061 zobrazení

Syntaktické stromy jsou pravděpodobně nejdůležitější datovou strukturou spodní vrstvy Roslyn API, protože bez nich se žádný nástroj a ani samotný kompilátor nikam nepohnou. Proto se na ně podíváme opravdu zblízka. Vysvětlíme si, z jakých stavebních prvků se skládají a jaké mají důležité vlastnosti.

Následuje seznam všech předcházejících článků série, které doporučuji přečíst, než se vrhnete na tento.

  1. Seznamte se s Roslynem
  2. Roslyn - pracovní prostředí

compiler api

Opět připomenu, že se jedná o následující vrstvu, jejíž pravděpodobně nejzajímavější částí je pro nás syntaktická a sémantická reprezentace zdrojového kódu.

compiler layer

I tato vrstva otevírá nové možnosti, i když v tomto případě je potencionálních konzumentů mnohem méně, než v případě vrstvy workspaces. Cituji v překladu:

“Výhody Roslynu osobně vidím ve třech kruzích – desítky, tisíce a milióny uživatelů.

Pro desítky – to jsme my, C# a Visual Basic design tým – malé novinky v jazyce budou mít konečně i malou cenu implementace díky dobře navrhnuté architektuře.

Pro tisíce – interní i externí partneři – to znamená otevřený přístup k interním informacím například při vývoji nástrojů do Visual Studia. Jako dobrý příklad mě napadají Code Lens z VS 2013, které byly postaveny na starší verzi Roslynu.

Pro milióny – všichni budeme těžit z velké řady nástrojů a celkové user experience (pojem vysvětlen zde).” (D. Campbell, .NET Compiler Platform (“Roslyn”) for the Rest of Us)

Syntaktický strom

Syntaktické stromy (dále označovány jako AST) jsou primární datová struktura při kompilaci, analýze kódu, sémantické analýze a žádná část zdrojového kódu není z pohledu nástrojů rozpoznána, dokud není k dispozici AST reprezentovaný strukturou předdefinovaných elementů. AST mají tři hlavní vlastnosti:

  • Udržují si informace o všem, co je ve zdrojovém textu, včetně bílých znaků, komentářů a dokonce i chyb ve zdrojovém kódu, který je nekompletní.
  • Díky první vlastnosti je možné AST převést zpět na text v téže podobě, z jaké byl rozparsován. To umožňuje provádět rozsáhlé změny ve struktuře souboru a stále zachovat formát, který si uživatel definoval.
  • AST je neměnný, jakmile je jednou postavený. To nabízí spoustu výhod s touto vlastností spojenou, ale také otázku ohledně konzumace paměti. AST snapshoty vytvořené na základě jiných recyklují dříve vyrobené uzly stromu a proces je tak rychlý a paměťově nenáročný.

AST je postaven ze tří druhů elementů (jejichž označení nebudu překládat) – node, token, trivia. Aby byl následující popis jednotlivých elementů lépe uchopitelný, vytvořil jsem malý příklad, na který se budu celou dobu odkazovat a vše na něm vysvětlovat. Nemusíte se tedy trápit, jestli mu rovnou nerozumíte, prostě pokračujte v čtení a vracejte se k příkladu, kdykoliv se na něj odkážu.

int a = 1 + 2;

Pro tento kousek kódu Roslyn vygeneruje následující AST. Na jediný řádek kódu je to poměrně obsáhlá struktura, nemyslíte?

simple syntax tree

 

Syntax node

Nody jsou v obrázku označeny modře. Reprezentují deklarace, výrazy a další druhy top-level konstruktů (napadá Vás lepší překlad pro “declarations, statements, clauses, and expressions”?). V systému je každý druh syntax node reprezentován třídou dědící z bázové třídy SyntaxNode a množina těchto tříd není rozšiřitelná. Všechny druhy syntax node jsou neterminální symboly, což v teorii gramatik označuje uzly, které musí mít ještě další uzly pod sebou. Každý node má odkaz Parent na svého rodiče (null v případě kořene) a kolekci potomků. Taktéž má definované metody pro získání svých potomků podle jejich druhu (DescendantNodes, DescendantTokens, …).

Syntax token

Tokeny jsou v obrázku označeny zeleně. Reprezentují klíčová slova (int), identifikátory (a), literály (1 a 2) a interpunkci (; a + a =). Je trochu zavádějící napsat, že matematické operátory spadají do kategorie interpunkce, nicméně i podle jmen, které je reprezentují (o tom v sekci Kinds) tomu tak nasvědčuje. Tokeny jsou terminály gramatiky jazyka – nikdy nejsou rodičem jiného nodu nebo tokenu, ale jak lze na obrázku vidět, váže se na ně trivia. Zajímavostí tokenů je, že interně jsou všechny druhy reprezentovány jediným typem, SyntaxToken, což je z důvodu efektivnosti struct. Mezi tokeny se pak rozlišuje na základě proměnlivých vlastností.

Syntax trivia

Trivia je v obrázku označena bíle a šedivě. Reprezentuje bílé znaky (které nemusí být nutně obarvené bíle), komentáře a tzv. “preprocesor directives” (např. #PRAGMA). Tyto symboly význam kódu nemění, ale jak už jsem zmínil v první vlastnosti AST, je důležité si o nich udržovat přehled. Interně se trivia neuvažuje jako potomek tokenu (to by pak nebyl terminál), ale spíše jako jeho součást. Tedy každý token má kolekce LeadingTrivia a TrailingTrivia, což jsou předcházející symboly (bílé) a následující (šedivé). Systém je takový, že první token v dokumentu má přiřazené veškeré předcházející znaky a každý token má přiřazené následující znaky nacházející se na stejném řádku. Stejně jako v případě tokenů, veškerá trivia je reprezentována jediným hodnotovým typem SyntaxTrivia.

Kinds

Kind, neboli druh je z pohledu orientace asi nejvýznamnější vlastnost každého typu elementu stromu. Nabývá např. hodnoty VariableDeclaration pro node reprezentující deklaraci proměnné (v obrázku levý potomek kořene), nebo IntKeyword pro token reprezentující klíčové slovo zastupující typ Int32 a nakonec WhitespaceTrivia pro trivii zastupující mezeru. V kódu se s touto vlastností setkáte pod názvem RawKind, což je číslo, které je převoditelné do výčtového typu SyntaxKind, který je pro každý jazyk jiný.

Chyby

V první vlastnosti AST jsem zmínil, že strom počítá i s reprezentací chyb. Tedy lexikografických chyb, například chybějící symboly. Pokud nic nechybí, jen je to sémanticky špatně, pak se strom normálně postaví a až sémantická analýzy odhalí nedostatky. Pro ukázku schválně odmažu kousek kódu:

int a = 1 + 

a z toho vznikne následující strom.

AST error

Můžete si všimnout, že kompilátor zdárně doplnil chybějící středník na konci řádku a také ví, že nám chybí node pro literál obsahující nějaký token, ať už literál nebo identifikátor. Chyby jsou označeny černě.

V tomto článku jsme se podrobně věnovali syntaktickým stromům z pohledu Roslynu. Rozdělili jsme stromové elementy na tři druhy, řekli si k nim příklady a informace, které nám poslouží v dalších dílech série, které budou praktické. Nejdříve si ukážeme, jak Roslyn nainstalovat a zprovoznit ve Visual Studiu 2013 (pro starší verze není dostupný), jak vykreslovat syntaktické stromy pro libovolný zdrojový kód a pak už nám nic nebude bránit k tomu, abychom naprogramovali nějaké diagnostiky, opravy kódu, atd.

Zdroj: http://roslyn.codeplex.com/wikipage?title=Overview&referringTitle=Home

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

Ukázky kódu

Hezký článek, ale už se těším na nějakou ukázku kódu, například jak najít všechny třídy v otevřeném VS solution co něco společného implementují a dogenerovat k nim nějakou metodu jako partial třídu apod.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Výborně, děkuji za nápad, nějak jej pojmu a zrealizuji, až budeme u toho kódu. Předpokládám v přespříštím článku, takže cca za 2 týdny.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

SemicoloToken?

Co je to ten SemicoloToken v posledním diagramu?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Semi-colon v angličtině znamená středník. Je to doplněný chybějící středník na konci celého příkazu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback