Ayrıştırıcı jeneratör kullanmalı mıyım veya kendi özel lexer ve ayrıştırıcı kodumu atayım mı?


Yanıtlar:


78

Gerçekten üç seçenek var, üçü de farklı durumlarda tercih ediliyor.

Seçenek 1: ayrıştırma jeneratörleri veya 'bazı dilleri ayrıştırmanız gerekir ve sadece çalışmasını istiyorsanız, kahretsin'

Diyelim, ŞİMDİ bazı eski veri formatları için bir ayrıştırıcı oluşturmanız isteniyor. Ya da ayrıştırıcınızın hızlı olması gerekir. Veya ayrıştırıcınızın kolayca bakımı yapılabilir olması gerekir.

Bu gibi durumlarda, muhtemelen bir ayrıştırıcı jeneratörü kullanmaktan en iyisini alırsınız. Ayrıntılarla uğraşmak zorunda değilsiniz, doğru çalışması için çok sayıda karmaşık kod almak zorunda değilsiniz, sadece girişin uyması gereken gramer bilgisini yazıyorsunuz, biraz kullanım kodu ve presto: anında ayrıştırıcı yazıyorsunuz.

Avantajları açıktır:

  • Özellikle giriş formatı çok tuhaf değilse (özellikle 2. seçenek daha iyi olurdu), bir spesifikasyon yazmak oldukça kolaydır.
  • Kolayca anlaşılabilen, bakımı kolay bir eserle bitiyorsunuz: gramer tanımı genellikle koddan çok daha doğal.
  • İyi çözümleyici üreticiler tarafından oluşturulan çözümleyiciler genellikle elle yazılmış kodlardan çok daha hızlıdır. El yazısı kodu daha hızlı olabilir , ancak yalnızca kendi öğenizi biliyorsanız - bu, en çok kullanılan derleyicilerin el yazısı özyinelemeli bir çözümleyici kullanmasının nedeni budur.

Ayrıştırma jeneratörleriyle ilgili dikkat etmeniz gereken bir şey var: bazen dilbilginizi reddedebilir. Farklı ayrıştırıcı türlerine ve sizi nasıl ısırmalarına genel bir bakış için buradan başlamak isteyebilirsiniz . Burada , birçok uygulamaya ve kabul ettikleri gramer türlerine genel bir bakış bulabilirsiniz.

Seçenek 2: elle yazılmış ayrıştırıcılar veya 'kendi ayrıştırıcınızı oluşturmak istiyorsunuz ve kullanıcı dostu olmayı umuyorsunuz'

Ayrıştırıcı jeneratörler güzel, ancak çok kullanıcı (son kullanıcı, siz değil) dostu değiller. Genelde iyi hata mesajları veremezsiniz ya da hata kurtarma sağlayamazsınız. Belki de diliniz çok gariptir ve ayrıştırıcılar dilbilginizi reddeder veya jeneratörün size verdiğinden daha fazla kontrole ihtiyacınız var.

