मुझे पार्सर के लिए एक व्याकरण कैसे निर्दिष्ट करना चाहिए?


12

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

मुझे उम्मीद नहीं है कि एक व्याकरण को निर्दिष्ट करने की प्रक्रिया को स्वचालित करने के लिए एक एल्गोरिथ्म है, लेकिन मुझे आशा है कि समस्या को संरचना करने के तरीके हैं जो मेरे वर्तमान दृष्टिकोण के अनुमान और परीक्षण और त्रुटि को खत्म करते हैं।

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

मैं मुख्य रूप से एक व्याकरण निर्दिष्ट करने की समस्या में दिलचस्पी रखता हूं जो औपचारिक रूप से ठोस उदाहरणों (सकारात्मक और नकारात्मक) के संग्रह का प्रतिनिधित्व करता है। यह नया सिंटेक्स डिजाइन करने की समस्या से अलग है । इस भेद को इंगित करने के लिए मैकनील का धन्यवाद।

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

पार्सर के लिए व्याकरण कैसे निर्दिष्ट किया जाता है? क्या एक पुस्तक या संदर्भ है जो कि सर्वोत्तम प्रथाओं, डिजाइन विधियों और किसी पार्सर के लिए एक व्याकरण निर्दिष्ट करने के बारे में अन्य उपयोगी जानकारी का वर्णन करने के लिए वास्तविक मानक है? पार्सर व्याकरण के बारे में पढ़ते समय मुझे किन बिंदुओं पर ध्यान देना चाहिए?


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

8
@kjo यह दुर्भाग्यपूर्ण है। यदि आप जो कुछ भी पूछ रहे हैं वह संदर्भों की एक सूची है, तो आप स्टैक एक्सचेंज को उसकी पूरी क्षमता का उपयोग नहीं कर रहे हैं। आपकी मेटा-समस्या साइट का उपयोग नहीं कर रही है। स्टैक एक्सचेंज पर सूची प्रश्न लगभग सार्वभौमिक रूप से हतोत्साहित किए जाते हैं क्योंकि वे प्रश्नोत्तर मॉडल में बहुत अच्छी तरह से फिट नहीं होते हैं। मैं आपको उन प्रश्नों को पूछने की दिशा में अपनी मानसिकता बदलने की सलाह देता हूं जिनमें उत्तर हैं, आइटम नहीं, विचार या राय
एडम लेअर

3
@kjo यह निश्चित रूप से एक सवाल है, लेकिन स्टैक एक्सचेंज पर पूछने का सही प्रश्न नहीं है । एसई यहाँ संदर्भों की सूची बनाने के लिए नहीं है। यह करने के लिए यहाँ है हो संदर्भ। कृपया अधिक विस्तृत विवरण के लिए मेरी टिप्पणी से जुड़ी मेटा पोस्ट के माध्यम से पढ़ें ।
एडम लेअर

5
@kjo: कृपया निराश मत हो! अन्ना के संपादन ने आपके प्रश्न का मूल और दिल रखा और उसने आपके प्रश्न को उस रूप में और अधिक बनाने में मदद की, जिसकी हम प्रोग्रामर से उम्मीद करते हैं। मैं कोई निश्चित संदर्भ नहीं जानता जो आप चाहते हैं, लेकिन एक उत्तर प्रदान करने में सक्षम था। [OTOH, क्या मैं इस तरह के एक संदर्भ को जानता था, मैंने निश्चित रूप से इसे शामिल किया होगा।] हम मेरे जैसे जवाबों को और अधिक प्रोत्साहित करना चाहते हैं, क्योंकि इस विशिष्ट मामले में, मुझे विश्वास नहीं है कि आप जो चाहते हैं, उसके लिए एक संदर्भ है दूसरों से बात करने का अनुभव।
मैक्नील

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

जवाबों:


12

नमूना फ़ाइलों से आपको उन उदाहरणों से सामान्यीकरण करना चाहते हैं, इसके आधार पर निर्णय लेने की आवश्यकता होगी। मान लें कि आपके पास निम्नलिखित तीन नमूने थे: (प्रत्येक एक अलग फ़ाइल है)

f() {}
f(a,b) {b+a}
int x = 5;

आप दो नमूनों को तुच्छ रूप से निर्दिष्ट कर सकते हैं जो इन नमूनों को स्वीकार करेंगे:

तुच्छ व्याकरण एक:

start ::= f() {} | f(a,b) {b+a} | int x = 5;

