Java'nın C'den daha kolay ayrıştırılmasını sağlayan nedir?


90

C ve C ++ dilbilgilerinin içeriğe duyarlı olduğu gerçeğini biliyorum ve özellikle C'de bir "lexer hack" e ihtiyacınız var. Öte yandan, Java'yı yalnızca İki dil arasındaki önemli benzerliğe rağmen, 2 adet ileriye dönük bakış.

Ayrıştırmayı daha uygun hale getirmek için C ile ilgili neyi değiştirmeniz gerekir?

Soruyorum çünkü C'nin bağlam duyarlılığına dair gördüğüm tüm örnekler teknik olarak izin verilebilir, ancak çok garip. Örneğin,

foo (a);

void işlevini foobağımsız değişkenle çağırıyor olabilir a. Veya abir tür nesnesi olarak bildiriliyor fooolabilir, ancak parantezlerden de kolayca kurtulabilirsiniz. Kısmen, bu tuhaflık, C dilbilgisi için "doğrudan bildirici" üretim kuralının hem işlevleri hem de değişkenleri bildirmenin ikili amacını yerine getirmesi nedeniyle ortaya çıkar .

Öte yandan, Java dilbilgisinin değişken bildirimi ve işlev bildirimi için ayrı üretim kuralları vardır. Eğer yazarsan

foo a;

o zaman bunun bir değişken bildirimi olduğunu bilirsiniz ve fooaçık bir şekilde bir tür adı olarak ayrıştırılabilir. Sınıf foomevcut kapsamda herhangi bir yerde tanımlanmadıysa bu geçerli kod olmayabilir , ancak bu, daha sonraki bir derleyici geçişinde gerçekleştirilebilecek anlamsal analiz için bir iştir.

Typedef nedeniyle C'nin ayrıştırılmasının zor olduğunu söylediğini gördüm, ancak kendi türlerinizi Java'da da bildirebilirsiniz. Ayrıca hangi C dilbilgisi kuralları direct_declaratorhatalı?


7
Harika soru. Muhtemelen çok geniş ya da temelde düşünceli olsa da.
asteri

37
Bu, ayrıştırıcılar hakkında geçerli bir sorudur ve bu konudaki tek geniş fikir veya fikir, son birkaç cümledir (muhtemelen kaldırılmalı veya değiştirilmelidir). Yakın oylarla çıkın.
R .. GitHub BUZA YARDIM ETMEYİ DURDUR

1
Soruyu buna göre düzenledim, geri bildirim için @R .. için teşekkürler.
korrok

3
Hemen hemen her (standart) bilgisayar dili içeriğe duyarlıdır ; tek tip bir değişken tanımlayamaz ve çoğu dili yanlış kullanamazsınız . Bu, " dilin tüm gramerlerinin " içeriğe duyarlı olmasından farklıdır ; ayrıştırıcı oluşturan çoğu kişi bağlamdan bağımsız (veya daha da kısıtlayıcı) bir ayrıştırıcı oluşturur ve ardından bağlamdan bağımsız özellikleri kontrol etmek için ayrıştırıcının dışındaki korsanları kullanır.
Ira Baxter

1
@IraBaxter Ben buna "hacks" demezdim. Problemi ikiye bölmek mantıklı görünüyor, çünkü bağlama duyarlı dilleri ayrıştırmak verimli bir şekilde yapılamaz (ve aslında bağlamdan bağımsız dilleri ayrıştırmak bile verimli değildir ve bu yüzden genellikle bağlamdan bağımsız alt kümeler ile sınırlandırıyoruz) . Bağlamdan bağımsız bir ayrıştırma + statik analiz, yalnızca AST üzerinden bağlama duyarlı özellikleri kontrol etmek için makul bir şeydir.
Bakuriu

Yanıtlar:


76

C ++ 'ın ayrıştırılması zorlaşıyor. Java'yı ayrıştırmak da zorlaşıyor.

C'nin (ve C ++) neden ayrıştırılmasının "zor" olduğunu tartışan bu SO yanıtına bakın . Kısa özet, C ve C ++ gramerlerinin doğası gereği belirsiz olduğudur; bunlar size birden fazla çözümleme sağlar ve belirsizlikleri çözmek için bağlam kullanmanız gerekir . İnsanlar daha sonra siz ayrıştırırken belirsizlikleri çözmeniz gerektiğini varsayma hatasını yaparlar; öyle değil, aşağıya bakın. Ayrıştırırken belirsizlikleri çözmekte ısrar ederseniz, ayrıştırıcınız daha karmaşık hale gelir ve inşa etmesi çok daha zor hale gelir; ama bu karmaşıklık kendi kendine verilen bir yaradır.

IIRC, Java 1.4'ün "açık" LALR (1) dilbilgisi belirsiz değildi, bu nedenle ayrıştırılması "kolay" idi. Modern Java'nın en azından uzun mesafeli yerel belirsizliklere sahip olmadığından pek emin değilim; "... >>" iki şablonu kapatır mı yoksa bir "sağa kaydırma operatörü" mü olduğuna karar verme sorunu her zaman vardır. Modern Java'nın artık LALR (1) ile ayrıştırılmadığından şüpheleniyorum .

