क्या मुझे पार्सर जनरेटर का उपयोग करना चाहिए या मुझे अपने स्वयं के कस्टम लेसर और पार्सर कोड को रोल करना चाहिए?


81

प्रोग्रामिंग भाषा के व्याकरण पर काम करने के प्रत्येक तरीके के क्या विशिष्ट फायदे और नुकसान हैं?

क्यों / मुझे कब अपना रोल करना चाहिए? मुझे जनरेटर का उपयोग कब / क्यों करना चाहिए?


Boost.Spirit क्यूई को एक शॉट दें ।
अब्राहिम मोहम्मदी

जवाबों:


78

वास्तव में तीन विकल्प हैं, उनमें से तीन अलग-अलग स्थितियों में बेहतर हैं।

विकल्प 1: पार्सर जनरेटर, या 'आपको कुछ भाषा को पार्स करने की आवश्यकता है और आप इसे काम करना चाहते हैं,'

कहते हैं, अब आपको कुछ प्राचीन डेटा प्रारूप के लिए एक पार्सर बनाने के लिए कहा जाता है। या आपको अपने पार्सर को तेज़ होने की आवश्यकता है। या आप आसानी से बनाए रखने के लिए अपने पार्सर की जरूरत है।

इन मामलों में, आप शायद पार्सर जनरेटर का उपयोग कर रहे हैं। आपको विवरणों के बारे में जानने की आवश्यकता नहीं है, आपको ठीक से काम करने के लिए बहुत सारे जटिल कोड प्राप्त करने की आवश्यकता नहीं है, आप बस व्याकरण लिखें जो इनपुट का पालन करेगा, कुछ हैंडलिंग कोड और प्रेस्टो लिखें: तत्काल पार्सर।

लाभ स्पष्ट हैं:

  • यह (आमतौर पर) विनिर्देशन लिखना काफी आसान है, विशेष रूप से अगर इनपुट प्रारूप बहुत अजीब नहीं है (विकल्प 2 बेहतर होगा यदि यह है)।
  • आप आसानी से समझ में आने वाले काम के एक बहुत ही आसानी से बनाए जाने वाले टुकड़े के साथ समाप्त होते हैं: एक व्याकरण की परिभाषा आमतौर पर कोड की तुलना में बहुत अधिक प्राकृतिक प्रवाह होती है।
  • अच्छे पार्सर जनरेटर द्वारा उत्पन्न पार्सर आमतौर पर हाथ से लिखे गए कोड की तुलना में बहुत तेज होते हैं। हाथ से लिखा कोड तेज़ हो सकता है, लेकिन केवल अगर आप अपना सामान जानते हैं - यही कारण है कि सबसे व्यापक रूप से उपयोग किए जाने वाले संकलक हाथ से लिखे गए पुनरावर्ती-वंश पार्सर का उपयोग करते हैं।

एक बात है जो आपको पार्सर-जनरेटर के साथ सावधान रहना होगा: कभी-कभी आपके व्याकरण को अस्वीकार कर सकता है। विभिन्न प्रकार के पार्सर्स के अवलोकन के लिए और वे आपको कैसे काट सकते हैं, आप यहां शुरू करना चाहते हैंयहां आप बहुत सारे कार्यान्वयन और व्याकरण के प्रकारों का अवलोकन पा सकते हैं, जिन्हें वे स्वीकार करते हैं।

विकल्प 2: हाथ से लिखे पर्सर, या 'आप अपने खुद के पार्सर का निर्माण करना चाहते हैं, और आप उपयोगकर्ता के अनुकूल होने की परवाह करते हैं'

पार्सर जनरेटर अच्छे हैं, लेकिन वे बहुत उपयोगकर्ता (अंत-उपयोगकर्ता, आप नहीं) के अनुकूल नहीं हैं। आप आमतौर पर अच्छे त्रुटि संदेश नहीं दे सकते हैं, न ही आप त्रुटि सुधार प्रदान कर सकते हैं। शायद आपकी भाषा बहुत ही अजीब है और पार्सर आपके व्याकरण को अस्वीकार कर देते हैं या आपको जनरेटर द्वारा आपको अधिक नियंत्रण की आवश्यकता होती है।