तुच्छ व्याकरण दो:

start ::= tokens
tokens ::= token tokens | <empty>
token ::= identifier | literal | { | } | ( | ) | , | + | = | ;

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

इसलिए, आपको जिस प्रक्रिया का पालन करने की आवश्यकता होगी, वह उच्च स्तर पर शुरू करना है और यह तय करना है कि "मैं कितने प्रत्येक उदाहरण की अनुमति देना चाहता हूं?" यदि एक वाक्य रचना का निर्माण किसी भी संख्या को दोहराने के लिए समझ में आता है, जैसे कि methodएक वर्ग में, आप इस फॉर्म के साथ एक नियम चाहते हैं:

methods ::= method methods | empty

जो EBNF में बेहतर बताया गया है :

methods ::= {method}

यह संभवतः स्पष्ट होगा जब आप केवल शून्य या एक इंस्टेंसेस चाहते हैं (जिसका अर्थ है कि निर्माण वैकल्पिक है, जैसे कि extendsजावा क्लास के लिए क्लॉज़ के साथ ), या जब आप एक या एक से अधिक इंस्टेंसेस की अनुमति देना चाहते हैं (जैसा कि एक घोषणा में एक वैरिएबल इनिशियलाइज़र के साथ होता है) )। आपको तत्वों के बीच एक विभाजक की आवश्यकता जैसे मुद्दों पर ध्यान देने की आवश्यकता होगी (जैसे कि ,एक तर्क सूची में), प्रत्येक तत्व के बाद एक टर्मिनेटर की आवश्यकता होती है ( ;अलग बयानों के साथ), या कोई विभाजक या टर्मिनेटर की आवश्यकता नहीं है (मामले के रूप में) एक कक्षा में विधियों के साथ)।

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

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

इस बिंदु पर, आपको कुछ नियमों को लागू करने के लिए व्याकरणिक रूप से प्रलोभन दिया जा सकता है, एक नियम जैसे कि एक Personउम्र निर्दिष्ट की गई है आप अपनी जन्मतिथि को भी निर्दिष्ट नहीं होने देना चाहते हैं। जब आप ऐसा करने के लिए अपने व्याकरण का निर्माण कर सकते हैं, तो आपको सब कुछ पार्स होने के बाद "अर्थिक चेक" पास के साथ इसे लागू करना आसान हो सकता है। यह व्याकरण को सरल रखता है और, मेरी राय में, नियम के उल्लंघन के लिए बेहतर त्रुटि संदेश बनाता है।


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

10

मैं सीख सकता हूं कि एक पार्सर के लिए व्याकरण को कैसे निर्दिष्ट किया जाए?

अधिकांश पार्सर जनरेटर के लिए, यह आमतौर पर बैकस-नौर के <nonterminal> ::= expressionप्रारूप का कुछ संस्करण है । मैं इस धारणा पर जा रहा हूं कि आप ऐसा कुछ उपयोग कर रहे हैं और हाथ से अपने पार्सर बनाने की कोशिश नहीं कर रहे हैं। यदि आप एक प्रारूप के लिए एक पार्सर का उत्पादन कर सकते हैं जहां आपको सिंटैक्स दिया गया है (मैंने नीचे एक नमूना समस्या शामिल की है), व्याकरण निर्दिष्ट करना आपकी समस्या नहीं है।

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

... मुझे यकीन नहीं है कि व्याकरण मैं आया हूं जो अच्छा है ("अच्छे" के किसी भी उचित उपाय से)।

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

सान्द्र परिस्थितियों में, एक व्याकरण का परीक्षण कुछ और परीक्षण करने जैसा है: आपको नॉनटर्मिनल के सभी वेरिएंट (और टर्मिनलों, यदि वे एक लेसर द्वारा उत्पन्न किए जा रहे हैं) का अभ्यास करने के लिए पर्याप्त परीक्षण मामलों के साथ आना होगा।


नमूना समस्या

एक व्याकरण लिखें जो पाठ फ़ाइलों को सूची में सम्‍मिलित करेगा जो नीचे दिए गए नियमों द्वारा परिभाषित है:

  • एक सूची में शून्य या अधिक चीजें शामिल हैं
  • एक चीज में एक पहचानकर्ता , एक खुला ब्रेस, एक आइटम सूची और एक बंद ब्रेस होता है।
  • एक _item_list_ में शून्य या अधिक आइटम शामिल हैं
  • एक पहचानकर्ता , एक समान चिह्न, एक अन्य पहचानकर्ता और अर्धविराम की एक वस्तु की कमी ।
  • एक पहचानकर्ता एक या एक से अधिक वर्ण AZ, az, 0-9 या अंडरस्कोर का अनुक्रम है।
  • व्हाट्सएप को नजरअंदाज किया जाता है।