Bu durumlarda, elle yazılmış özyinelemeli iniş çözümleyici kullanmak muhtemelen en iyisidir. Doğru yaparken karmaşık olabilir, ayrıştırıcınız üzerinde tam kontrol sahibi olursunuz, böylece hata mesajları ve hatta hata kurtarma gibi ayrıştırıcı üreticilerle yapamayacağınız her türlü güzel şeyi yapabilirsiniz (tüm noktalı virgülleri C # dosyasından kaldırmayı deneyin). : C # derleyicisi şikayet eder, ancak noktalı virgüllerin varlığına bakmaksızın bu hataların çoğunu yine de tespit eder).

Elle yazılmış ayrıştırıcılar, ayrıştırıcının kalitesinin yeterince yüksek olduğu varsayılarak, genellikle oluşturulanlardan daha iyi performans gösterir. Öte yandan, iyi bir çözümleyici yazmayı başaramazsanız - genellikle deneyim (bilgi eksikliği) veya tasarım eksikliği nedeniyle - o zaman performans genellikle yavaştır. Lexers için ise tam tersi doğrudur: genellikle üretilen lexers tablo aramalarını kullanır ve bunları (çoğu) elle yazılmışlardan daha hızlı yapar.

Eğitim-bilge, kendi çözümleyicinizi yazmak size bir jeneratör kullanmaktan daha fazlasını öğretecektir. Sonuçta daha karmaşık kodlar yazmanız gerekir, ayrıca bir dili nasıl ayrıştırdığınızı tam olarak anlamanız gerekir. Öte yandan, kendi dilinizi nasıl yaratacağınızı öğrenmek istiyorsanız (bu nedenle, dil tasarımında deneyim edinin), seçenek 1 veya seçenek 3 tercih edilir: eğer bir dil geliştiriyorsanız, muhtemelen çok şey değişecek, ve seçenek 1 ve 3 size bununla daha kolay zaman verir.

Seçenek 3: el ile yazılmış ayrıştırma jeneratörlerini veya 'bu projeden çok şey öğrenmeye çalışıyorsunuz ve çok fazla tekrar kullanabileceğiniz şık bir kod parçasına son vermeyeceksiniz'

Şu anda yürüdüğüm yol bu: kendi ayrıştırıcı jeneratörünüzü yazıyorsunuz . Çok önemsiz olsa da, bunu yapmak size muhtemelen en iyisini öğretecektir.

Bunun gibi bir projeyi yapmanın neyi içerdiği hakkında bir fikir vermek için size kendi gelişimimden bahsedeceğim.

Lexer üreteci

Önce kendi lexer jeneratörümü yarattım. Genellikle kodun nasıl kullanılacağından başlayarak yazılımlar tasarlarım, bu yüzden kodumu nasıl kullanmak istediğimi düşündüm ve bu kod parçasını yazdım (C # dilinde):

Lexer<CalculatorToken> calculatorLexer = new Lexer<CalculatorToken>(
    new List<StringTokenPair>()
    { // This is just like a lex specification:
      //                    regex   token
        new StringTokenPair("\\+",  CalculatorToken.Plus),
        new StringTokenPair("\\*",  CalculatorToken.Times),
        new StringTokenPair("(",    CalculatorToken.LeftParenthesis),
        new StringTokenPair(")",    CalculatorToken.RightParenthesis),
        new StringTokenPair("\\d+", CalculatorToken.Number),
    });

foreach (CalculatorToken token in
             calculatorLexer.GetLexer(new StringReader("15+4*10")))
{ // This will iterate over all tokens in the string.
    Console.WriteLine(token.Value);
}

// Prints:
// 15
// +
// 4
// *
// 10

Girdi dizgisi-belirteç çiftleri, bir aritmetik yığının fikirlerini kullanarak temsil ettikleri normal ifadeleri tarif eden karşılık gelen özyinelemeli bir yapıya dönüştürülür. Bu daha sonra bir NFA'ya (nondeterministik sonlu otomaton) dönüştürülür ve bu sırada bir DFA'ya (deterministik sonlu otomaton) dönüştürülür. Daha sonra dizeleri DFA ile eşleştirebilirsiniz.

Bu şekilde, lexers'ın tam olarak nasıl çalıştığı hakkında iyi bir fikir edinebilirsiniz. Ek olarak, doğru şekilde yaparsanız, lexer jeneratörünüzün sonuçları kabaca profesyonel uygulamalar kadar hızlı olabilir. Ayrıca, seçenek 2 ile karşılaştırıldığında hiçbir ifade kaybettiniz ve seçenek 1 ile karşılaştırıldığında hiçbir ifade kaybettiniz.

Lexer jeneratörümü 1600'den fazla kod satırında uyguladım. Bu kod yukarıdakileri çalıştırır, ancak programı her başlattığınızda anında lexer'ı oluşturur: Bir noktada diske yazmak için kod ekleyeceğim.

Kendi lexer yazmayı bilmek istiyorsanız, bu başlamak için iyi bir yerdir.

Ayrıştırıcı jeneratör

Ardından ayrıştırma jeneratörünüzü yazın. Farklı ayrıştırıcı türlerine genel bir bakış için buraya tekrar atıfta bulunuyorum - kural olarak, ne kadar çok ayrıştırırlarsa, yavaşlarlar.

Hız benim için bir sorun değil, bir Earley ayrıştırıcı uygulamayı seçtim. Bir Earley ayrıştırıcısının gelişmiş uygulamalarının diğer ayrıştırıcı türlerinden iki kat daha yavaş olduğu gösterilmiştir .

Bu hız çarpması karşılığında, herhangi bir gramer, hatta belirsiz olanları bile ayrıştırma olanağına sahip olursunuz . Bu, ayrıştırıcınızın içinde herhangi bir özyinelemeye sahip olup olmadığı ya da vardiya azaltıcı bir çatışmanın ne olduğu konusunda endişelenmenize gerek olmadığı anlamına gelir. Sonuç olarak hangi ayrıştırma ağacının bir önemi yoksa, 1 + 2 + 3'ü (1 + 2) +3 olarak ya da 1 olarak ayrıştırmanızın önemi olmadığı gibi, belirsiz gramerleri kullanarak gramerleri daha kolay tanımlayabilirsiniz. + (2 + 3).

Ayrıştırma jeneratörümü kullanan bir kod parçası şöyle görünebilir:

Lexer<CalculatorToken> calculatorLexer = new Lexer<CalculatorToken>(
    new List<StringTokenPair>()
    {
        new StringTokenPair("\\+",  CalculatorToken.Plus),
        new StringTokenPair("\\*",  CalculatorToken.Times),
        new StringTokenPair("(",    CalculatorToken.LeftParenthesis),
        new StringTokenPair(")",    CalculatorToken.RightParenthesis),
        new StringTokenPair("\\d+", CalculatorToken.Number),
    });

Grammar<IntWrapper, CalculatorToken> calculator
    = new Grammar<IntWrapper, CalculatorToken>(calculatorLexer);

// Declaring the nonterminals.
INonTerminal<IntWrapper> expr = calculator.AddNonTerminal<IntWrapper>();
INonTerminal<IntWrapper> term = calculator.AddNonTerminal<IntWrapper>();
INonTerminal<IntWrapper> factor = calculator.AddNonTerminal<IntWrapper>();

// expr will be our head nonterminal.
calculator.SetAsMainNonTerminal(expr);

// expr: term | expr Plus term;
calculator.AddProduction(expr, term.GetDefault());
calculator.AddProduction(expr,
                         expr.GetDefault(),
                         CalculatorToken.Plus.GetDefault(),
                         term.AddCode(
                         (x, r) => { x.Result.Value += r.Value; return x; }
                         ));

// term: factor | term Times factor;
calculator.AddProduction(term, factor.GetDefault());
calculator.AddProduction(term,
                         term.GetDefault(),
                         CalculatorToken.Times.GetDefault(),
                         factor.AddCode
                         (
                         (x, r) => { x.Result.Value *= r.Value; return x; }
                         ));

// factor: LeftParenthesis expr RightParenthesis
//         | Number;
calculator.AddProduction(factor,
                         CalculatorToken.LeftParenthesis.GetDefault(),
                         expr.GetDefault(),
                         CalculatorToken.RightParenthesis.GetDefault());
calculator.AddProduction(factor,
                         CalculatorToken.Number.AddCode
                         (
                         (x, s) => { x.Result = new IntWrapper(int.Parse(s));
                                     return x; }
                         ));

IntWrapper result = calculator.Parse("15+4*10");
// result == 55

(IntWrapper'ın yalnızca bir Int32 olduğunu unutmayın; C #, bunun bir sınıf olmasını gerektirir, bu yüzden bir sarmalayıcı sınıfı tanıtmak zorunda kaldım)

Umarım yukarıdaki kodun çok güçlü olduğunu görürsünüz: bulabileceğiniz herhangi bir gramer ayrıştırılabilir. Dilbilgisine çok sayıda görevi yerine getirebilecek isteğe bağlı kod parçaları ekleyebilirsiniz. Tüm bunları halletmeyi başarırsanız, sonuç kodunu birçok işi yapmak için yeniden kullanabilirsiniz: bu kod parçasını kullanarak bir komut satırı yorumlayıcısı oluşturmayı hayal edin.


3
Yüksek performanslı bir ayrıştırıcı ve lexer oluşturmak için gereken iş miktarını hafife aldığınızı düşünüyorum.

Kendi lexer jeneratörümü oluşturmayı çoktan bitirdim ve bunun yerine farklı bir algoritma uygulamaya karar verdiğimde kendi çözümleyici jeneratörümü oluşturmakla çok uzağım. Her şeyin işe yaraması çok uzun sürmedi, ama sonra yine 'yüksek performans', sadece 'iyi performans' ve 'büyük asimptotik performans' hedeflerini almadım - Unicode, iyi çalışma süreleri elde etmek için bir kaltak ve C # kullanarak zaten bir performans ek yükü getirir.
Alex ten Brink,

Çok güzel cevap. Seçenek Nr ile aynı fikirdeyim. 3 yukarıda belirtilen tüm nedenlerden dolayı. Ancak, benim durumumda olduğu gibi, bir dil tasarlama konusunda da çok ciddiyseniz, belki de kendi oluşturucunuzu oluşturmaya çalışırken aynı zamanda ayrıştırma jeneratörlerini kullanmanız gerektiğini de ekleyebilirim. Böylece dil konularına başlayabilir ve dilinizi daha hızlı hareket halinde görebilirsiniz
Lefteris

1
Dördüncü bir seçenek var: ayrıştırıcı birleştiriciler.
YuriAlbuquerque 24:14

@AlextenBrink Herhangi bir şans eseri github hesabınız var mı? Gerçekten bu lexer / ayrıştırıcı üzerinde ellerimi almak istiyorum. Yaptığın etkileyici şey.
Behrooz,

22

Asla, hiç ayrıştırıcı yazmadıysan, yapmanı tavsiye ederim. Bu eğlenceli olur ve işlerin nasıl öğrenmek ve ayrıştırıcı ve lexer jeneratörleri yapmaktan kurtaracak o çabayı takdir öğrenmek yanındaki bir ayrıştırıcı gereken zamanı.

Ayrıca, http://compilers.iecc.com/crenshaw/ sayfasını okumayı denemenizi tavsiye ederim .


2
İyi öneri ve çok faydalı bir bağlantı.
Maniero

14

Kendi özyinelemeli iniş ayrıştırıcınızı yazmanın avantajı , sözdizimi hatalarında yüksek kaliteli hata mesajları üretebilmenizdir . Ayrıştırma jeneratörlerini kullanarak, hata üretimleri yapabilir ve belirli noktalarda özel hata mesajları ekleyebilirsiniz, ancak ayrıştırma jeneratörleri ayrıştırma üzerinde tam denetime sahip olma gücüyle eşleşmez.

Kendinizinkini yazmanın bir başka avantajı, gramerinize bire bir yazışma yapmayan daha basit bir gösterime ayrılmanın daha kolay olmasıdır.

Dilbilginiz sabitse ve hata mesajları önemliyse, kendi dilinizi yuvarlayın ya da en azından ihtiyacınız olan hata mesajlarını veren bir ayrıştırıcı oluşturucu kullanın. Dilbilginiz sürekli değişiyorsa, bunun yerine ayrıştırıcı jeneratörleri kullanmayı düşünmelisiniz.

Bjarne Stroustrup, C ++ 'ın ilk uygulaması için YACC'yi nasıl kullandığı hakkında konuşuyor (bkz. C ++' ın Tasarımı ve Evrimi ). Bu ilk durumda, bunun yerine kendi özyinelemeli iniş ayrıştırıcısını yazmasını diledi!


İlk denemelerin bir ayrıştırıcı jeneratörüyle yapılması gerektiğine ikna olmadım. Özel bir çözüme geçmek için bana bazı avantajlar verdin. Henüz hiçbir şeye karar vermiyorum, ama bana yardım etmek için yararlı bir cevap.
Maniero

++ Bu cevap tam olarak söyleyeceğim şey. Çok sayıda dil inşa ettim ve neredeyse her zaman özyinelemeli kökenli kullandım. Sadece ihtiyaç duyduğum dilin C veya C ++ (ya da Lisp) üzerine bazı makroları yerleştirerek kurulduğunu da ekledim.
Mike Dunlavey,

JavaCC'nin en iyi hata mesajlarına sahip olduğu iddia ediliyor. Ayrıca, V8 ve Firefox'ta JavaScript hatalarını ve uyarı mesajlarını fark edin, herhangi bir ayrıştırıcı jeneratör kullanmadıklarını düşünüyorum.
Ming-Tang

2
@SHiNKiROU: Gerçekten de, bu muhtemelen JavaCC'nin de özyinelemeli iniş ayrıştırma kullandığı bir kaza değildir.
Macneil

10

Seçenek 3: Hiçbiri (Kendi ayrıştırıcı jeneratörünüzü döndürmeyin)

Kullanmamak için bir neden var diye antlr , bizon , Coco / R , Grammatica , JavaCC , Limon , yarım kaynatılmış , SableCC , Quex , vb - o anında kendi ayrıştırıcı + lexer rulo gerektiği anlamına gelmez.

Tanımlayın neden neden onlar size hedefe ulaşmak izin vermeyin - tüm bu araçlar yeterince iyi değil?

Dilbilgisi ile uğraştığınız tuhaflıkların benzersiz olduğundan emin değilseniz, sadece bunun için tek bir özel çözümleyici + lexer oluşturmamalısınız. Bunun yerine, istediğiniz şeyi yaratacak, ancak gelecekteki ihtiyaçları karşılamak için de kullanılabilecek bir araç oluşturun, sonra diğer insanların da sizinle aynı sorunu yaşamasını önlemek için Özgür Yazılım olarak yayınlayın.


1
Öncelikle ayrıştırma jeneratörlerini deneyin ve daha sonra özel bir çözümü deneyin, ancak hangi özel (dis) avantajları kabul ediyorum? Bu neredeyse genel bir tavsiyedir.
Maniero

1
Bu genel bir tavsiye - ama sonra genel bir soru sordunuz. : P Yarın artılar ve eksiler hakkında daha spesifik düşüncelerle genişleteceğim.
Peter Boughton

1
Özel bir ayrıştırıcı ve lexer oluşturmak için gereken iş miktarını hafife aldığınızı düşünüyorum. Özellikle tekrar kullanılabilir bir tane.

8

Kendi ayrıştırıcınızı kullanmak, sizi doğrudan dilinizin karmaşıklığı hakkında düşünmeye zorlar. Dilin ayrıştırılması zorsa, muhtemelen anlaşılması zor olacaktır.

İlk günlerde ayrıştırma üreticilerine çok fazla ilgi vardı, çok karmaşık (bazıları "işkence" diyebilirdi) dil sözdizimi tarafından motive edildi. JOVIAL özellikle kötü bir örnekti: diğer her şeyin en fazla bir sembol gerektirdiği bir zamanda iki sembol bakış açısı gerektiriyordu. Bu, bir JOVIAL derleyici için ayrıştırıcıyı beklenenden daha zor hale getirdi (General Dynamics / Fort Worth Division, F-16 programı için JOVIAL derleyicileri tedarik ederken zor yoldan öğrendiği için).

Bugün, özyinelemeli iniş evrensel olarak tercih edilen yöntemdir, çünkü derleyici yazarları için daha kolaydır. Özyinelemeli alçalış derleyiciler basit, temiz bir dil tasarımını kuvvetli bir şekilde ödüllendirir; bu nedenle, özyinelemeli, dağınık olandan daha basit, temiz bir dil için özyinelemeli bir çözümleyici yazmak çok daha kolaydır.

Sonunda: Dilinizi LISP'ye yerleştirmeyi ve LISP tercümanının sizin için ağır yük kaldırmasına izin vermeyi düşündünüz mü? AutoCAD bunu yaptı ve hayatlarını çok daha kolay hale getirdi. Dışarıda oldukça hafif hafif LISP tercümanları var, bazıları gömülebilir.


Özel bir çözüm bulmak ilginç bir tartışma.
Maniero

1
Çok hoş. Fortran'ın, JOVIAL'dan önce bir şeyleri ayrıştırmak için neredeyse rasgele (tüm çizgi) bir bakış açısı gerektirdiği bir bilgi noktası olarak ekleyeceğim. Ancak o zaman, bir dilin nasıl yapılacağı (veya uygulanacağı) hakkında başka bir fikirleri yoktu.
Macneil

Yürüme, gittiğin yere gitmenin gerçekten değip değmeyeceğini düşünmek için zaman tanıdığından en iyi ulaşım aracı. O da sağlıklı.
babou

6

Bir kez ticari uygulama için bir ayrıştırıcı yazdım ve yacc kullandım . Bir geliştiricinin her şeyi C ++ 'ta elle yazdığı ve yaklaşık beş kat daha yavaş çalıştığı rakip bir prototip vardı.

Bu ayrıştırıcının lexer gelince, tamamen elle yazdım. Sürdü - üzgünüm, neredeyse 10 yıl önceydi, bu yüzden tam olarak hatırlamıyorum - C'de yaklaşık 1000 satır .

Sözcüğü elle yazmamın nedeni, ayrıştırıcının giriş dilbilgisi idi. Bu bir zorunluluktu, ayrıştırıcı uygulamamın tasarladığım şeyin aksine uyması gereken bir şeydi. (Tabii ki farklı şekilde tasarlardım. Ve daha iyisi!) Dilbilgisi, içeriğe bağımlıydı ve hatta bazı yerlerde anlambilime bağlıydı. Örneğin, bir noktalı virgül bir yerdeki bir belirtecin parçası olabilir, ancak daha önce ayrıştırılan bazı öğelerin anlamsal yorumuna dayanarak farklı bir yerdeki bir ayırıcı olabilir. Bu yüzden, anlamsal bağımlılıkları elle yazılmış bir Lexer'a "gömdüm" ve bu beni yacc içinde uygulanması kolay olan oldukça basit bir BNF ile bıraktı .

EKLENDİ cevaben MacNeil'in : yacc programcı sağlayan çok güçlü bir soyutlama sağlayan böyle terminalleri, sigara terminalleri, yapımları ve malzeme açısından düşünüyorum. Ayrıca, yylex()işlevi uygularken , geçerli belirteci döndürmeye odaklanmamda bana yardımcı oldu ve ondan önce veya sonra ne olduğu konusunda endişelenmeyin. C ++ programcısı karakter düzeyinde, bu soyutlamanın yararı olmadan çalıştı ve daha karmaşık ve daha az verimli bir algoritma oluşturdu. Yavaş hızın C ++ 'ın kendisi veya herhangi bir kütüphaneyle bir ilgisi olmadığı sonucuna vardık. Hafızaya yüklenen dosyalar ile saf ayrıştırma hızını ölçtük; Eğer bir dosya tamponlama problemimiz olsaydı, yacc bunu çözmek için bizim seçim aracımız olmazdı.

Üstelik EKLEME İSTİYOR : Bu, genel olarak ortakları yazmak için bir reçete değil, sadece belirli bir durumda nasıl çalıştığına bir örnek.


Elle elde edilen beş kat daha yavaş C ++ uygulamasını merak ediyorum: Belki de dosya tamponlama yetersizdi? Büyük bir fark yaratabilir.
Macneil

@ Macneil: Cevabımı bir ek göndereceğim; yorum çok uzun.
azheglov

1
++ İyi bir deneyim. Performansa çok fazla ağırlık vermem. Aksi takdirde iyi programların aptalca ve gereksiz bir şey tarafından yavaşlatılması kolaydır. Ne yapmamasını bilecek kadar özyinelemeli iniş ayrıştırıcıları yazdım, bu yüzden daha hızlı bir şey olup olmadığından şüpheliyim. Sonuçta, karakterlerin okunması gerekiyor. Masaları dolduran ayrıştırıcıların biraz daha yavaş olacağından şüpheleniyorum, ancak farkına varmak için yeterli değil.
Mike Dunlavey,

3

Bu tamamen ayrıştırmanız gerekenlere bağlıdır. Kendinizi bir lexer'ın öğrenme eğrisine varacak kadar hızlı yuvarlayabilir misiniz? Ayrıştırılacak malzeme kararını daha sonra pişman olmayacak kadar statik mi? Mevcut uygulamaları aşırı karmaşık buluyor musunuz? Eğer öyleyse, eğlenerek kendi eğlencelerinizi yapın, ancak sadece bir öğrenme eğrisi kullanmıyorsanız.

Son zamanlarda, şimdiye kadar kullandığım en basit ve en kolay olan limon ayrıştırıcısını gerçekten sevdim . İşleri kolaylaştırmak uğruna, sadece çoğu ihtiyaç için kullanıyorum. SQLite, diğer bazı kayda değer projeleri olduğu gibi kullanır.

Ancak, ben hiç bir şekilde kullanmaya ihtiyacım olduğunda (bu nedenle, limon), yolumuza girmeden, lexers ile hiç ilgilenmiyorum. Olabilirsin ve öyleyse neden bir tane yapmıyorsun? Var olanı kullanmaya geri döneceğinize dair bir fikrim var, ama gerekiyorsa kaşınıyorsunuz :)


3
+1 "Bir Lexer'ın öğrenme eğrisine varabileceğinden daha hızlı bir şekilde kendin atabilir misin?"
bobah

Evet, iyi nokta.
Maniero

3

Amacın ne olduğuna bağlı.

Ayrıştırıcıların / derleyicilerin nasıl çalıştığını öğrenmeye mi çalışıyorsunuz? O zaman sıfırdan kendin yaz. Yaptıklarının tüm içeriğini ve çıkışlarını takdir etmeyi gerçekten öğrenmenin tek yolu bu. Son bir kaç aydır bir tane yazıyorum ve ilginç ve değerli bir deneyim oldu, özellikle de 'ah, bu yüzden dil X bunu neden yapıyor?'

Son başvuru tarihine bir başvuru için hızlı bir şekilde bir araya getirmeniz mi gerekiyor? O zaman belki bir çözümleyici aracı kullanın.

Önümüzdeki 10, 20, belki 30 yıl boyunca genişletmek isteyeceğiniz bir şeye mi ihtiyacınız var? Kendinizinkini yazın ve zaman ayırın. Buna değecek.


Bu benim derleyiciler üzerindeki ilk çalışmam, öğreniyorum / deneyimliyorum ve uzun zamandır sürdürme niyetim.
Maniero

3

Martin Fowlers'ın dil tezgahı yaklaşımını düşündün mü ? Makaleden alıntı

Bir dil tezgahının denklemde yaptığı en belirgin değişiklik, harici DSL'ler yaratma kolaylığıdır. Artık bir çözümleyici yazmak zorunda değilsin. Soyut sözdizimini tanımlamanız gerekir - fakat bu aslında oldukça basit bir veri modelleme adımıdır. Ek olarak DSL'iniz güçlü bir IDE alır - bu editörü tanımlamak için biraz zaman harcamanıza rağmen. Jeneratör hala yapmanız gereken bir şey ve benim fikrim, hiç olmadığı kadar kolay olmadığı. Ancak, iyi ve basit bir DSL için bir jeneratör oluşturmak, egzersizin en kolay kısımlarından biridir.

Bunu okumak, kendi ayrıştırıcınızı yazma günlerinin bittiğini ve mevcut kütüphanelerden birini kullanmanın daha iyi olduğunu söyleyebilirim. Kütüphaneye hakim olduktan sonra, gelecekte oluşturacağınız tüm DSL'ler bu bilgiden yararlanır. Ayrıca, diğerleri ayrıştırma yaklaşımınızı öğrenmek zorunda değildir.

Yorumu kapsayacak şekilde düzenleyin (ve soruyu yeniden düzenlendi)

Kendinizinkini yuvarlamanın avantajları

  1. Ayrıştırıcıya sahip olacak ve karmaşık bir dizi sorunla bu güzel düşünme deneyimini kazanacaksınız
  2. Başka hiç kimsenin düşünmediği özel bir şeyle karşılaşabilirsiniz (olası değil ama zekice bir adam gibi görünüyorsun)
  3. Sizi ilginç bir problemle meşgul edecek.

Yani kısacası, ustalaşmak için güçlü bir motivasyona sahip olduğunuz, gerçekten zor bir problemin bağırsaklarına derinlemesine dalmak istediğinizde kendinize yuvarlanmalısınız.

Başkasının kütüphanesini kullanmanın avantajları

  1. Tekerleği yeniden icat etmekten kaçınacaksınız (programlamada kabul edeceğiniz sık rastlanan bir problem)
  2. Nihai sonuçlara odaklanabilirsiniz (parlak yeni dil) ve nasıl çözümlendiği hakkında çok fazla endişe duymazsınız.
  3. Dilinizi çok daha hızlı eylemde görürsünüz (ancak ödülünüz daha az olacaktır çünkü hepsi bu kadar değil)

Bu nedenle, hızlı sonuç almak istiyorsanız başka birinin kütüphanesini kullanın.

Genel olarak, bu, soruna ve dolayısıyla çözüme ne kadar sahip olmak istediğinize bağlı. Eğer hepsini istiyorsan kendin yap.


Düşünmeye harika bir alternatif.
Maniero

1
@bigown Sorunuza daha iyi cevap vermek için düzenlendi
Gary Rowe

2

Kendi yazınızı yazmanın en büyük avantajı kendi yazınızı nasıl yapacağınızı bilmenizdir. Yacc gibi bir alet kullanmanın en büyük avantajı, aleti nasıl kullanacağınızı bilmenizdir. İlk keşif için treetop hayranıyım .


Özellikle yararlı değil. “Araba kullanmayı öğrenmenin avantajı, araba kullanabilmenizdir. Bisiklete binmeyi öğrenmenin avantajları, bisiklet
sürebilmenizdir

1

Neden açık kaynaklı bir ayrıştırıcı jeneratöre dokunup kendin yapmıyorsun? Ayrıştırma jeneratörleri kullanmazsanız, kodunuzu korumak çok zor olacaktır, eğer büyük değişiklikler yaparsanız, dilinizin sözdizimini değiştirirsiniz.

Parserlerimde, kodları okunaklı kılmak için bazı ifadeler kullanmak için düzenli ifadeler kullandım (Perl tarzı). Ancak, ayrıştırıcı tarafından üretilen kod daha hızlı devlet tablolar ve uzun yaparak olabilir switch- caseEğer sürece kaynak kod boyutunu artırabilir s, .gitignoreonlarla.

Özel olarak yazılan ayrıştırıcılarımın iki örneği:

https://github.com/SHiNKiROU/DesignScript - TEMEL bir lehçe, çünkü dizi gösterimine bakmak için tembel olduğum için hata mesajı feda ettim, https://github.com/SHiNKiROU/ExprParser - Bir formül hesaplayıcısı. Garip metaprogramlama numaralarına dikkat edin


0

"Bu denenmiş ve test edilmiş" tekerleği "mi kullanmalıyım veya yeniden icat etmeli miyim?"


1
Bahsettiğiniz bu "tekerlek" nedir? ;-)
Jason Whitehorn,

IMO bu, bu soru hakkında iyi bir fikir değil. Bu sadece belirli bir duruma uygun olmayan genel bir öneridir. Area51.stackexchange.com/proposals/7848 teklifinin erken kapatıldığından şüphelenmeye başladım .
Maniero

2
Eğer tekerleği yeniden icat etmemiş olsaydık, günlük olarak 100kmph + 'da yolculuk yapmazdık - tahta akslarda büyük ağır kaya toplanmaları kullanılmadığı takdirde, kullanılan modern lastiklerin bir çok çeşidinden daha iyidir. çok fazla araç var mı?
Peter Boughton

Bu geçerli bir fikir ve doğru sezgi. Belirli bir avantaj veya dezavantajları listeleyebiliyorsanız, bu cevabın daha yararlı olabileceğini düşünüyorum, çünkü bu tür şeyler tamamen şartlara bağlıdır.
Macneil,

@Peter: Bir şeyi yeniden icat etmek (tamamen farklı yapmak anlamına gelir) ama ek gereksinimleri karşılamak için mevcut bir çözümü iyileştirmek bir şeydir. 'İyileştirme' için her şeyim, ancak çoktan çözülmüş bir problem için çizim tahtasına geri dönmek yanlış görünüyor.
JBRWilkinson
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.