Ancak, her iki dil için de güçlü ayrıştırıcılar (veya C ve C ++ ön uçlarının çoğunlukla yaptığı gibi zayıf ayrıştırıcılar ve bağlam toplama saldırıları) kullanarak ayrıştırma problemi aşılabilir. C ve C ++, bir önişlemciye sahip olmanın ek karmaşıklığına sahiptir; bunlar pratikte göründüklerinden daha karmaşıktır. Bir iddia, C ve C ++ ayrıştırıcılarının çok zor olduğu ve elle yazılmaları gerektiğidir. Doğru değil; Java ve C ++ ayrıştırıcılarını GLR ayrıştırıcı oluşturucularla gayet iyi bir şekilde oluşturabilirsiniz.

Ancak ayrıştırma, sorunun gerçekte olduğu yerde değildir.

Ayrıştırdıktan sonra, AST / ayrıştırma ağacı ile bir şeyler yapmak isteyeceksiniz. Pratikte, her tanımlayıcı için tanımının ne olduğunu ve nerede kullanıldığını bilmeniz gerekir ("ad ve tür çözünürlüğü", dikkatsizce, sembol tabloları oluşturma). Bu, kalıtım, arayüzler, aşırı yükleme ve şablonlarla birleştirilen ayrıştırıcıyı doğru bir şekilde elde etmekten çok daha fazla işe yarıyor ve tüm bunların anlamsallığının onlarca ila yüzlerce sayfaya yayılmış gayri resmi doğal dilde yazılmış olması gerçeğiyle karıştırılıyor. dil standardı. C ++ burada gerçekten kötü. Java 7 ve 8, bu açıdan oldukça kötü olmaya başlıyor. (Ve ihtiyacınız olan tek şey sembol tabloları değil; "Ayrıştırmadan Sonra Yaşam" üzerine daha uzun bir makale için biyografime bakın).

Çoğu insan saf ayrıştırma kısmıyla mücadele eder (çoğu zaman asla bitmez; gerçek diller için çalışan ayrıştırıcıların nasıl oluşturulacağına ilişkin birçok soru için SO'nun kendisini kontrol edin), böylece ayrıştırmadan sonra hayatı asla görmezler. Ve sonra neyin ayrıştırılmasının zor olduğu hakkında halk teoremleri alırız ve bu aşamadan sonra ne olacağı hakkında hiçbir sinyal almayız.

C ++ sözdizimini düzeltmek sizi hiçbir yere götürmez.

C ++ sözdizimini değiştirmeyle ilgili olarak: Herhangi bir C ++ dilbilgisindeki yerel ve gerçek belirsizliklerin çeşitliliği ile ilgilenmek için birçok yeri yamalamanız gerektiğini göreceksiniz. Israr ediyorsanız, aşağıdaki liste iyi bir başlangıç ​​noktası olabilir . C ++ standartları komitesi değilseniz bunu yapmanın bir anlamı olmadığını iddia ediyorum; Bunu yaptıysanız ve bunu kullanarak bir derleyici oluşturduysanız, aklı başında kimse onu kullanmaz. Ayrıştırıcı oluşturan adamların rahatlığı için mevcut C ++ uygulamalarına çok fazla yatırım yapıldı; ayrıca, acıları bitti ve mevcut ayrıştırıcılar iyi çalışıyor.

Kendi ayrıştırıcınızı yazmak isteyebilirsiniz. Tamam sorun değil; sadece topluluğun geri kalanının sizin için daha kolay hale getirmek için kullanmaları gereken dili değiştirmenize izin vermesini beklemeyin. Hepsi kendileri için daha kolay olmasını istiyor ve bu da dili belgelendiği ve uygulandığı şekliyle kullanmak.


İyi cevap. Ayrıca bu sorunlardan bazılarını çözmeye çalışan D ve C + 'ya da bakın. s / content /
contend

3
Daha önce Life After Parsing'i okudum ve gerçekten göz açıcı olduğunu gördüm; Anlamsal analizde (ad / tür çözümlemesi, ...) ayrıştırmada olduğundan çok daha fazla iş olduğunu bana açıkça gösterdi. Ben değilim değil herhangi bir dil sözdizimini değiştirmeye çalışıyor. Ben do özellikleri öncelikle sözdizimsel analiz yapmak hangi bir dil ve ardından semantik analizin ne olduğunu anlamak istiyorum. C böyle bir dil değildir (lexer hackine ihtiyaç duyar); Hep Java'nın olduğunu düşündüm ve nedenini bilmek istiyorum.
korrok

1
@Korrok: GLR ayrıştırıcıları ile Java / C ++ oluşturma hakkındaki cevabımı okuyun. Herhangi bir lexer hackine ihtiyacınız yok . Yani, yanlış ayrıştırma teknolojisini kullanan insanların zihninde ayrım var. ... Tam bir C ++ ön uç (özellikle de yaptığımız C ++ 14) oluşturmak Java8 yapmaktan daha zordur, ancak ikisi de zordur (çaba ve ayrıntılara dikkat etmek açısından) ve ayrıştırmak en kolay parçadır.
Ira Baxter

1
"Ayrıştırmadan Sonra Yaşam" konusunda hemfikirim: örneğin, C #'daki aşırı yük çözümü herhangi bir 3-SAT problemini kodlayabilir ve bu nedenle NP-zordur.
Jörg W Mittag

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.