इनपुट का उदाहरण (सभी मान्य):

clank { foo = bar; baz = bear; }
clunk {
    quux =bletch;
    281_apple = OU812;
    He_Eats=Asparagus ; }

2
और "बैकस-नौर के कुछ संस्करण" का उपयोग करना सुनिश्चित करें और बीएनएफ ही नहीं। बीएनएफ एक व्याकरण को व्यक्त कर सकता है, लेकिन यह बहुत ही सामान्य अवधारणाएं बनाता है, जैसे कि सूचियां, जितना वे होना चाहिए उससे कहीं अधिक जटिल। EBNF जैसे विभिन्न उन्नत संस्करण हैं, जो इन मुद्दों पर सुधार करते हैं।
मेसन व्हीलर

7

Macneil और Blrfl के उत्तर बहुत अच्छे हैं। मैं सिर्फ प्रक्रिया की शुरुआत के बारे में कुछ टिप्पणियां जोड़ना चाहता हूं।

एक वाक्य रचना एक कार्यक्रम का प्रतिनिधित्व करने का एक तरीका है । तो आपकी भाषा का वाक्य विन्यास इस प्रश्न के आपके उत्तर से निर्धारित होना चाहिए: एक कार्यक्रम क्या है?

आप कह सकते हैं कि एक कार्यक्रम कक्षाओं का एक संग्रह है। ठीक है, जो हमें देता है

program ::= class*

एक प्रारंभिक बिंदु के रूप में। या आपको इसे लिखना पड़ सकता है

program ::= ε
         |  class program

अब, एक वर्ग क्या है? इसे एक नाम मिला है; एक वैकल्पिक सुपरक्लास विनिर्देश; और कंस्ट्रक्टर, विधि और क्षेत्र घोषणाओं का एक गुच्छा। साथ ही, आपको एक एकल (असंदिग्ध) इकाई में समूह बनाने के कुछ तरीके की आवश्यकता होती है, और इसमें प्रयोज्यता के लिए कुछ रियायतें शामिल होनी चाहिए (उदाहरण के लिए, इसे आरक्षित शब्द के साथ टैग करें class)। ठीक है:

class ::= "class" identifier extends-clause? "{" class-member-decl * "}"

यह एक संकेतन ("ठोस वाक्यविन्यास") है जिसे आप चुन सकते हैं। या आप इस पर आसानी से निर्णय ले सकते हैं:

class ::= "(" "class" identifier extends-clause "(" class-member-decl* ")" ")"

या

class ::= "class" identifier "=" "CLASS" extends-clause? class-member-decl* "END"

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

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

अंत में, व्याकरण में अपनी भाषा के बारे में सब कुछ दर्शाने की कोशिश न करें। उदाहरण के लिए, आप कुछ प्रकार के अगम्य कोड (जैसे, returnजावा के बाद, एक स्टेटमेंट के बाद ) को मना कर सकते हैं । आपको संभवतः व्याकरण में रटना करने की कोशिश नहीं करनी चाहिए, क्योंकि आप या तो चीजों को याद करेंगे (वूप्स, क्या होगा अगर returnब्रेसिज़ में है, या क्या होगा यदि दोनों ifस्टेटमेंट की शाखाएं वापस आ जाती हैं?) या आप अपने व्याकरण के तरीके को भी जटिल बना देंगे। प्रबंधन करना। यह एक संदर्भ-संवेदनशील बाधा है; इसे एक अलग पास के रूप में लिखें। एक संदर्भ-संवेदनशील बाधा का एक और बहुत ही सामान्य उदाहरण एक प्रकार की प्रणाली है। 1 + "a"यदि आप पर्याप्त परिश्रम करते हैं, तो आप व्याकरण की तरह अभिव्यक्तियों को अस्वीकार कर सकते हैं , लेकिन आप अस्वीकार नहीं कर सकते हैं 1 + x(जहां xप्रकार स्ट्रिंग है)। इसलिएव्याकरण में आधे पके हुए प्रतिबंधों से बचें और उन्हें अलग पास के रूप में सही ढंग से करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.