इन मामलों में, हाथ से लिखे गए पुनरावर्ती-वंशीय पार्सर का उपयोग करना संभवतः सबसे अच्छा है। यह सही होने पर जटिल हो सकता है, आपके पास अपने पार्सर पर पूरा नियंत्रण है, ताकि आप सभी प्रकार के अच्छे सामान कर सकें जो आप पार्सर जनरेटर के साथ नहीं कर सकते हैं, जैसे त्रुटि संदेश और यहां तक ​​कि त्रुटि पुनर्प्राप्ति (सी # फ़ाइल से सभी अर्धविरामों को हटाने का प्रयास करें) : C # कंपाइलर शिकायत करेगा, लेकिन अर्धविराम की मौजूदगी की परवाह किए बिना ज्यादातर अन्य त्रुटियों का पता लगाएगा)।

हाथ से लिखे गए पार्सर आमतौर पर उत्पन्न लोगों की तुलना में बेहतर प्रदर्शन करते हैं, यह मानते हुए कि पार्सर की गुणवत्ता काफी अधिक है। दूसरी ओर, यदि आप एक अच्छा पार्सर लिखने का प्रबंधन नहीं करते हैं - आमतौर पर अनुभव (ज्ञान) या डिजाइन की कमी (संयोजन) के कारण - तो प्रदर्शन आमतौर पर धीमा होता है। लेक्सर्स के लिए इसके विपरीत सच है: आम तौर पर उत्पन्न लेकर्स टेबल लुकअप का उपयोग करते हैं, जिससे वे (सबसे) हाथ से लिखे गए लोगों की तुलना में तेज होते हैं।

शिक्षा-वार, अपने स्वयं के पार्सर को लिखना आपको एक जनरेटर का उपयोग करने से अधिक सिखाएगा। आपको अधिक से अधिक जटिल कोड लिखना होगा, इसके अलावा आपको यह समझना होगा कि आप किसी भाषा को कैसे पार्स करते हैं। दूसरी ओर, यदि आप सीखना चाहते हैं कि अपनी भाषा कैसे बनाएं (तो, भाषा डिजाइन में अनुभव प्राप्त करें), या तो विकल्प 1 या विकल्प 3 बेहतर है: यदि आप एक भाषा विकसित कर रहे हैं, तो यह संभवतः बहुत कुछ बदल देगा। और विकल्प 1 और 3 आपको इसके साथ एक आसान समय देते हैं।

विकल्प 3: हाथ से लिखे पार्सर जनरेटर, या 'आप इस परियोजना से बहुत कुछ सीखने की कोशिश कर रहे हैं और आप एक निफ्टी कोड के साथ समाप्त होने का बुरा नहीं मानेंगे।

यह वह रास्ता है जिस पर मैं वर्तमान में चल रहा हूं: आप अपना स्वयं का पार्सर जनरेटर लिखें । अत्यधिक अनौपचारिक होते हुए, ऐसा करना संभवतः आपको सबसे अधिक सिखाएगा।

आपको एक विचार देने के लिए कि इस तरह की परियोजना में क्या शामिल है, मैं आपको अपनी प्रगति के बारे में बताऊंगा।

लेसर जेनरेटर

मैंने पहले अपना खुद का लेक्सर जनरेटर बनाया। मैं आमतौर पर सॉफ़्टवेयर डिज़ाइन करता हूं, जिसके साथ कोड का उपयोग कैसे किया जाएगा, इसलिए मैंने सोचा कि मैं कैसे अपने कोड का उपयोग करने में सक्षम होना चाहता हूं और कोड का यह टुकड़ा लिखा है (यह C # में है):

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

इनपुट स्ट्रिंग-टोकन जोड़े को एक समान पुनरावर्ती संरचना में परिवर्तित किया जाता है, जो नियमित अभिव्यक्तियों का वर्णन करता है जो वे एक अंकगणितीय स्टैक के विचारों का उपयोग करते हैं। इसके बाद इसे NFA (nondeterministic finite automaton) में बदल दिया जाता है, जो बदले में DFA (निर्धारक परिमित ऑटोमेटन) में परिवर्तित हो जाता है। फिर आप डीएफए के खिलाफ तार का मिलान कर सकते हैं।

इस तरह, आप एक अच्छा विचार प्राप्त करते हैं कि कैसे लेक्सर्स काम करते हैं। इसके अलावा, यदि आप इसे सही तरीके से करते हैं तो आपके लेक्सर जनरेटर से परिणाम पेशेवर कार्यान्वयन के रूप में तेजी से हो सकते हैं। आप विकल्प 2 की तुलना में किसी भी अभिव्यंजकता को नहीं खोते हैं, और विकल्प 1 की तुलना में बहुत अधिक अभिव्यक्ति नहीं है।

मैंने कोड के 1600 से अधिक लाइनों में अपने लेक्सर जनरेटर को लागू किया। यह कोड उपरोक्त काम करता है, लेकिन यह अभी भी हर बार जब आप कार्यक्रम शुरू करते हैं, तो मक्खी पर लेक्सर उत्पन्न करता है: मैं इसे किसी बिंदु पर डिस्क पर लिखने के लिए कोड जोड़ने जा रहा हूं।

यदि आप जानना चाहते हैं कि अपना खुद का लेख लिखने का तरीका, यह शुरू करने के लिए एक अच्छी जगह है।

पार्सर जनरेटर

आप फिर अपना पार्सर जनरेटर लिखें। मैं विभिन्न प्रकार के पार्सरों पर एक सिंहावलोकन के लिए यहां फिर से उल्लेख करता हूं - अंगूठे के एक नियम के रूप में, वे जितना अधिक पार्स कर सकते हैं, उतना ही धीमा।

गति मेरे लिए कोई समस्या नहीं है, मैंने एक एली पार्सर को लागू करने के लिए चुना। एक अर्ली पार्सर के उन्नत कार्यान्वयन को अन्य पार्सर प्रकारों के मुकाबले लगभग दोगुना दिखाया गया है

उस गति हिट के बदले में, आपको किसी भी तरह के व्याकरण, यहां तक ​​कि अस्पष्ट लोगों को पार्स करने की क्षमता मिलती है। इसका मतलब है कि आपको इस बारे में कभी चिंता करने की आवश्यकता नहीं है कि क्या आपके पार्सर में कोई लेफ्ट-रिकर्सन है, या एक शिफ्ट-कम संघर्ष क्या है। आप व्याकरण का अधिक आसानी से अस्पष्ट व्याकरणों का उपयोग कर परिभाषित कर सकते हैं यदि यह कोई फर्क नहीं पड़ता कि कौन सा तोता पेड़ परिणाम है, जैसे कि यह कोई फर्क नहीं पड़ता कि आप 1 + 2 + 3 के रूप में पार्स करते हैं (1 + 2) +3 या 1 के रूप में + (2 + 3)।

यह मेरे पार्सर जनरेटर का उपयोग कर कोड का एक टुकड़ा जैसा दिख सकता है:

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 बस एक Int32 है, सिवाय इसके कि C # के लिए इसे एक वर्ग की आवश्यकता है, इसलिए मुझे एक आवरण वर्ग शुरू करना पड़ा)

मुझे आशा है कि आप देख सकते हैं कि उपरोक्त कोड बहुत शक्तिशाली है: किसी भी व्याकरण के साथ आप आ सकते हैं। आप बहुत सारे कार्य करने में सक्षम व्याकरण में मनमाने ढंग से कोड जोड़ सकते हैं। यदि आप इस सभी कार्य को प्राप्त करने का प्रबंधन करते हैं, तो आप बहुत आसानी से बहुत सारे कार्य करने के लिए परिणामी कोड का फिर से उपयोग कर सकते हैं: कोड के इस टुकड़े का उपयोग करके कमांड-लाइन दुभाषिया बनाने की कल्पना करें।


3
मुझे लगता है कि आपको उच्च प्रदर्शन पार्सर और लेसर बनाने के लिए आवश्यक कार्य की मात्रा को कम करके आंका गया है।

मैंने पहले से ही अपने खुद के लेक्सर जनरेटर का निर्माण पूरा कर लिया है और जब मैंने इसके बजाय एक अलग एल्गोरिथ्म को लागू करने का फैसला किया तो मैं अपने खुद के पार्सर जनरेटर के निर्माण के साथ काफी दूर था। मुझे यह सब काम करने में लंबा समय नहीं लगा, लेकिन फिर मैंने 'उच्च प्रदर्शन' का लक्ष्य नहीं बनाया, बस 'अच्छा प्रदर्शन' और 'महान स्पर्शोन्मुख प्रदर्शन' - यूनिकोड अच्छा चलने का समय पाने के लिए एक कुतिया है और C # का उपयोग करने से पहले ही एक प्रदर्शन ओवरहेड हो जाता है।
एलेक्स दस ब्रिंक

बहुत अच्छा जवाब। मैं आपके विकल्प Nr से सहमत हूँ। 3 उपरोक्त सभी कारणों के लिए। लेकिन मैं यह भी जोड़ सकता हूं कि अगर, जैसा कि मेरा मामला है, आप एक भाषा को डिजाइन करने के बारे में भी बहुत गंभीर हैं, तो शायद आपको उसी समय पार्सर जनरेटर का उपयोग करना चाहिए जैसे कि अपना खुद का बनाने की कोशिश कर रहा है। तो आप भाषा के मुद्दों पर एक सिर शुरू कर सकते हैं और अपनी भाषा को तेजी से कार्रवाई में देखने में सक्षम हो सकते हैं
Lefteris

1
एक चौथा विकल्प है: पार्सर कॉम्बिनेटर।
यूरीअल्बुकर्क

@AlextenBrink क्या आपके पास किसी भी संयोग से एक github खाता है? मैं वास्तव में उस लेक्सर / पार्सर पर अपने हाथ लाना चाहता हूं। आपके द्वारा बनाई गई प्रभावशाली चीज।
बेहरोज़ जूल

22

यदि आपने कभी नहीं लिखा है, तो कभी एक पार्सर लिखा है मैं आपको यह करने की सलाह दूंगा। यह मजेदार है, और आप सीखते हैं कि चीजें कैसे काम करती हैं, और आप उस प्रयास की सराहना करना सीखते हैं जो पार्सर और लेक्सर जनरेटर आपको अगली बार आपको पार्सर की आवश्यकता होने से बचाते हैं ।

मैं यह भी सुझाव देता हूं कि आप http://compilers.iecc.com/crenshaw/ को पढ़ने का प्रयास करें, क्योंकि यह कैसे करना है, इसके बारे में एक बहुत ही डाउन-टू-अर्थ रवैया है।


2
अच्छा सुझाव और एक बहुत ही उपयोगी लिंक।
मनेरियो

14

अपने स्वयं के पुनरावर्ती वंश पार्सर को लिखने का लाभ यह है कि आप सिंटैक्स त्रुटियों पर उच्च-गुणवत्ता वाले त्रुटि संदेश उत्पन्न कर सकते हैं। पार्सर जनरेटर का उपयोग करना, आप त्रुटि निर्माण कर सकते हैं और कुछ बिंदुओं पर कस्टम त्रुटि संदेश जोड़ सकते हैं, लेकिन पार्सर जनरेटर केवल पार्सिंग पर पूर्ण नियंत्रण होने की शक्ति से मेल नहीं खाते हैं।

अपना खुद का लिखने का एक और फायदा यह है कि एक सरल प्रतिनिधित्व के लिए पार्स करना आसान है, जिसमें आपके व्याकरण के लिए एक से एक पत्राचार नहीं है।

यदि आपका व्याकरण निर्धारित है, और त्रुटि संदेश महत्वपूर्ण हैं, तो अपना स्वयं का रोल करने पर विचार करें, या कम से कम एक पार्सर जनरेटर का उपयोग करें जो आपको आवश्यक त्रुटि संदेश देता है। यदि आपका व्याकरण लगातार बदल रहा है, तो आपको इसके बजाय पार्सर जनरेटर का उपयोग करने पर विचार करना चाहिए।

Bjarne Stroustrup इस बारे में बात करते हैं कि उन्होंने C ++ के पहले कार्यान्वयन के लिए YACC का उपयोग कैसे किया (देखें C ++ का डिज़ाइन और विकास )। उस पहले मामले में, उन्होंने चाहा कि उन्होंने इसके बजाय अपने स्वयं के पुनरावर्ती वंश पार्सर लिखा!


मैं मुश्किल से आश्वस्त हूं कि पहला प्रयोग एक पार्सर जनरेटर के साथ होना चाहिए। आपने मुझे कस्टम समाधान के लिए स्वैप करने के कुछ फायदे दिए हैं। मैं अभी तक कुछ भी तय नहीं कर रहा हूं, लेकिन यह मेरी मदद करने के लिए एक उपयोगी उत्तर है।
मनेरियो

++ यह जवाब बिल्कुल वही है जो मैं कहूंगा। मैंने कई भाषाओं का निर्माण किया है और लगभग हमेशा पुनरावर्ती वंश का उपयोग किया है। मैं केवल यह जोड़ना चाहूंगा कि कई बार ऐसा हुआ है जब मुझे जिस भाषा की ज़रूरत थी वह सी या सी ++ (या लिस्प) के शीर्ष पर कुछ मैक्रोज़ को बिछाकर सबसे सरल रूप से बनाया गया था।
माइक डनलवे

JavaCC में सबसे अच्छा त्रुटि संदेश होने का दावा किया जाता है। इसके अलावा, V8 और फ़ायरफ़ॉक्स पर जावास्क्रिप्ट त्रुटि और चेतावनी संदेशों को नोटिस करें, मुझे लगता है कि उन्होंने किसी भी पार्सर जनरेटर का उपयोग नहीं किया।
मिंग-तांग

2
@SHiNKiROU: वास्तव में, यह शायद एक दुर्घटना नहीं है कि जावासीसी पुनरावर्ती वंशज पार्सिंग का भी उपयोग करता है।
मैकनील

10

विकल्प 3: न तो (अपने स्वयं के पार्सर जनरेटर को रोल करें)

सिर्फ इसलिए कि ANTLR , bison , Coco / R , Grammatica , JavaCC , Lemon , Parboiled , SableCC , Quex , इत्यादि का उपयोग न करने का एक कारण है - इसका मतलब यह नहीं है कि आपको तुरंत अपने स्वयं के पार्सर या लेसर को रोल करना चाहिए।

पहचानें कि ये सभी उपकरण पर्याप्त अच्छे क्यों नहीं हैं - वे आपको अपना लक्ष्य हासिल करने क्यों नहीं देते?

जब तक आप निश्चित नहीं होते कि जिस व्याकरण में आप काम कर रहे हैं उसमें विषमताएँ अद्वितीय हैं, तो आपको इसके लिए केवल एक ही कस्टम पार्सर + लेक्सर नहीं बनाना चाहिए। इसके बजाय, एक ऐसा उपकरण बनाएं जो आप चाहते हैं, लेकिन भविष्य की जरूरतों को पूरा करने के लिए भी इस्तेमाल किया जा सकता है, फिर इसे अन्य लोगों की समस्या को रोकने के लिए फ्री सॉफ्टवेयर के रूप में जारी करें।


1
मैं पहले पार्सर जनरेटर की कोशिश से सहमत हूं और फिर एक कस्टम समाधान की कोशिश करता हूं, लेकिन क्या विशिष्ट (डिस) फायदे हैं? यह लगभग एक सामान्य सलाह है।
मनेरियो

1
यह सामान्य सलाह है - लेकिन फिर आपने एक सामान्य प्रश्न पूछा। : पी मैं इसे पेशेवरों और विपक्षों पर कल कुछ और विशिष्ट विचारों के साथ विस्तारित करूँगा।
पीटर बॉटन

1
मुझे लगता है कि आप कस्टम पार्सर और लेसर बनाने के लिए आवश्यक काम की मात्रा को कम आंकते हैं। विशेष रूप से एक पुन: प्रयोज्य।

8

अपने स्वयं के पार्सर को रोल करना आपको अपनी भाषा की जटिलता के बारे में सीधे सोचने के लिए मजबूर करता है। अगर भाषा को छांटना कठिन है, तो शायद समझना कठिन है।

शुरुआती दिनों में पार्सर जनरेटर में बहुत रुचि थी, अत्यधिक-जटिल से प्रेरित (कुछ कहेंगे "अत्याचार") भाषा वाक्य रचना। JOVIAL एक विशेष रूप से खराब उदाहरण था: इसे दो प्रतीक लुकहेड की आवश्यकता थी, एक ऐसे समय में जब किसी एक चीज को सबसे अधिक एक प्रतीक की आवश्यकता होती है। इसने JOVIAL कंपाइलर के लिए पार्सर का निर्माण करना अपेक्षा से अधिक कठिन बना दिया (क्योंकि जनरल डायनेमिक्स / फोर्ट वर्थ डिवीजन ने F-16 प्रोग्राम के लिए JOVIAL कंपाइलरों की खरीद के लिए कठिन तरीका सीखा)।

आज, पुनरावर्ती वंश सार्वभौमिक रूप से पसंदीदा तरीका है, क्योंकि यह संकलक लेखकों के लिए आसान है। पुनरावर्ती वंश संकलक सरल, स्वच्छ भाषा डिजाइन को पुरजोर रूप से पुरस्कृत करते हैं, जिसमें एक सरल, स्वच्छ भाषा के लिए पुनरावर्ती, गन्दा की तुलना में एक पुनरावर्ती-वंशीय पार्सर लिखना बहुत आसान है।

अंत में: क्या आपने अपनी भाषा को LISP में एम्बेड करने और LISP दुभाषिया को आपके लिए भारी उठाने की अनुमति देने पर विचार किया है? ऑटोकैड ने ऐसा किया, और पाया कि इससे उनका जीवन बहुत आसान हो गया। वहाँ कुछ हल्के LISP दुभाषिए हैं, कुछ एम्बेड करने योग्य हैं।


एक कस्टम समाधान रोल करने के लिए यह एक दिलचस्प तर्क है।
मनेरियो

1
बहुत अच्छा। मैं सिर्फ जानकारी के एक बिंदु के रूप में जोड़ूंगा कि फोरट्रान को जोवियल से पहले चीजों को पार्स करने के लिए लगभग मनमानी (पूरी लाइन) लुकहेड की आवश्यकता थी। लेकिन उस समय, उनके पास भाषा बनाने (या लागू करने) का कोई अन्य विचार नहीं था।
मैकनील

चलना परिवहन का सबसे अच्छा साधन है क्योंकि यह आपको यह सोचने का समय देता है कि क्या आप जहां जा रहे हैं वह वास्तव में इसके लायक है। यह स्वस्थ भी है।
बबलू

6

मैंने एक बार व्यावसायिक अनुप्रयोग के लिए एक पार्सर लिखा है और मैंने yacc का उपयोग किया है । एक प्रतिस्पर्धी प्रोटोटाइप था जहां एक डेवलपर ने सी ++ में हाथ से पूरी बात लिखी थी और इसने लगभग पांच बार धीमी गति से काम किया था।

इस पार्सर के लिए लेसर के रूप में, मैंने इसे पूरी तरह से हाथ से लिखा था। यह लिया - क्षमा करें, यह लगभग 10 साल पहले था, इसलिए मुझे यह ठीक से याद नहीं है - सी में लगभग 1000 लाइनें ।

लेसर को हाथ से लिखने का कारण पार्सर का इनपुट व्याकरण था। यह एक आवश्यकता थी, कुछ मेरे पार्सर कार्यान्वयन का अनुपालन करना था, जैसा कि मैंने कुछ डिज़ाइन किया था। (बेशक मैंने इसे अलग तरह से डिज़ाइन किया होगा। और बेहतर!) व्याकरण गंभीर रूप से संदर्भ-निर्भर था और यहां तक ​​कि कुछ स्थानों पर शब्दार्थ पर निर्भर था। उदाहरण के लिए एक अर्धविराम एक स्थान पर एक टोकन का हिस्सा हो सकता है, लेकिन एक अलग जगह में एक विभाजक - कुछ तत्व की एक अर्थपूर्ण व्याख्या के आधार पर जो पहले से बाहर था। इसलिए, मैंने हाथ से लिखे गए लेसर में इस तरह के शब्दार्थ पर निर्भरता को "दफन" कर दिया और मुझे काफी सीधी बीएनएफ के साथ छोड़ दिया जो कि याक में लागू करना आसान था।

Macneil के जवाब में जोड़ा गया : yacc एक बहुत शक्तिशाली अमूर्तता प्रदान करता है जो प्रोग्रामर को टर्मिनलों, गैर-टर्मिनलों, प्रस्तुतियों और उस तरह के सामान के संदर्भ में सोचने देता है। साथ ही, yylex()फ़ंक्शन को लागू करते समय, इससे मुझे वर्तमान टोकन वापस करने पर ध्यान केंद्रित करने में मदद मिली और इसके बारे में चिंता नहीं हुई कि इसके पहले या बाद में क्या था। सी ++ प्रोग्रामर ने चरित्र स्तर पर काम किया, इस तरह के अमूर्तता के लाभ के बिना और अधिक जटिल और कम कुशल एल्गोरिदम बनाने के लिए समाप्त हो गया। हमने निष्कर्ष निकाला कि धीमी गति का सी ++ या किसी भी पुस्तकालय से कोई लेना-देना नहीं था। हमने मेमोरी में लोड की गई फ़ाइलों के साथ शुद्ध पार्सिंग गति को मापा; अगर हमारे पास फ़ाइल बफरिंग की समस्या थी, तो इसे हल करने के लिए yacc हमारी पसंद का उपकरण नहीं होगा।

इसके अलावा ADD करना चाहते हैं : यह सामान्य रूप से पार्सर्स लिखने के लिए एक नुस्खा नहीं है, यह एक उदाहरण है कि यह एक विशेष स्थिति में कैसे काम करता है।


मैं पाँच बार धीमी सी + + हाथ से कार्यान्वयन के बारे में उत्सुक हूं: शायद यह खराब फाइल बफरिंग थी? यह एक बड़ा बदलाव ला सकता है।
मैकनील

@ मैकनील: मैं अपने जवाब के लिए एक अतिरिक्त पोस्ट करने जा रहा हूं; टिप्पणी बहुत लंबी है।
अजहेग्लोव

1
++ अच्छा अनुभव। मैं प्रदर्शन पर बहुत अधिक भार नहीं डालूंगा। अन्यथा अच्छे कार्यक्रमों के लिए यह आसान है कि कुछ मूर्खतापूर्ण और अनावश्यक द्वारा धीमा किया जाए। मैंने यह जानने के लिए पर्याप्त पुनरावर्ती-वंशीय पार्सर लिखे हैं कि क्या नहीं करना है, इसलिए मुझे संदेह है कि अगर कुछ बहुत तेज है। आखिरकार, पात्रों को पढ़ने की आवश्यकता है। मुझे संदेह है कि टेबल बंद करने वाले पार्सर्स थोड़े धीमे होंगे, लेकिन शायद नोटिस करने के लिए पर्याप्त नहीं है।
माइक डनलैवी

3

यह पूरी तरह से निर्भर करता है कि आपको क्या करना है। क्या आप अपने खुद के रोल को तेजी से रोल कर सकते हैं जितना कि आप एक लक्सर के सीखने की अवस्था को मार सकते हैं? क्या सामान को स्थिर रूप से पार्स किया जा सकता है जो आपको बाद में निर्णय पर पछतावा नहीं होगा? क्या आप मौजूदा कार्यान्वयन को अत्यधिक जटिल पाते हैं? यदि ऐसा है, तो मज़े से अपनी भूमिका निभाएँ, लेकिन केवल तभी जब आप सीखने की अवस्था को कम नहीं कर रहे हों।

हाल ही में, मैं वास्तव में नींबू के पार्सर की तरह आया हूं , जो यकीनन सबसे सरल और सबसे आसान है जिसका मैंने कभी उपयोग किया है। चीजों को बनाए रखना आसान बनाने के लिए, मैं ज्यादातर जरूरतों के लिए इसका इस्तेमाल करता हूं। SQLite इसे और साथ ही कुछ अन्य उल्लेखनीय परियोजनाओं का उपयोग करता है।

लेकिन, मुझे लेक्सर्स में बिल्कुल भी दिलचस्पी नहीं है, इससे परे वे मेरे रास्ते में नहीं आते हैं जब मुझे एक (इसलिए, नींबू) का उपयोग करने की आवश्यकता होती है। आप हो सकते हैं, और यदि हां, तो क्यों नहीं? मुझे लगता है कि आप मौजूद हैं का उपयोग करके वापस आ जाएंगे, लेकिन अगर आपको चाहिए तो खुजली खरोंचें :)


3
+1 के लिए "क्या आप अपने खुद के रोल को तेजी से रोल कर सकते हैं जितना कि आप एक लक्सर के सीखने की अवस्था को मार सकते हैं?"
बोबाह 9:10

हाँ, अच्छी बात है।
मनिएरो

3

यह इस बात पर निर्भर करता है कि आपका लक्ष्य क्या है।

क्या आप सीखने की कोशिश कर रहे हैं कि पार्सर / कंपाइलर कैसे काम करते हैं? फिर खरोंच से अपना खुद का लिखें। एकमात्र तरीका है कि आप वास्तव में सभी इन्स और बाहरी लोगों की सराहना करना सीखेंगे कि वे क्या कर रहे हैं। मैं पिछले कुछ महीनों से एक लिख रहा हूं, और यह एक दिलचस्प और मूल्यवान अनुभव रहा है, वास्तव में 'आह, तो भाषा एक्स ऐसा क्यों करती है ...' क्षण।

क्या आपको एक समय सीमा पर एक आवेदन के लिए जल्दी से कुछ डालने की ज़रूरत है? तो शायद एक पार्सर उपकरण का उपयोग करें।

क्या आपको कुछ ऐसा चाहिए जो आप अगले 10, 20, शायद 30 वर्षों में भी बढ़ाना चाहें? अपना खुद का लिखें, और अपना समय लें। यह अच्छी तरह से इसके लायक होगा।


यह संकलकों पर मेरा पहला काम है, मैं सीख रहा हूं / प्रयोग कर रहा हूं और इसे लंबे समय तक बनाए रखने का मेरा इरादा है।
मनिएरो

3

क्या आपने मार्टिन फाउलर भाषा कार्यक्षेत्र दृष्टिकोण पर विचार किया है ? लेख से उद्धरण

सबसे स्पष्ट परिवर्तन जो एक भाषा कार्यक्षेत्र समीकरण को बनाता है वह बाहरी डीएसएल बनाने में आसानी है। अब आपको पार्सर लिखना नहीं होगा। आपको सार सिंटैक्स को परिभाषित करना होगा - लेकिन यह वास्तव में एक बहुत ही सीधा डेटा मॉडलिंग कदम है। इसके अलावा आपके डीएसएल को एक शक्तिशाली आईडीई मिलता है - हालांकि आपको उस संपादक को परिभाषित करने में कुछ समय बिताना होगा। जनरेटर अभी भी कुछ ऐसा है जो आपको करना है, और मेरी समझ यह है कि यह पहले से कहीं ज्यादा आसान नहीं है। लेकिन फिर एक अच्छे और सरल डीएसएल के लिए जनरेटर का निर्माण करना व्यायाम के सबसे आसान हिस्सों में से एक है।

इसे पढ़कर, मैं कहूंगा कि आपके खुद के पार्सर लिखने के दिन खत्म हो गए हैं और यह उपलब्ध पुस्तकालयों में से एक का उपयोग करना बेहतर है। एक बार जब आप पुस्तकालय में महारत हासिल कर लेते हैं तो भविष्य में आपके द्वारा बनाए गए सभी डीएसएल उस ज्ञान से लाभान्वित होते हैं। इसके अलावा, दूसरों को पार्स करने के लिए आपके दृष्टिकोण को सीखना नहीं है।

टिप्पणी को कवर करने के लिए संपादित करें (और संशोधित प्रश्न)

खुद के रोल करने के फायदे

  1. आप खुद ही पार्लर होंगे और समस्याओं की एक जटिल श्रृंखला के माध्यम से सोच के सभी सुंदर अनुभव प्राप्त करेंगे
  2. आप कुछ विशेष के साथ आ सकते हैं जो किसी और ने नहीं सोचा है (संभावना नहीं है लेकिन आप एक चतुर चाप की तरह लगते हैं)
  3. यह आपको एक दिलचस्प समस्या से घेरे रखेगा

तो संक्षेप में, आपको अपना खुद का रोल करना चाहिए जब आप वास्तव में एक गंभीर रूप से कठिन समस्या के आंत्र में गहरी हैक करना चाहते हैं जिसे आप मास्टर करने के लिए दृढ़ता से प्रेरित महसूस करते हैं।

किसी और के पुस्तकालय का उपयोग करने के लाभ

  1. आप पहिया को फिर से आविष्कार करने से बचेंगे (प्रोग्रामिंग में एक आम समस्या जिससे आप सहमत होंगे)
  2. आप अंतिम परिणाम (आप नई भाषा चमकदार) पर ध्यान केंद्रित कर सकते हैं और यह कैसे पार्स किया जाता है आदि के बारे में बहुत ज्यादा चिंता न करें
  3. आप अपनी भाषा को बहुत तेज़ी से देखेंगे (लेकिन आपका इनाम कम होगा 'क्योंकि यह सब आप नहीं थे)

इसलिए, यदि आप एक त्वरित अंतिम परिणाम चाहते हैं तो किसी और के पुस्तकालय का उपयोग करें।

कुल मिलाकर, यह इस विकल्प पर निर्भर करता है कि आप समस्या का कितना समाधान करना चाहते हैं, और इस प्रकार समाधान। अगर आप यह सब चाहते हैं तो अपना रोल करें।


यह सोच का एक बढ़िया विकल्प है।
मनेरियो

1
@bigown ने आपके प्रश्न का बेहतर उत्तर देने के लिए संपादन किया
गैरी रोवे

2

अपने खुद के लिखने के लिए बड़ा फायदा यह है कि आप जानेंगे कि कैसे लिखा जाए। याक जैसे उपकरण का उपयोग करने का बड़ा फायदा यह है कि आप जानेंगे कि उपकरण का उपयोग कैसे किया जाता है। मैं शुरुआती अन्वेषण के लिए treetop का प्रशंसक हूं ।


विशेष रूप से सहायक नहीं। आपने यह भी कहा होगा कि, “ड्राइविंग सीखने का लाभ यह है कि आप ड्राइव कर सकते हैं। बाइक
चलाना

1

क्यों नहीं एक खुले स्रोत पार्सर जनरेटर कांटा और इसे अपना बना लें? यदि आप पार्सर जनरेटर का उपयोग नहीं करते हैं, तो आपको बनाए रखने के लिए कोड बहुत कठिन होगा, यदि आपने अपनी भाषा के सिंटैक्स में बड़े बदलाव किए हैं।

मेरे पार्सर में, मैंने नियमित अभिव्यक्ति (मेरा मतलब है, पर्ल-स्टाइल) का उपयोग टोकन के लिए किया, और कोड पठनीयता को बढ़ाने के लिए कुछ सुविधा कार्यों का उपयोग किया। हालांकि, एक पार्सर उत्पन्न कोड तेजी से राज्य टेबल और लंबे समय तक बना कर किया जा सकता है switch- caseरों है, जो जब तक आप स्रोत कोड आकार में वृद्धि हो सकती .gitignoreहै उन्हें।

यहाँ मेरे कस्टम लिखित पार्सर के दो उदाहरण हैं:

https://github.com/SHiNKiROU/DesignScript - एक मूलभूत बोली, क्योंकि मैं सरणी संकेतन में लुकाहेड्स लिखने के लिए बहुत आलसी था, मैंने त्रुटि संदेश की गुणवत्ता का त्याग किया था https://github.com/SHiNKROROU/ExprParser - एक सूत्र कैलकुलेटर। अजीब मेटाप्रोग्रामिंग ट्रिक्स पर ध्यान दें


0

"क्या मुझे इस कोशिश और परीक्षण किए गए 'पहिया' का उपयोग करना चाहिए या इसे फिर से मजबूत करना चाहिए?"


1
यह "पहिया" क्या है जो आप बोलते हैं? ;-)
जेसन व्हाइटहॉर्न

इस प्रश्न के बारे में IMO एक अच्छी राय नहीं है। यह सिर्फ एक सामान्य सलाह है जो विशिष्ट मामले के लिए उपयुक्त नहीं है। मुझे संदेह होने लगा है कि समय-समय पर इस क्षेत्र 51 . stackexchange.com/proposals/7848 प्रस्ताव को बंद कर दिया गया था।
मनिएरो

2
यदि पहिये का पुन: आविष्कार नहीं किया गया था, तो हम प्रतिदिन 100 किमी प्रति घंटे की रफ्तार से यात्रा नहीं करेंगे - जब तक कि आप लकड़ी के एक्सल पर चट्टान की बड़ी भारी गांठ का सुझाव नहीं देंगे, तब तक उपयोग किए जाने वाले आधुनिक टायरों के कई प्रकारों से बेहतर है इतने सारे वाहन?
पीटर बॉटन

यह एक मान्य राय है, और यह सही अंतर्ज्ञान है। मैं सोच रहा हूं कि यह उत्तर अधिक उपयोगी हो सकता है यदि आप विशिष्ट लाभ या नुकसान की सूची बना सकते हैं, क्योंकि इस तरह की बात पूरी तरह से परिस्थितियों पर निर्भर करती है।
मैकनील

@ पेटर: किसी चीज़ को फिर से मजबूत करना एक बात है (इसका मतलब है कि यह पूरी तरह से अलग है) लेकिन अतिरिक्त आवश्यकताओं को पूरा करने के लिए मौजूदा समाधान को परिष्कृत करना बेहतर है। मैं सभी 'सुधार' के लिए हूं, लेकिन पहले से हल की गई समस्या के लिए ड्राइंग बोर्ड पर वापस जाना गलत लगता है।
JBRWilkinson
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.