Söz Dizim Ağacıyla Biraz Oynamaya Var Mısınız?

   Sizlerden aldığım geri bildirimler sonrasında Roslyn ile bizlere sunulan API’lerden Söz Dizim API’si ile ilgili örneklere devam etmenin faydalı olacağını görüyorum. Bu makalemde bir kodun oluşturduğumuz söz dizim ağacında dolaşarak bundan nasıl yeni bir ağaç oluşturabileceğimizi sizlerle paylaşacağım.

   Konuya yabancılık çekilmemesi adına son iki makalemde verdiğim örnek üzerinden devam edelim;

var sozDizimAgaci = SyntaxTree.ParseCompilationUnit(
@"using System;
namespace Com.Enterprisecoding.RoslynOrnegi {
    public class MerhabaDunya {
        public static void Mesaj() {
            Console.WriteLine(""Enterprisecoding'den Roslyn'e Merhaba"");
        }
    }
}");


var sozDizimAgaciKok = sozDizimAgaci.Root;

   Bu basit kod parçacığında string olarak verdiğimiz ifadeye ait söz dizim ağacını oluşturarak kök’ünü daha sonra kullanabilmek adına bir değişkene atamaktayız.

   Varsayalım ki çalışma zamanında okuyarak söz dizim ağacına ulaştığınız bir kod içerisindeki bir ya da bir grup alanı dinamik olarak değiştirip bu şekilde derlemek istiyorsunuz. Bunu yapmaktaki amacınız örneğin bir scripting uygulamasında güvenlik gerekçesi ile bazı kod parçacıklarına izin vermemek olabileceği gibi ekibinizin yazdığı kodları inceleyerek belirli kod desenlerini yenileri ile değiştirmek (refactor) ya da tespit edilen hataları kodları düzeltmekte olabilir. Konuyu basite indirgemek adına, bizim örneğimiz çok daha basit bir amaca hizmet edecek; konsola yazdırılan eksik ifadeyi günceli ile değiştirmek…

   Önceki yazılarımı takip ettiyseniz mutlaka biliyorsunuzdur, Roslyn ile üretilen söz dizim ağaçları salt okunurdur. Bu özellikleri sayesinde de paralelde güvenli olarak işlenebilmektedirler. Böylesi bir durumda söz dizim ağacı üzerinden doğrudan bir değişiklik yapılabilmesi mümkün değildir. bunun yerine mevcut ağaç baz alınarak istenilen değişikliklerle yenisi üretilmelidir. Şanslıyız ki böylesi bir kodu sıfırdan yazmamıza gerek yok; SyntaxExtensions static sınıfı altında bulanan ReplaceToken fonksiyonu bunu bizim adımıza yapacaktır. ReplaceToken fonksiyon bu işlemi yapabilmek için bizden sadece değiştirilecek eski ve yeni simgeleri isteyecektir. Bu durumda ilk yapmamız gereken değiştirmek istediğimiz ifadeye ait simgeye ulaşmak; ki bu Roslyn ile oldukça kolay bir iş haline geliyor;

var konsolCiktiSimgesi = sozDizimAgaciKok.GetFirstToken(p => p.Kind == SyntaxKind.StringLiteralToken);

   Tabi ki bu satırı yazarken çalışma-zamanında oluşacak olan söz dizim ağacını bildiğim ve StringLiteralToken türünden ilk simgenin benim aradığım simge olacağını göz önüne almalısınız. Kendi geliştirdiğiniz uygulamalarda aradığınız simgeye ulaşabilmek için daha karmaşık iş mantıkları kullanmanız gerekebilir.

   Aradığım simgeye ulaştıktan sonra ikinci adımda da bunun yerini alacak olan simgeyi oluşturmalıyım. Bunun için Syntax sınıfı yardımıyla StringLiteralExpression türünden bir LiteralExpression oluşturarak bu ifade yardımıyla nihai simgeme ulaşmalıyım. Bu işlem için gerekli kod parçacığı aşağıda bulunabilir;

var yeniKonsolCiktiSimgesi = Syntax.LiteralExpression(SyntaxKind.StringLiteralExpression,
            Syntax.Literal(
                text: @"""Enterprisecoding ve Roslyn'den herkese merhaba!""",
                value: "Enterprisecoding ve Roslyn'den herkese merhaba!")).Token;

   Elimde eski ve yeni simgeler olduktan sonrası ise oldukça kolay. Tek yapmam gereken bu iki simgeyi ve söz dizim ağacı kök’ünü kullanarak yeni bir söz dizim ağacı oluşturmak;

sozDizimAgaciKok = sozDizimAgaciKok.ReplaceToken(konsolCiktiSimgesi, yeniKonsolCiktiSimgesi);

   Bu kadar basit! Artık elimde güncellenmiş haliyle yeni söz dizim ağacım bulunmakta. Bunu kullanarak bir assembly üretebileceğim gibi yeni halini formatlayıp dosya sistemine yazmam da mümkün.

    Son olarak; yukarıda paylaştıklarımı bir araya getirecek olursak, elimizde aşağıdaki kod olacaktır;

var sozDizimAgaci = SyntaxTree.ParseCompilationUnit(
@"using System;
namespace Com.Enterprisecoding.RoslynOrnegi {
    public class MerhabaDunya {
        public static void Mesaj() {
            Console.WriteLine(""Enterprisecoding'den Roslyn'e Merhaba"");
        }
    }
}");


var sozDizimAgaciKok = sozDizimAgaci.Root;
var konsolCiktiSimgesi = sozDizimAgaciKok.GetFirstToken(p => p.Kind == SyntaxKind.StringLiteralToken);
            
var yeniKonsolCiktiSimgesi = Syntax.LiteralExpression(SyntaxKind.StringLiteralExpression,
            Syntax.Literal(
                text: @"""Enterprisecoding ve Roslyn'den herkese merhaba!""",
                value: "Enterprisecoding ve Roslyn'den herkese merhaba!")).Token;

sozDizimAgaciKok = sozDizimAgaciKok.ReplaceToken(konsolCiktiSimgesi, yeniKonsolCiktiSimgesi);

   Bu kod parçacığı örnek girdimizi aşağıdaki hale dönüştürecektir;

using System;
namespace Com.Enterprisecoding.RoslynOrnegi {
    public class MerhabaDunya {
        public static void Mesaj() {
            Console.WriteLine("Enterprisecoding ve Roslyn'den herkese merhaba!");
        }
    }
}

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir