C ++ bağlamdan bağımsız mı, bağlama duyarlı mı?


405

Sık sık C ++ 'ın bağlama duyarlı bir dil olduğunu iddia ediyorum. Aşağıdaki örneği alın:

a b(c);

Bu bir değişken tanımı mı, yoksa işlev bildirimi mi? Bu sembolün anlamına bağlıdır c. Eğer cbir olan değişken , daha sonra a b(c);adında bir değişken tanımlar bÇeşidi a. Doğrudan ile başlatılır c. Ancak cbir tür ise , o zaman a b(c);a balan cve döndüren adlı bir işlevi bildirir a.

Bağlamdan bağımsız dillerin tanımına bakarsanız, temel olarak tüm dilbilgisi kurallarının tam olarak bir terminal olmayan simgeden oluşan sol taraflara sahip olması gerektiğini söyleyecektir. Bağlama duyarlı dilbilgileri ise, sol tarafta terminal ve terminal olmayan simgelerin rastgele dizelerine izin verir.

"C ++ Programlama Dili" Ek A'ya göz atarken, sol tarafında terminal olmayan tek bir sembol dışında başka bir şey olan tek bir dilbilgisi kuralı bulamadım. Bu, C ++ 'ın bağlamdan bağımsız olduğu anlamına gelir. (Elbette, bağlamdan bağımsız her dil, bağlamdan bağımsız dillerin içeriğe duyarlı dillerin bir alt kümesini oluşturması anlamında içeriğe duyarlıdır, ancak mesele bu değildir.)

Peki, C ++ bağlamdan bağımsız mı, bağlama duyarlı mı?


12
@CarlNorum Lütfen sol tarafta terminal olmayan tek bir sembol içermeyen tek bir C ++ dilbilgisi kuralı gösterin ve size hemen inanacağım.
fredoverflow

9
IIUC, bağlam duyarlılığı için çizgiyi çizdiğiniz yere bağlıdır. İnsanların statik olarak yazılan programlama dillerinin neredeyse hepsinin bağlama duyarlı olduğunu, CFG ayrıştırma araçlarıyla kendileri için pratik bir derleyici oluşturamadığınız için değil, bu tür uygulamaların bazı geçersiz programları ve ancak daha sonra yazım denetimi sırasında reddedilir. Bu nedenle, kötü yazılan programların dilde (CS anlamında, yani bir dizi dizede) olmadığını düşünüyorsanız, ayrıştırıcının kabul etmesi gerekir, C ++ 'dan daha fazla dil içeriğe duyarlıdır.

6
@DeadMG: Hayır, yanılıyorsun. Biçimsel dil teorisinde "ayrıştırma" veya "anlambilim" yoktur, sadece bir dizeler dizisi olan "dil" vardır.
13:26, jpalecek

27
"Bağlamdan bağımsız dilbilgisi" tanımınıza şu ana kadar cevap verilmemiştir. Bana göre, bu soruya verilen doğru cevap ya ek A'da tanımınıza uymayan bir üretimden bahsediyor ya da tanımınızın yanlış ya da yetersiz olduğunu gösteriyor. Olduğun yerde kal!
Yörüngedeki Hafiflik Yarışları

8
Bkz Is D'nin gramer gerçekten bağlamdan-bağımsız? . Aslında, buradaki herkesin bu soruyu ve cevaplarını okuması gerektiğini düşünüyorum !
Yörüngedeki Hafiflik Yarışları

Yanıtlar:


341

Aşağıda, C ++ 'ın ayrıştırılmasının neden (muhtemelen) en sevdiğim gösterimi (muhtemelen) Turing-complete , çünkü yalnızca belirli bir tam sayı asal ise sözdizimsel olarak doğru bir program gösteriyor.

Bu yüzden iddia ediyorum C ++ 'ın ne bağlamsız ne de bağlam duyarlı .

Herhangi bir prodüksiyonun her iki tarafında rastgele sembol dizilerine izin verirseniz, Chomsky hiyerarşisinde bağlama duyarlı bir dilbilgisinden daha güçlü olan bir Type-0 dilbilgisi ("kısıtlamasız") üretirsiniz ; kısıtsız dilbilgisi Turing-complete'tur. Bağlama duyarlı (Tip-1) dilbilgisi, bir prodüksiyonun sol tarafında birden çok bağlam sembolüne izin verir, ancak aynı bağlam, prodüksiyonun sağ tarafında da görünmelidir (dolayısıyla "bağlama duyarlı" adı). [1] Bağlama duyarlı dilbilgisi doğrusal bağlı Turing makinelerine eşdeğerdir .

Örnek programda, birincil hesaplama doğrusal sınırlı bir Turing makinesi tarafından gerçekleştirilebilir, bu nedenle Turing denkliğini tam olarak kanıtlamaz, ancak önemli olan ayrıştırıcının sözdizimsel analiz yapmak için hesaplamayı yapması gerektiğidir. Bir şablon örneği olarak ifade edilebilir herhangi bir hesaplama olabilir ve C ++ şablon örneklemesinin Turing-complete olduğuna inanmak için her neden olabilir. Bkz. Örneğin, Todd L. Veldhuizen'in 2003 tarihli makalesi .

Ne olursa olsun, C ++ bir bilgisayar tarafından ayrıştırılabilir, bu nedenle bir Turing makinesi tarafından kesinlikle ayrıştırılabilir. Sonuç olarak, sınırsız bir dilbilgisi bunu tanıyabilir. Aslında böyle bir gramer yazmak pratik olmaz, bu yüzden standart bunu yapmaya çalışmaz. (Aşağıya bakınız.)

Belirli ifadelerin "belirsizliği" ile ilgili sorun çoğunlukla kırmızı bir ringa balığıdır. Başlangıç ​​olarak, belirsizlik bir dil değil, belirli bir dilbilgisinin bir özelliğidir. Bir dilin belirgin gramerleri olmadığı kanıtlanabilse bile, bağlamsız bir gramer tarafından tanınabilirse, bağlam içermez. Benzer şekilde, bağlamdan bağımsız bir dilbilgisi tarafından tanınamıyor ancak bağlama duyarlı bir dilbilgisi tarafından tanınabiliyorsa, içeriğe duyarlıdır. Belirsizlik ilgili değildir.

Ancak, auto b = foo<IsPrime<234799>>::typen<1>();aşağıdaki programdaki 21 numaralı satır (ör. ) Gibi her durumda, ifadeler hiç belirsiz değildir; bağlama göre farklı şekilde ayrıştırılırlar. Sorunun en basit ifadesinde, belirli tanımlayıcıların sözdizimsel kategorisi, nasıl bildirildiklerine bağlıdır (örneğin, türler ve işlevler), bu da resmi dilin iki keyfi uzunluklu dizenin aynı program aynıdır (beyan ve kullanım). Bu, aynı kelimenin ardışık iki tam kopyasını tanıyan dilbilgisi olan "kopya" dilbilgisi ile modellenebilir. Pompalama lemmasıyla kanıtlanması kolaydırbu dilin bağlamdan bağımsız olmadığı. Bu dil için bağlama duyarlı bir dilbilgisi mümkündür ve bu sorunun cevabında bir Tip-0 dilbilgisi sağlanmıştır: /math/163830/context-sensitive-grammar-for-the- kopyalama dili .

Eğer biri C ++ 'ı ayrıştırmak için bağlama duyarlı (veya kısıtlamasız) bir dilbilgisi yazmaya çalışacak olsaydı, büyük olasılıkla evreni karalamalarla doldururdu. C ++ 'ı ayrıştırmak için bir Turing makinesi yazmak da aynı derecede imkansız bir girişim olacaktır. Bir C ++ programı yazmak bile zordur ve bildiğim kadarıyla hiçbirinin doğru olmadığı kanıtlanmıştır. Bu nedenle standart tam bir resmi dilbilgisi sağlamaya çalışmaz ve ayrıştırma kurallarından bazılarını teknik İngilizce olarak yazmayı seçer.

C ++ standardında biçimsel bir dilbilgisi gibi görünen şey, C ++ dilinin sözdiziminin tam biçimsel tanımı değildir. Önişlemeden sonra dilin tam biçimsel tanımı bile değildir, biçimselleştirilmesi daha kolay olabilir. (Ancak bu dil olmazdı: standart tarafından tanımlanan C ++ dili ön işlemciyi içerir ve ön işlemcinin çalışması algoritmik olarak tanımlanır, çünkü herhangi bir dilbilgisel formalizmde tanımlanması son derece zor olacaktır. bir kereden fazla uygulanması gereken kurallar da dahil olmak üzere sözcüksel ayrışmanın açıklandığı standardın.)

Çeşitli gramerler (sözcüksel analiz için üst üste binen iki dilbilgisi, biri önişlemeden önce gerçekleşir, diğeri gerekirse daha sonra artı "sözdizimsel" dilbilgisi) bu önemli notla birlikte (vurgu eklenmiştir):

C ++ sözdiziminin bu özeti, kavramaya yardımcı olmak amacıyla hazırlanmıştır. Dilin kesin bir ifadesi değildir . Özellikle, burada açıklanan dilbilgisi , geçerli C ++ yapılarının bir üst kümesini kabul eder . İfadeleri deklarasyonlardan ayırmak için ayrımcılık kuralları (6.8, 7.1, 10.2) uygulanmalıdır. Ayrıca, sözdizimsel olarak geçerli ancak anlamsız yapıları ayıklamak için erişim kontrolü, belirsizlik ve tür kuralları kullanılmalıdır.

Son olarak, burada vaat edilen program. 21 numaralı hat, yalnızca N girişi IsPrime<N>asal olduğunda sözdizimsel olarak doğrudur . Aksi takdirde, typenbir şablon değil bir tamsayıdır, sözdizimsel olarak geçerli bir ifade olmadığı için sözdizimsel olarak yanlış olduğu typen<1>()gibi ayrıştırılır .(typen<1)>()()

template<bool V> struct answer { answer(int) {} bool operator()(){return V;}};

template<bool no, bool yes, int f, int p> struct IsPrimeHelper
  : IsPrimeHelper<p % f == 0, f * f >= p, f + 2, p> {};
template<bool yes, int f, int p> struct IsPrimeHelper<true, yes, f, p> { using type = answer<false>; };
template<int f, int p> struct IsPrimeHelper<false, true, f, p> { using type = answer<true>; };

template<int I> using IsPrime = typename IsPrimeHelper<!(I&1), false, 3, I>::type;
template<int I>
struct X { static const int i = I; int a[i]; }; 

template<typename A> struct foo;
template<>struct foo<answer<true>>{
  template<int I> using typen = X<I>;
};
template<> struct foo<answer<false>>{
  static const int typen = 0;
};

int main() {
  auto b = foo<IsPrime<234799>>::typen<1>(); // Syntax error if not prime
  return 0;
}

[1] Daha teknik olarak ifade etmek gerekirse, bağlama duyarlı bir gramerdeki her üretim şu biçimde olmalıdır:

αAβ → αγβ

burada Aterminal olmayan bir ve α, βmuhtemelen gramer sembollerin boş dizileridir ve γboş olmayan bir dizidir. (Dilbilgisi sembolleri terminal veya terminal olmayan olabilir).

Bu A → γsadece bağlamda olduğu gibi okunabilir [α, β]. Bağlamdan bağımsız (Tip 2) bir dilbilgisinde αve βboş olmalıdır.

Her bir üretimin formda olması gereken "tekdüze" kısıtlama ile gramerleri de kısıtlayabileceğiniz ortaya çıkıyor:

α → βnerede |α| ≥ |β| > 0  ( |α|"uzunluğu" anlamına gelir α)

Monotonik dilbilgileri tarafından tanınan dil kümesinin, içeriğe duyarlı dilbilgileri tarafından tanınan dil kümesiyle tam olarak aynı olduğunu kanıtlamak mümkündür ve çoğu zaman kanıtları monotonik dilbilgisi üzerine dayandırmak daha kolaydır. Sonuç olarak, "tekdüze" anlamına geldiği gibi "bağlama duyarlı" ifadesini görmek oldukça yaygındır.


27
Bu yüzden sadece içeriğe duyarlı olmakla kalmaz, aynı zamanda Turing-complete olan şablonlarda ifade edebileceğiniz herhangi bir içeriğe bağımlı hale getirilebilir.
Köpek yavrusu

7
@mehrdad, OP bağlam duyarlı dilbilgisi değil, "bağlama duyarlı dil" diyor. Belirsizlik bir dilin değil bir dilbilgisinin özelliğidir. Dil gerçekten bağlama duyarlıdır, ancak belirli bir dilbilgisi belirsiz olduğu için değildir.
rici

2
Örneğimin belirsiz olmadığını unutmayın . Geçerli bir programın açık bir ifadesidir. 21. satırdaki değeri değiştirirseniz, hatalı biçimlendirilebilir. Ancak her iki durumda da belirsiz değildir.
rici

5
Bir şüphem var: Gösterdiğiniz gibi, şablon değerlendirmesinin sonucu, iyi biçimlendirilmiş ve kötü biçimlendirilmiş bir program arasında fark yaratabilir. Şablon değerlendirme tamamlandı. Peki dilde (C ++) bir dize olup olmadığını doğru bir şekilde saptamak turing-completeeness gerektirir mi? Söylediğiniz gibi, bağlama duyarlı bir dil "sadece" AFAIK'ı tamamlamayan "lineer sınırlı bir otomat" dır. Yoksa argümanınız C ++ standardının şablon değerlendirme derinliği dahil bazı şeylere koyduğu sınırları kullanıyor mu?

4
@AntonGolov: Bu örneğin orijinal versiyonum bunu yaptı ( basit bir tane için 0içine girerek başarabilirsiniz ()), ama bence bu daha ilginç, çünkü bunu anlamak için şablon örneğine ihtiyacınız olduğunu gösteriyor dize sözdizimsel olarak doğru bir C ++ programıdır. Her iki dal da derlenirse, farkın "anlamsal" olduğu iddiasına karşı çıkmak için daha çok çalışmam gerekirdi. Merakla, sık sık "sözdizimsel" tanımlamak için meydan okudum, ancak hiç kimse "sözdizimsel olduğunu sanmıyorum şeyler" dışında "anlamsal" bir tanım
sunmadı

115

İlk olarak, haklı olarak dilbilgisi böylece, C ++ standart sonunda dilbilgisi hiçbir bağlam duyarlı kurallar vardır gözlenen olan içeriğe ücretsiz.

Ancak, bu dilbilgisi C ++ dilini tam olarak tanımlamaz, çünkü C ++ olmayan programlar üretir.

int m() { m++; }

veya

typedef static int int;

"İyi biçimlendirilmiş C ++ programları kümesi" olarak tanımlanan C ++ dili bağlamdan bağımsız değildir (yalnızca talep edilmesi gereken değişkenlerin bunu yaptığını göstermek mümkündür). Turing-complete programlarını teorik olarak şablonlara yazabileceğiniz ve sonuçlarına göre kötü biçimlendirilmiş bir program yapabileceğiniz göz önüne alındığında, içeriğe duyarlı bile değildir.

Şimdi, (cahil) insanlar (genellikle dil teorisyenleri değil, ayrıştırıcı tasarımcılar) tipik olarak aşağıdaki anlamlardan bazılarında "bağlamsız" kullanırlar

  • belirsiz
  • Bison ile ayrıştırılamıyor
  • LL (k), LR (k), LALR (k) veya ayrıştırıcı tarafından tanımlanan dil sınıfı ne olursa olsun

Standardın arkasındaki dilbilgisi bu kategorileri karşılamaz (yani belirsiz (LL (k) değil ...), bu nedenle C ++ dilbilgisi onlar için "bağlamsız" dır. Ve bir anlamda, çalışan bir C ++ ayrıştırıcısı üretmek çok zor.

Burada kullanılan özelliklerin yalnızca bağlamdan bağımsız dillerle zayıf bir şekilde bağlantılı olduğunu unutmayın - belirsizliğin bağlam duyarlılığıyla hiçbir ilgisi yoktur (aslında, bağlama duyarlı kurallar genellikle üretimleri belirsizleştirmeye yardımcı olur), diğer ikisi sadece bağlamın alt kümeleridir -ücretsiz diller. Bağlamdan bağımsız dillerin ayrıştırılması doğrusal bir süreç değildir (deterministik olanların ayrıştırılmasına rağmen).


7
ambiguity doesn't have anything to do with context-sensitivityBu da benim sezgimdi, bu yüzden (a) katılıyorum ve (b) yapamadığımı açıklayan birini gördüğüme sevindim. Temeli olan tüm argümanları diskalifiye etti a b(c);ve önceliği bağlam-duyarlılık iddialarının belirsizliğe bağlı olduğu iddialarının “sıkça duyulduğu” orijinal soruyu kısmen tatmin ettiğine inanıyorum ... özellikle dilbilgisi için aslında hiçbir belirsizlik olmadığında MVP.
Yörüngedeki Hafiflik Yarışları

6
@KonradRudolph: Standardın söylediği gibi "Birden fazla şablon içerebilecek özyinelemeli örneklemelerin toplam derinliği sınırını belirten, uygulama tanımlı bir miktar vardır. Örneklemede sonsuz özyinelemenin sonucu tanımsızdır." (14.7.1p15) Her geçerli c ++ programını anlamak için bir uygulamanın gerekli olmadığını, çok büyük özyineleme derinliğine sahip programların geçersiz olduğunu belirtmiyorum. Geçersiz olarak işaretlenenler yalnızca sonsuz yineleme derinliği olanlardır.
rici

3
@KonradRudolph: Bunun "genel referans" olduğuna itiraz ediyorum. Oldukça karmaşık bir makaleyi okuduğum ve bu küçük gerçeği ortaya çıkarmak için yeterince anlamadığım gerçeği bunu göstermek için yeterli olmalıdır. "Bilgisayarlar genellikle elektrik kullanır" veya "bitler doğru veya yanlış olabilir" gibi bir şey söylememişsinizdir.
Yörüngede Hafiflik Yarışları

3
Bu gerçek gerçekten çok biliniyorsa, ona bir referans bulmanın, bir kişinin sağlanması gerekip gerekmediği konusunda uzun süre tartışmaktan daha kolay olacağını düşünüyorum. Yapıcıdan bahsetmiyorum bile.
Samuel Edwin Ward

5
Anlayabildiğim kadarıyla, @Konrad "Bağlama duyarlı Turing tamamlandı ile eşdeğerdir" dediğinde yanılmıştı. (en azından "Tamamlandı" ile "Yinelemeli olarak numaralandırılabilir" olarak belirtiyorsa) ve o zamandan beri bu hatayı tanıyamadı. Burada yer alan uygun
kümelenme

61

Evet. Aşağıdaki ifade, tür çözümlenmiş bağlama bağlı olarak farklı bir işlem sırasına sahiptir :

Düzenleme: Gerçek işlem sırası değiştiğinde, dekore etmeden önce dekore edilmemiş bir AST için ayrıştırılan bir "normal" derleyici kullanmak inanılmaz zorlaştırır (tip bilgileri yaymak). Bahsedilen diğer bağlama duyarlı şeyler buna kıyasla "oldukça kolaydır" (şablon değerlendirmesinin hiç de kolay olmadığını).

#if FIRST_MEANING
   template<bool B>
   class foo
   { };
#else
   static const int foo = 0;
   static const int bar = 15;
#endif

Bunu takiben:

static int foobar( foo < 2 ? 1 < 1 : 0 > & bar );

Hangi tip tanımların kapsamda olduğunu hatırlayarak bu problem C için olduğu gibi çözülemez?
Blaisorblade

1
@Blaisorblade: Bir derleyiciyi "temiz" hale getirmenin bir yolu, girişlerden bir ayrıştırma ağacı ve ardından tür analizini yapan bir adım oluşturmak gibi görevleri zincirdeki bağımsız adımlara ayırmaktır. C ++ sizi 1) bu adımları bir veya 2 olarak birleştirmeye zorlar) belgeyi her iki / tüm olası yorumlara göre ayrıştırır ve tür çözümleme aşamalarının doğru yorumlamaya daraltmasına izin verir.
Sam Harwell

@ 280Z28: kabul etti, ancak C için de durum böyle; Bu soruya iyi bir yanıtın neden C ++ 'ın C'den daha kötü olduğunu göstermesi gerektiğini düşünüyorum. Burada bağlantılı doktora tezi bunu yapıyor: stackoverflow.com/a/243447/53974
Blaisorblade

26

Sorunuzu cevaplamak için iki farklı soruyu ayırt etmeniz gerekir.

  1. Hemen hemen her programlama dilinin sözdizimi bağlamdan bağımsızdır. Tipik olarak, genişletilmiş Backus-Naur formu veya bağlamsız gram olarak verilir.

  2. Bununla birlikte, bir program programlama dili tarafından tanımlanan bağlamsız gram ile uyumlu olsa bile, mutlaka geçerli bir program değildir. Geçerli bir program olabilmek için bir programın yerine getirmesi gereken bağlamsız birçok özellik vardır. Örneğin, bu tür en basit özellik değişkenlerin kapsamıdır.

Sonuç olarak, C ++ 'ın bağlamdan bağımsız olup olmadığı sorduğunuz soruya bağlıdır.


5
Programlama diliniz için bir CFG elde etmek için genellikle "sadece sözdizimi" düzeyini beklediğinizden daha düşük bir seviyeye yerleştirmeniz gerektiğine dikkat etmek ilginçtir. Örneğin C'yi ele alalım. C'de basit bir değişken bildirimi için dilbilgisi kuralının olacağını düşünebilirsiniz VARDECL : TYPENAME IDENTIFIER, ancak bunu yapamazsınız , çünkü tür adlarını CF düzeyindeki diğer tanımlayıcılardan ayırt edemezsiniz. Başka bir örnek: CF düzeyinde, a*bdeğişken bildirimi ( bişaretçi türü a) veya çarpma olarak ayrıştırmaya karar veremezsiniz .
LaC

2
@LaC: Evet, bunu belirttiğin için teşekkürler! Bu arada, sadece sözdizimi için daha sık kullanılan bir teknik terim olduğundan eminim . Doğru terim olan var mı?
Dan

4
@Dan: Bahsettiğiniz şey, bağlamsız bir gramer tarafından verilen dilin bir yaklaşımıdır. Tabii ki böyle bir yaklaşım, tanım gereği coontext içermez. "Sözdiziminin" programlama dilleri tartışılırken sıklıkla kullanıldığı duygusudur.
reinierpost

13

Bjarne Stroustrup'un C ++ Tasarım ve Evrimi'ne göz atmak isteyebilirsiniz . İçinde, C ++ 'ın erken bir sürümünü ayrıştırmak için yacc (veya benzeri) kullanmaya çalışan sorunlarını tarif ediyor ve bunun yerine özyinelemeli iniş kullanmasını diliyor.


Vay be ... Teşekkürler. Herhangi bir yapay dili ayrıştırmak için bir CFG'den daha güçlü bir şey kullanmayı düşünmenin gerçekten mantıklı olup olmadığını merak ediyorum .
Dervin Thunk

C ++ 'ın nedenlerini anlamak için harika bir kitap. Ben ve Lippman's C ++ nasıl çalıştığını anlamak için C ++ Nesne Modeli içinde öneririz. Her ikisi de biraz tarihli olmasına rağmen onlar hala iyi bir okuma vardır.
Matt Price

"Meta-S" Quinn Tyler Jackson'ın içeriğe duyarlı bir ayrıştırma motorudur. Kullanmadım, ama etkileyici bir hikaye anlatıyor. Comp.compilers'daki yorumlarına bir göz atın ve bkz. Rnaparse.com/MetaS%20defined.htm
Ira Baxter

@IraBaxter: x-ref'niz bugün MIA - ve yazılıma sağlam referanslar zor görünüyor (Google arama, 'site: rnaparse.com meta-s' veya 'quinn jackson meta- s '; bitler ve parçalar vardır, ancak meta-s.com bilgilendirici olmayan bir web sitesine yönlendirir).
Jonathan Leffler

@Jonathan: Bir süredir şikayetinizi fark ettiniz. Bağlantının neden kötü olduğunu bilmiyorum, yazarken iyi olduğunu düşündüm. Quinn, derleyicilerde oldukça aktifti. Google lapa lapa oluyor gibi görünüyor, bulabildiğim tek şey bu: groups.google.com/group/comp.compilers/browse_thread/thread/… IIRC, yeniden pazarlamak için Hawaii'deki bazı kıyafetler için MetaS haklarını imzaladı. Bunun teknik olarak ne kadar garip olduğu düşünüldüğünde, IMHO bu onun ölüm emrini imzalıyor. Çok zekice bir plan gibiydi.
Ira Baxter

12

Evet C ++ içeriğe duyarlıdır, içeriğe duyarlıdır. Sözdizimi ağacını, bağlamsız bir ayrıştırıcı kullanarak dosyada ayrıştırarak oluşturamazsınız, çünkü bazı durumlarda karar vermek için önceki bilgiden sembolü bilmeniz gerekir (örn. Ayrıştırma sırasında bir sembol tablosu oluşturmak).

İlk örnek:

A*B;

Bu bir çarpma ifadesi mi?

VEYA

Bu bir Bdeğişken göstergesinin tür belirteci olması mı?A mi?

A bir değişkense, bir ifadedir, A türü ise, bir işaretçi bildirimidir.

İkinci örnek:

A B(bar);

Bu bir argüman alan bir işlev prototipi midir? bar tür mi?

VEYA

Bu beyan değişken mi BÇeşidi Aile A yapıcısını çağırırbar bir başlatıcısı olarak sabiti?

Tekrar bilmek gerekir barSembol tablosundan bir değişken mi yoksa bir tür olduğunu .

Üçüncü örnek:

class Foo
{
public:
    void fn(){x*y;}
    int x, y;
};

Bu, ayrıştırma sırasında sembol tablosu oluştururken yardımcı olmaz çünkü x ve y bildirimi işlev tanımından sonra gelir. Bu nedenle, ilk önce sınıf tanımını taramanız ve ikinci bir geçişte yöntem tanımlarına bakmanız gerekir; x * y'ye bir işaretçi bildirimi veya herhangi bir şey değil, bir ifade olduğunu söylemek için.


1
A B();işlev tanımında bile bir işlev bildirimidir. En
sinir bozucu

Msgstr "Sözdizimi ağacını sadece dosya üzerinden ayrıştırarak oluşturamazsın" FALSE. Cevabımı gör.
Ira Baxter

10

C ++, GLR ayrıştırıcı ile ayrıştırılır. Kaynak kodunu Ayrıştırma sırasında Yani, ayrıştırıcı olabilir belirsizlik karşılaşacak ama devam etmek ve hangisinin kullanılacağına dilbilgisi kuralı karar vermelidir sonra .

ayrıca bak,

C ++ neden bir LR (1) ayrıştırıcısıyla ayrıştırılamıyor?


Bu bağlamdan bağımsız dilbilgisi hatırla olamaz tanımlamak TÜM bir programlama dili sözdizimi kuralları. Örneğin, Özellik dilbilgisi bir ifade türünün geçerliliğini kontrol etmek için kullanılır.

int x;
x = 9 + 1.0;

Sen olamaz serbest içerik ile aşağıdaki kural açıklamaktadır: atama Sağ Yan Sol Eli tarafının aynı tipte olmalıdır.


4
Çoğu C ++ ayrıştırıcısı GLR ayrıştırma teknolojisini kullanmaz. GCC bunu yapmaz. Bazıları yapar. Bunu yapmak için semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html adresine bakın .
Ira Baxter

10

"Bağlama duyarlı" biçiminin resmi tanımı ile "bağlama duyarlı" ın gayri resmi kullanımı arasında biraz karışıklık olduğunu hissediyorum. İlki iyi tanımlanmış bir anlama sahiptir. İkincisi, "girdiyi ayrıştırmak için içeriğe ihtiyacınız var" demek için kullanılır.

Burada da sorulur: Bağlam duyarlılığı ve Belirsizlik .

Bağlamdan bağımsız bir dilbilgisi:

<a> ::= <b> | <c>
<b> ::= "x"
<c> ::= "x"

Bu belirsizdir, bu nedenle "x" girişini ayrıştırmak için bir bağlam gerekir (ya da belirsizlikle yaşayın veya "Uyarı: E8271 - Giriş 115 satırında belirsiz"). Ama kesinlikle bağlama duyarlı bir dilbilgisi değil.


Bir prodüksiyonun sol tarafında birden fazla sembol bulunması bu sorunu nasıl çözer? Bu cevabın soruyu cevapladığını sanmıyorum.
user541686

1
Cevabım ilk cümleye yanıt olarak: "Sık sık C ++ 'ın bağlama duyarlı bir dil olduğunu iddia ediyorum." Bu hak talepleri gayri resmi olarak "bağlama duyarlı" ifadesini kullanıyorsa, sorun yoktur. C ++ resmen bağlama duyarlı olduğunu düşünmüyorum.
Omri Barel

Ben C ++ düşünüyorum olduğunu resmen içeriğe duyarlı, ama ben yaşıyorum sorun bir içeriğe duyarlı dilbilgisi olur CFG daha ++ C ayrıştırma herhangi daha başarılı olurdu anlamak kalmamasıdır.
user541686

6

Algol benzeri bir dil bağlam içermez, çünkü tanımlayıcıların türlerine göre görünebilecek ifadeleri ve ifadeleri sınırlayan kurallara sahiptirler ve bildirim ile kullanım arasında oluşabilecek ifadelerin sayısında sınır yoktur.

Genel çözüm, geçerli programların bir üst kümesini gerçekten kabul eden ve bağlama duyarlı bölümleri geçici olarak yerleştiren bağlamsız bir ayrıştırıcı yazmaktır kurallara ekli "anlamsal" koduna yerleştiren .

C ++, Turing-complete şablon sistemi sayesinde bunun ötesine geçer. Bkz. Yığın Taşması Sorusu 794015 .




5

a b(c);İki geçerli ayrıştırma bildirimi ve değişkeni olduğu gibi içeriğe duyarlıdır . "Eğer cbir türse" dediğinizde , bu bağlam, tam orada ve C ++ 'nin buna nasıl duyarlı olduğunu tam olarak açıkladınız. "Ne nedir?c ?" bunu açık bir şekilde çözümleyemediniz.

Burada bağlam, belirteç seçiminde ifade edilir; ayrıştırıcı, bir türü adlandırıyorsa tanımlayıcıyı tür adı belirteci olarak okur. Bu en basit çözümdür ve içeriğe duyarlı olmanın karmaşıklığından (bu durumda) kaçınır.

Düzenleme: Elbette, bağlam duyarlılığı ile ilgili daha fazla sorun var, sadece gösterdiğiniz konuya odaklandım. Şablonlar bunun için özellikle kötü.


1
Ayrıca a<b<c>>d, değil mi? (
Örneğiniz aslında

Bu daha çok bir lexing sorunu, sanırım. Ama kesinlikle aynı kategoride, evet.
Köpek yavrusu

2
Soru soran, C'den daha içeriğe nasıl duyarlı olduğunu sormaz , yalnızca içeriğe duyarlı olduğunu gösterir.
Köpek yavrusu

Yani .. bir C ++ fazla içeriğe duyarlı C'nin?
Kerrek SB

2
@DeadMG Soruyu cevapladığınızı sanmıyorum (ben de olduğumu sanmıyorum). Bir prodüksiyonun sol tarafında terminal bulunması bu sorunu nasıl çözüyor?
user541686

5

C ++ standardındaki yapımlar bağlamsız yazılmıştır, ancak hepimizin bildiği gibi dili tam olarak tanımlamamaktadır. Çoğu insanın mevcut dilde belirsizlik olarak gördüklerinden bazıları, bağlama duyarlı bir dilbilgisi ile açık bir şekilde çözülebilir.

En belirgin Örneğin, çoğu Vexing Ayrıştırma düşünelim: int f(X);. Eğer Xbir değer, bu tanımlar olup file başlatılır Bir değişken olarak X. Eğer Xbir tür, bir tanımlayan ftip tek bir parametre alan bir fonksiyonu olarakX .

Buna gramer açısından baktığımızda bunu şöyle görebiliriz:

A variable_decl ::= <type> <identifier> '(' initializer ')' ';'

B function_decl ::= <type> <identifier> '(' param_decl ')' ';'

A ::= [declaration of X as value]
B ::= [declaration of X as type]

Tabii ki, tamamen doğru olmak için, diğer türlerin bildirimlerine müdahale etme olasılığını hesaba katmak için bazı ekstra "şeyler" eklememiz gerekir. veya bu siparişle ilgili bir şey).

Bu yine de tipik bir CSG'den oldukça farklıdır (veya en azından hatırladıklarım). Bu, oluşturulmakta olan bir sembol tablosuna bağlıdır - yalnızca bundan Xönce gelen bir ifade türüne değil, aynı zamanda doğru sembol / tanımlayıcı için doğru ifade türüne özel olarak bir tür veya değer olarak tanıyan bölüm .

Bu nedenle, emin olmak için bazı şeyler yapmak zorundayım, ama acil tahminim, bunun en azından terim normal olarak kullanıldığı gibi gerçekten bir CSG olarak nitelendirilmemesi.


(Bağlamdan bağımsız) prodüksiyonlar, bağlamsız ayrıştırma motoru tarafından ayrıştırılabilmesi için en sinir bozucu ayrışmayı yeterince iyi tanımlar. Bu, ayrıştırma tamamlanana kadar birden fazla yorumdan hangisinin geçerli olduğuna karar verme sorununu geciktirir, ancak bu, ayrıştırıcı ve ad çözümleyicinin mühendisliğini kolaylaştırır, çünkü bunlar geleneksel C ++ ayrıştırıcılarında olduğu gibi modüler değil. Çoğu sinir bozucu ayrıştırma için AST'ye bakın: stackoverflow.com/questions/17388771/…
Ira Baxter

5

Bağlamdan bağımsız dilbilgisinin en basit örneği şablonları içeren ifadeleri ayrıştırmayı içerir.

a<b<c>()

Bu,

template
   |
   a < expr > ()
        |
        <
      /   \
     b     c

Veya

 expr
   |
   <
 /   \
a   template
     |
     b < expr > ()
          |
          c

İki AST sadece 'a' bildirimi - 'a' bir şablonsa eski AST veya değilse ikincisi - incelenerek ortadan kaldırılabilir.


C ++ 11'in ikinci yorumu zorunlu kıldığına inanıyorum ve öncekine kaydolmak için parens eklemeniz gerekiyor.
Joseph Garvin

1
@JosephGarvin, hayır. C ++ <, olabiliyorsa köşeli ayraç olması gerektiğini belirtir (örn., Şablonu adlandıran bir tanımlayıcıyı izler). C ++ 11 , kullanımın makul olması durumunda >ilk karakterin >>yakın parantez olarak yorumlanması gereksinimini ekledi . Bu , şablonun a<b>c>nerede ayrıştırıldığını etkiler aancak üzerinde etkisi yoktur a<b<c>.
rici

@aaron: bu nasıl daha basit a();(hangisi ya expr.callda expr.type.conv)?
rici

@rici: Hata! Asimetrik olduğunu fark etmedim.
Joseph Garvin

5
Belirsizlik veya bağlam duyarlılığı tanımlıyor musunuz ?
corazza

4

C ++ şablonlarının Turing Powerful olduğu gösterilmiştir. Resmi bir referans olmasa da, işte bu açıdan bakabileceğiniz bir yer:

http://cpptruths.blogspot.com/2005/11/c-templates-are-turing-complete.html

Bir tahminde bulunacağım. CFG'ler, bir ağaç geçişinde veya azaltma olaylarında çeşitli TP mekanizmaları ile birlikte - bu başka bir hikaye. Genel anlamda, Durma Sorunu nedeniyle, doğru / yanlış olarak gösterilemeyen ancak yine de doğru / yanlış olan bazı C ++ programları vardır.

{PS- Meta-S'nin yazarı olarak (yukarıdaki birkaç kişi tarafından belirtilmiştir) - Thothic'in ne geçersiz olduğunu hem de ücretsiz olarak mevcut olmadığını söyleyebilirim. Belki de yanıtımın bu sürümünü silmeyeceğim ya da -3'e oy vermeyeceğim şeklinde ifade ettim.}



2

Açıkçası, soruyu kelimesi kelimesine alırsanız, tanımlayıcıları olan neredeyse tüm diller bağlama duyarlıdır.

Tanımlayıcı kullanımının bir kısmını doğru bir şekilde yapabilmek için bir tanımlayıcının bir tür adı (bir sınıf adı, typedef tarafından tanıtılan bir ad, bir typename şablon parametresi), bir şablon adı veya başka bir ad olup olmadığını bilmek gerekir. Örneğin:

x = (name)(expression);

tür bir if nameise tür adı ve fonksiyon çağrısı isename . Başka bir durum, değişken tanımını ve işlev bildirimini ayırt etmenin mümkün olmadığı "en sinir bozucu ayrıştırma" dır (işlev bildirimi olduğunu söyleyen bir kural vardır).

Bu zorluk, bağımlı isimlerin ihtiyacını typenameve templateisimlerini getirdi . C ++ 'ın geri kalanı bildiğim kadarıyla içeriğe duyarlı değildir (yani bunun için bağlamsız bir gramer yazmak mümkündür).


2

Meta-S ", Quinn Tyler Jackson'ın içeriğe duyarlı bir ayrıştırma motorudur. Kullanmadım, ancak etkileyici bir hikaye anlatıyor. Comp.compilers'daki yorumlarına göz atın ve rnaparse.com/MetaS%20defined.htm - Ira Baxter 25 Temmuz, 10:42

Doğru bağlantı motorları ayrıştırmaktır

Meta-S, Thothic adında geçersiz bir şirketin mülkiyetiydi. Ben ilgilenen herkese Meta-S ücretsiz bir kopyasını gönderebilirsiniz ve ben araştırma ayrıştırma rna kullandım. Örnek klasörlerde yer alan "sözde dilbilgisi" nin biyoenformatik olmayan, amatör programcı tarafından yazıldığını ve temelde çalışmadığını lütfen unutmayın. Dilbilgilerim farklı bir yaklaşım benimsiyor ve gayet iyi çalışıyor.


Bu aslında ilginç bir bulgu.
Dervin Thunk

0

Burada büyük bir sorun, "bağlamsız" ve "bağlama duyarlı" terimlerinin bilgisayar bilimlerinde biraz sezgisel olmamasıdır. C ++ için, bağlam duyarlılığı belirsizliğe çok benziyor, ancak genel durumda bu doğru olmayabilir.

C / ++ 'da, bir if ifadesine yalnızca işlev gövdesi içinde izin verilir. Bu onu içeriğe duyarlı yapıyor gibi görünüyor, değil mi? Hayır. Bağlamdan bağımsız gramerler, bazı kod satırlarını çıkarabileceğiniz ve geçerli olup olmadığını belirleyebileceğiniz özelliğe ihtiyaç duymaz. Aslında bağlamdan bağımsız demek değildir. Gerçekten sadece, neye benzediğiyle ilgili bir şey ima eden bir etiket.

Şimdi, bir işlev gövdesi içindeki bir ifade, derhal dilbilgisel ataların dışında tanımlanan bir şeye bağlı olarak farklı şekilde ayrıştırılırsa (örneğin, bir tanımlayıcının bir türü veya değişkeni tanımlayıp tanımlamadığı), a * b;gerçekte bağlama duyarlıdır. Burada gerçek bir belirsizlik yoktur; abir tür ve bir çarpma ise bir işaretçi bildirimi olarak ayrıştırılır .

Bağlama duyarlı olmak zorunlu olarak "ayrıştırılması zor" anlamına gelmez. C aslında o kadar da zor değil çünkü rezil a * b;"belirsizlik" içeren bir sembol tablosu ile çözülebilirtypedef daha önce karşılaşılanları . C ++ zaman zaman olduğu gibi bu durumu çözmek için (Turing Complete olduğu kanıtlanmış) herhangi bir rastgele şablon örneği gerektirmez. C ++ ile aynı bağlam duyarlılığına sahip olmasına rağmen, sınırlı bir süre içinde derlenmeyecek bir C programı yazmak aslında mümkün değildir.

Python (ve boşluklara duyarlı diğer diller) de içeriğe bağımlıdır, çünkü lexer'da girinti ve adanmış jetonlar oluşturmak için durum gerektirir, ancak bu tipik bir LL-1 dilbilgisinden ayrıştırmayı zorlaştırmaz. Aslında, Python'un neden bu kadar bilgilendirici olmayan sözdizimi hata mesajlarına sahip olduğunun bir parçası olan bir ayrıştırıcı üreteci kullanıyor. Burada ayrıca a * b;, "belirsiz" dilbilgisi olmayan (ilk paragrafta belirtildiği gibi) bağlama duyarlı bir dilin somut bir örneğini veren Python'daki sorun gibi bir "belirsizlik" olmadığını belirtmek de önemlidir .


-4

Bu cevap, C ++ 'ın bağlamdan bağımsız olmadığını söylüyor ... ayrıştırılamadığı (yanıtlayan tarafından değil) bir anlamı var ve yanıt, belirli bir sabit bir program değilse geçersiz bir C ++ programı üreten zor bir kod örneği sunuyor asal sayı.

Diğerlerinin de gözlemlediği gibi, dilin içeriğe duyarlı / özgür olup olmadığı sorusu, belirli bir gramer hakkındaki aynı sorudan farklıdır.

Ayrıştırılabilirlik ile ilgili soruyu ayarlamak için, C ++ için, mevcut bir GLR ile ayrıştırılarak kaynak metnin bağlamsız ayrıştırması için bir AST üretmek için kullanılabilecek bağlamsız gramerler olduğuna dair ampirik kanıtlar sunuyorum. Açık bir dilbilgisi tarafından yönlendirilen ayrıştırıcı tabanlı araç.

Evet, "çok fazla kabul ederek" başarılı olur; kabul ettiği her şey geçerli bir C ++ programı değildir, bu yüzden ek kontrollerle (tip kontrolleri) takip edilir. Ve evet, tür denetleyicisi hesaplanabilirlik sorunlarıyla karşılaşabilir. Pratikte araçların bu sorunu yoktur; insanlar böyle programlar yazsaydı, hiçbiri derlenmezdi. (Bence standart aslında bir şablonu açmak için yapabileceğiniz hesaplama miktarına bir sınır koyuyor, bu yüzden aslında hesaplama aslında sonlu ama muhtemelen oldukça büyük).

Ne demek istiyorsan, kaynak programın geçerli C ++ kaynak programları kümesinin bir üyesi olup olmadığını belirleyin , o zaman sorunun çok daha zor olduğunu kabul edeceğim. Ama sorun bu ayrıştırma değil .

Araç, ayrıştırmayı ayrıştırılan programın tür denetlemesinden izole ederek bu sorunu giderir. (Bağlamın yokluğunda çoklu yorumların olduğu yerlerde , ayrıştırma ağacında birkaç olası ayrıştırma içeren bir belirsizlik düğümü kaydeder ; tür denetimi hangisinin doğru olduğuna karar verir ve geçersiz alt ağaçları ortadan kaldırır). Aşağıdaki örnekte (kısmi) bir ayrıştırma ağacı görebilirsiniz; tüm ağaç SO cevabına sığmayacak kadar büyüktür. 234797 veya 234799 değeri kullanılırsa ayrıştırma ağacı elde edeceğinizi unutmayın.

Aracın ad / tip çözücüsünü 234799 orijinal değeri ile AST üzerinden çalıştırmak başarılı olur. 234797 değeriyle ad çözümleyicisi "beklendiği gibi" hata iletisi ile başarısız olur, "typen bir tür değildir." ve dolayısıyla bu sürüm geçerli bir C ++ programı değildir.

967 tree nodes in tree.
15 ambiguity nodes in tree.
(translation_unit@Cpp~GCC5=2#6b11a20^0 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
 (declaration_seq@Cpp~GCC5=1021#6b06640^1#6b11a20:1 {10} Line 1 Column 1 File C:/temp/prime_with_templates.cpp
  (pp_declaration_seq@Cpp~GCC5=1022#6b049a0^1#6b06640:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b04980^1#6b049a0:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b04960^1#6b04980:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2082#6afbde0^1#6b04960:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter@Cpp~GCC5=2085#6afbd80^1#6afbde0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6afbd40^1#6afbd80:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6afb880^1#6afbd40:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6afb840^1#6afb880:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6afb7e0^1#6afb840:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1138#6afb7a0^1#6afb7e0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6afb7e0
   |   | )decl_specifier#6afb840
   |   |)basic_decl_specifier_seq#6afb880
   |   |(ptr_declarator@Cpp~GCC5=1417#6afbc40^1#6afbd40:2 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6afbba0^1#6afbc40:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6afbb80^1#6afbba0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6afbaa0^1#6afbb80:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6afb9c0^1#6afbaa0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afb780^1#6afb9c0:1[`V'] Line 1 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6afb9c0
   |   |   )id_expression#6afbaa0
   |   |  )declarator_id#6afbb80
   |   | )noptr_declarator#6afbba0
   |   |)ptr_declarator#6afbc40
   |   )parameter_declaration#6afbd40
   |  )template_parameter#6afbd80
   | )template_parameter_list#6afbde0
   | (declaration@Cpp~GCC5=1033#6b04940^1#6b04960:2 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |  (block_declaration@Cpp~GCC5=1050#6b04920^1#6b04940:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   (simple_declaration@Cpp~GCC5=1060#6b04900^1#6b04920:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b048e0^1#6b04900:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b048c0^1#6b048e0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |  (type_specifier@Cpp~GCC5=1110#6b048a0^1#6b048c0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   (class_specifier@Cpp~GCC5=1761#6b04880^1#6b048a0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   |(class_head@Cpp~GCC5=1763#6afb980^1#6b04880:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   | (class_key@Cpp~GCC5=1791#6afbca0^1#6afb980:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp)class_key
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afbcc0^1#6afb980:2[`answer'] Line 1 Column 25 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | (optional_base_clause@Cpp~GCC5=1872#6afba60^1#6afb980:3 Line 1 Column 32 File C:/temp/prime_with_templates.cpp)optional_base_clause
   |   |   |)class_head#6afb980
   |   |   |(member_specification@Cpp~GCC5=1794#6b042e0^1#6b04880:2 {2} Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b04060^1#6b042e0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04040^1#6b04060:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04020^1#6b04040:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1673#6afbec0^1#6b04020:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6afbfe0^1#6afbec0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6afbf80^1#6afbfe0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6afbf60^1#6afbf80:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6afbea0^1#6afbf60:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6afbb40^1#6afbea0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6afbc80^1#6afbb40:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6afbc20^1#6afbc80:1[`answer'] Line 1 Column 34 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |  )unqualified_id#6afbc80
   |   |   |   |   | )id_expression#6afbb40
   |   |   |   |   |)declarator_id#6afbea0
   |   |   |   |   )noptr_declarator#6afbf60
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1559#6afbd00^1#6afbf80:2 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(pp_parameter_declaration_list@Cpp~GCC5=1570#6afb940^1#6afbd00:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pp_parameter_declaration_seq@Cpp~GCC5=1574#6afb800^1#6afb940:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (parameter_declaration@Cpp~GCC5=1610#6afb9a0^1#6afb800:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6afbf40^1#6afb9a0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(decl_specifier@Cpp~GCC5=1073#6afbfa0^1#6afbf40:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6afbfc0^1#6afbfa0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (simple_type_specifier@Cpp~GCC5=1140#6afb860^1#6afbfc0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   |   | )trailing_type_specifier#6afbfc0
   |   |   |   |   |   |)decl_specifier#6afbfa0
   |   |   |   |   |   )basic_decl_specifier_seq#6afbf40
   |   |   |   |   |  )parameter_declaration#6afb9a0
   |   |   |   |   | )pp_parameter_declaration_seq#6afb800
   |   |   |   |   |)pp_parameter_declaration_list#6afb940
   |   |   |   |   )parameter_declaration_clause#6afbd00
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6afbce0^1#6afbf80:3 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6afbf80
   |   |   |   | )ptr_declarator#6afbfe0
   |   |   |   |)function_head#6afbec0
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04000^1#6b04020:2 Line 1 Column 46 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=888#6afbee0^1#6b04000:1 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)compound_statement
   |   |   |   |)function_body#6b04000
   |   |   |   )function_definition#6b04020
   |   |   |  )member_declaration#6b04040
   |   |   | )member_declaration_or_access_specifier#6b04060
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b042c0^1#6b042e0:2 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04820^1#6b042c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04280^1#6b04820:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1674#6b04220^1#6b04280:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b040e0^1#6b04220:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (decl_specifier@Cpp~GCC5=1073#6b040c0^1#6b040e0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b040a0^1#6b040c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(simple_type_specifier@Cpp~GCC5=1138#6b04080^1#6b040a0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   )trailing_type_specifier#6b040a0
   |   |   |   |  )decl_specifier#6b040c0
   |   |   |   | )basic_decl_specifier_seq#6b040e0
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6b04200^1#6b04220:2 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6b041e0^1#6b04200:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6b041a0^1#6b041e0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6b04180^1#6b041a0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b04160^1#6b04180:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=320#6b04140^1#6b04160:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (operator_function_id@Cpp~GCC5=2027#6b04120^1#6b04140:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(operator@Cpp~GCC5=2070#6b04100^1#6b04120:1 Line 1 Column 62 File C:/temp/prime_with_templates.cpp)operator
   |   |   |   |   |   )operator_function_id#6b04120
   |   |   |   |   |  )unqualified_id#6b04140
   |   |   |   |   | )id_expression#6b04160
   |   |   |   |   |)declarator_id#6b04180
   |   |   |   |   )noptr_declarator#6b041a0
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1558#6afba40^1#6b041e0:2 Line 1 Column 65 File C:/temp/prime_with_templates.cpp)parameter_declaration_clause
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6b041c0^1#6b041e0:3 Line 1 Column 66 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6b041e0
   |   |   |   | )ptr_declarator#6b04200
   |   |   |   |)function_head#6b04220
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04300^1#6b04280:2 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=889#6b04760^1#6b04300:1 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (pp_statement_seq@Cpp~GCC5=894#6b04780^1#6b04760:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (statement@Cpp~GCC5=857#6b04440^1#6b04780:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(jump_statement@Cpp~GCC5=1011#6afba80^1#6b04440:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pm_expression@Cpp~GCC5=551#6b04380^1#6afba80:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (cast_expression@Cpp~GCC5=543#6b04360^1#6b04380:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (unary_expression@Cpp~GCC5=465#6b04340^1#6b04360:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(primary_expression@Cpp~GCC5=307#6b04320^1#6b04340:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b042a0^1#6b04320:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04260^1#6b042a0:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04240^1#6b04260:1[`V'] Line 1 Column 74 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |   |  )unqualified_id#6b04260
   |   |   |   |   |   | )id_expression#6b042a0
   |   |   |   |   |   |)primary_expression#6b04320
   |   |   |   |   |   )unary_expression#6b04340
   |   |   |   |   |  )cast_expression#6b04360
   |   |   |   |   | )pm_expression#6b04380
   |   |   |   |   |)jump_statement#6afba80
   |   |   |   |   )statement#6b04440
   |   |   |   |  )pp_statement_seq#6b04780
   |   |   |   | )compound_statement#6b04760
   |   |   |   |)function_body#6b04300
   |   |   |   )function_definition#6b04280
   |   |   |  )member_declaration#6b04820
   |   |   | )member_declaration_or_access_specifier#6b042c0
   |   |   |)member_specification#6b042e0
   |   |   )class_specifier#6b04880
   |   |  )type_specifier#6b048a0
   |   | )decl_specifier#6b048c0
   |   |)basic_decl_specifier_seq#6b048e0
   |   )simple_declaration#6b04900
   |  )block_declaration#6b04920
   | )declaration#6b04940
   |)template_declaration#6b04960
   )declaration#6b04980
  )pp_declaration_seq#6b049a0
  (pp_declaration_seq@Cpp~GCC5=1022#6b06620^1#6b06640:2 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b06600^1#6b06620:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b065e0^1#6b06600:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2083#6b05460^1#6b065e0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter_list@Cpp~GCC5=2083#6b05140^1#6b05460:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   (template_parameter_list@Cpp~GCC5=2083#6b04ee0^1#6b05140:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(template_parameter_list@Cpp~GCC5=2082#6b04cc0^1#6b04ee0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (template_parameter@Cpp~GCC5=2085#6b04ca0^1#6b04cc0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (parameter_declaration@Cpp~GCC5=1611#6b04c80^1#6b04ca0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04a40^1#6b04c80:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |(decl_specifier@Cpp~GCC5=1073#6b04a20^1#6b04a40:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6b04a00^1#6b04a20:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |  (simple_type_specifier@Cpp~GCC5=1138#6b049e0^1#6b04a00:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   | )trailing_type_specifier#6b04a00
   |   |   |)decl_specifier#6b04a20
   |   |   )basic_decl_specifier_seq#6b04a40
   |   |   (ptr_declarator@Cpp~GCC5=1417#6b04c40^1#6b04c80:2 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(noptr_declarator@Cpp~GCC5=1421#6b04be0^1#6b04c40:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (declarator_id@Cpp~GCC5=1487#6b04bc0^1#6b04be0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |  (id_expression@Cpp~GCC5=317#6b04b60^1#6b04bc0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   (unqualified_id@Cpp~GCC5=319#6b04ac0^1#6b04b60:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   |(IDENTIFIER@Cpp~GCC5=3368#6b049c0^1#6b04ac0:1[`no'] Line 3 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   )unqualified_id#6b04ac0
   |   |   |  )id_expression#6b04b60
   |   |   | )declarator_id#6b04bc0
   |   |   |)noptr_declarator#6b04be0
   |   |   )ptr_declarator#6b04c40
   |   |  )parameter_declaration#6b04c80
   |   | )template_parameter#6b04ca0
   |   |)template_parameter_list#6b04cc0
   |   |(template_parameter@Cpp~GCC5=2085#6b04ec0^1#6b04ee0:2 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   | (parameter_declaration@Cpp~GCC5=1611#6b04ea0^1#6b04ec0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |  (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04b40^1#6b04ea0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   (decl_specifier@Cpp~GCC5=1073#6b04ba0^1#6b04b40:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   |(trailing_type_specifier@Cpp~GCC5=1118#6b04c60^1#6b04ba0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   | (simple_type_specifier@Cpp~GCC5=1138#6b04580^1#6b04c60:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |)trailing_type_specifier#6b04c60
   |   |   )decl_specifier#6b04ba0
   |   |  )basic_decl_specifier_seq#6b04b40
   |   |  (ptr_declarator@Cpp~GCC5=1417#6b04e60^1#6b04ea0:2 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   (noptr_declarator@Cpp~GCC5=1421#6b04e40^1#6b04e60:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |(declarator_id@Cpp~GCC5=1487#6b04de0^1#6b04e40:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   | (id_expression@Cpp~GCC5=317#6b04d80^1#6b04de0:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04ce0^1#6b04d80:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04560^1#6b04ce0:1[`yes'] Line 3 Column 24 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |  )unqualified_id#6b04ce0
   |   |   | )id_expression#6b04d80
   |   |   |)declarator_id#6b04de0
   |   |   )noptr_declarator#6b04e40
   |   |  )ptr_declarator#6b04e60
   |   | )parameter_declaration#6b04ea0
   |   |)template_parameter#6b04ec0
   |   )template_parameter_list#6b04ee0
   |   (template_parameter@Cpp~GCC5=2085#6b05120^1#6b05140:2 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |(parameter_declaration@Cpp~GCC5=1611#6b05100^1#6b05120:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04d20^1#6b05100:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |  (decl_specifier@Cpp~GCC5=1073#6b04dc0^1#6b04d20:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b04e80^1#6b04dc0:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   |(simple_type_specifier@Cpp~GCC5=1140#6b046e0^1#6b04e80:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   )trailing_type_specifier#6b04e80
   |   |  )decl_specifier#6b04dc0
   |   | )basic_decl_specifier_seq#6b04d20
   |   | (ptr_declarator@Cpp~GCC5=1417#6b05080^1#6b05100:2 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |  (noptr_declarator@Cpp~GCC5=1421#6b05020^1#6b05080:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   (declarator_id@Cpp~GCC5=1487#6b05000^1#6b05020:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |(id_expression@Cpp~GCC5=317#6b04fa0^1#6b05000:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   | (unqualified_id@Cpp~GCC5=319#6b04f00^1#6b04fa0:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |  (IDENTIFIER@Cpp~GCC5=3368#6b046c0^1#6b04f00:1[`f'] Line 3 Column 33 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | )unqualified_id#6b04f00
   |   |   |)id_expression#6b04fa0
   |   |   )declarator_id#6b05000
   |   |  )noptr_declarator#6b05020
   |   | )ptr_declarator#6b05080
   |   |)parameter_declaration#6b05100
   |   )template_parameter#6b05120
   |  )template_parameter_list#6b05140
   |  (template_parameter@Cpp~GCC5=2085#6b05440^1#6b05460:2 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6b05420^1#6b05440:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b05160^1#6b05420:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b04fe0^1#6b05160:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6b050e0^1#6b04fe0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1140#6b050c0^1#6b050e0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6b050e0
   |   | )decl_specifier#6b04fe0
   |   |)basic_decl_specifier_seq#6b05160
   |   |(ptr_declarator@Cpp~GCC5=1417#6b053e0^1#6b05420:2 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6b053c0^1#6b053e0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6b05360^1#6b053c0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6b05280^1#6b05360:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6b051a0^1#6b05280:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6b046a0^1#6b051a0:1[`p'] Line 3 Column 40 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6b051a0
   |   |   )id_expression#6b05280
   |   |  )declarator_id#6b05360
   |   | )noptr_declarator#6b053c0
   |   |)ptr_declarator#6b053e0
   |   )parameter_declaration#6b05420
   |  )template_parameter#6b05440
   | )template_parameter_list#6b05460

Değişken bildirimi mi yoksa çarpma mı olduğunu belirleme tür denetleme özelliği değildir. Ayrıca o kendi kendini tanıtan şeylerin cevabını da silmek zorunda kaldım ... yine.
Köpek yavrusu

@Puppy: ne sevdiğini söyleyebilirsin, ama araç böyle çalışır. Araç adının silinmesi muhtemelen insanların araç adının ne olduğunu sormasını sağlayacaktır.
Ira Baxter

Soru aracın çalışmasını istemediğinden, aracın bu şekilde çalışıp çalışmadığı önemsizdir. Ayrıca, bunun gerçekleşmesini güvenle bekleyebileceğimizi düşünüyorum.
Köpek yavrusu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.