लेक्सर्स केवल साधारण पार्सर हैं जो मुख्य पार्सर के लिए प्रदर्शन अनुकूलन के रूप में उपयोग किए जाते हैं। यदि हमारे पास एक लेक्सर है, तो लेक्सर और पार्सर पूरी भाषा का वर्णन करने के लिए एक साथ काम करते हैं। जिन पार्सरों के पास एक अलग लेक्सिंग चरण नहीं होता है, उन्हें कभी-कभी "स्कैनर रहित" कहा जाता है।
लेक्सर्स के बिना, पार्सर को चरित्र-दर-वर्ण आधार पर संचालित करना होगा। चूंकि पार्सर को हर इनपुट आइटम के बारे में मेटाडेटा को स्टोर करना पड़ता है, और हर इनपुट आइटम राज्य के लिए तालिकाओं की पूर्व-गणना करना पड़ सकता है, इससे बड़े इनपुट आकारों के लिए अस्वीकार्य मेमोरी खपत हो सकती है। विशेष रूप से, हमें सार सिंटैक्स ट्री में एक अलग नोड प्रति चरित्र की आवश्यकता नहीं है।
चूँकि एक चरित्र-दर-वर्ण आधार पर पाठ काफी अस्पष्ट है, इसलिए इससे बहुत अधिक अस्पष्टता होगी जो कि संभालने के लिए कष्टप्रद है। एक नियम की कल्पना करो R → identifier | "for " identifier
। जहाँ पहचानकर्ता ASCII अक्षरों से बना है। यदि मैं अस्पष्टता से बचना चाहता हूं, तो मुझे यह निर्धारित करने के लिए अब 4-वर्ण लुकहेड की आवश्यकता है जो कि विकल्प चुना जाना चाहिए। एक लेसर के साथ, पार्सर को केवल यह जांचना होगा कि उसके पास एक IDENTIFIER है या टोकन - 1-टोकन लुकहेड है।
दो स्तरीय व्याकरण।
लेक्सर्स इनपुट वर्णमाला को अधिक सुविधाजनक वर्णमाला में अनुवाद करके काम करते हैं।
एक स्कैनर रहित पार्सर एक व्याकरण (N, P, P, S) का वर्णन करता है, जहाँ गैर-टर्मिनल N व्याकरण में नियमों के बाएँ हाथ के भाग होते हैं, वर्णमाला eg उदाहरण के लिए ASCII वर्ण हैं, प्रस्तुतियों P व्याकरण में नियम हैं। , और प्रारंभ प्रतीक S, पार्सर के शीर्ष स्तर का नियम है।
लेक्सर अब टोकन a, b, c,… की वर्णमाला को परिभाषित करता है। यह मुख्य पार्सर को इन टोकन को वर्णमाला के रूप में उपयोग करने की अनुमति देता है: a = {a, b, c,…}। लेसर के लिए, ये टोकन गैर-टर्मिनल हैं, और प्रारंभ नियम S L S S L → ε है | ए एस | बी एस | सी एस | …, यह है: टोकन का कोई भी क्रम। लेक्सर व्याकरण के नियम इन टोकन को बनाने के लिए आवश्यक सभी नियम हैं।
प्रदर्शन का लाभ नियमित भाषा के रूप में लेक्सर के नियमों को व्यक्त करने से आता है । इन्हें संदर्भ-मुक्त भाषाओं की तुलना में बहुत अधिक कुशलता से प्राप्त किया जा सकता है। विशेष रूप से, नियमित भाषाओं को O (n) स्थान और O (n) समय में पहचाना जा सकता है। व्यवहार में, एक कोड जनरेटर इस तरह के एक लेसर को अत्यधिक कुशल कूद तालिकाओं में बदल सकता है।
अपने व्याकरण से टोकन निकालना।
अपने उदाहरण को छूने के लिए: digit
और string
नियम एक चरित्र-दर-वर्ण स्तर पर व्यक्त किए जाते हैं। हम उन्हें टोकन के रूप में इस्तेमाल कर सकते हैं। बाकी व्याकरण बरकरार है। यहाँ लेक्सर व्याकरण, एक रेखीय व्याकरण के रूप में लिखा गया है ताकि यह स्पष्ट हो सके कि यह नियमित है:
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
string = '"' , string-rest ;
string-rest = '"' | STRING-CHAR, string-rest ;
STRING-CHAR = ? all visible characters ? - '"' ;
लेकिन चूंकि यह नियमित है, इसलिए हम आमतौर पर टोकन सिंटैक्स को व्यक्त करने के लिए नियमित अभिव्यक्ति का उपयोग करेंगे। यहाँ रेगेक्स के रूप में उपरोक्त टोकन परिभाषाएँ हैं, जो .NET वर्ण वर्ग बहिष्करण सिंटैक्स और पोसिक्स वर्णक्रमीयता का उपयोग करके लिखी गई हैं:
digit ~ [0-9]
string ~ "[[:print:]-["]]*"
मुख्य पार्सर के लिए व्याकरण में तब शेष नियम होते हैं जो लेक्सर द्वारा नियंत्रित नहीं किए जाते हैं। आपके मामले में, यह सिर्फ:
input = digit | string ;
जब लेक्सर्स का उपयोग आसानी से नहीं किया जा सकता है।
भाषा को डिजाइन करते समय, हम आमतौर पर इस बात का ध्यान रखेंगे कि व्याकरण को स्पष्ट रूप से एक लेक्सर स्तर और एक पार्सर स्तर में अलग किया जा सकता है, और यह कि लेक्सर स्तर एक नियमित भाषा का वर्णन करता है। यह हमेशा संभव नहीं है।
जब भाषाओं को एम्बेड कर रहे हैं। कुछ भाषाएं आपको कोड को स्ट्रिंग्स में इंटरपोल करने की अनुमति देती हैं "name={expression}"
:। अभिव्यक्ति सिंटैक्स संदर्भ-मुक्त व्याकरण का हिस्सा है और इसलिए इसे नियमित अभिव्यक्ति द्वारा टोकन नहीं किया जा सकता है। इसे हल करने के लिए, हम या तो लेसर के साथ पार्सर को फिर से जोड़ते हैं, या हम अतिरिक्त टोकन पेश करते हैं जैसे STRING-CONTENT, INTERPOLATE-START, INTERPOLATE-END
। एक स्ट्रिंग के लिए व्याकरण नियम तब लग सकता है String → STRING-START STRING-CONTENTS { INTERPOLATE-START Expression INTERPOLATE-END STRING-CONTENTS } STRING-END
:। बेशक अभिव्यक्ति में अन्य तार हो सकते हैं, जो हमें अगली समस्या की ओर ले जाता है।
जब टोकन एक दूसरे को शामिल कर सकते हैं। सी जैसी भाषाओं में, कीवर्ड पहचानकर्ताओं से अप्रभेद्य होते हैं। यह पहचानकर्ता से अधिक कीवर्ड को प्राथमिकता देकर लेक्सर में हल किया जाता है। ऐसी रणनीति हमेशा संभव नहीं होती है। एक कॉन्फ़िगर फ़ाइल की कल्पना करें कि कहां Line → IDENTIFIER " = " REST
, जहां कोई भी रेखा के अंत तक कोई भी चरित्र है, भले ही बाकी एक पहचानकर्ता की तरह दिखता हो। एक उदाहरण रेखा होगी a = b c
। लेक्सर वास्तव में गूंगा है और यह नहीं जानता है कि टोकन किस क्रम में हो सकता है। इसलिए यदि हम REST के ऊपर IDENTIFIER को प्राथमिकता देते हैं, तो लेक्सर हमें देगा IDENT(a), " = ", IDENT(b), REST( c)
। यदि हम IDENTIFIER पर बाकी को प्राथमिकता देते हैं, तो लेक्सर हमें दे देगा REST(a = b c)
।
इसे हल करने के लिए, हमें लेसर को पार्सर के साथ फिर से जोड़ना होगा। लेसर को आलसी बनाकर अलगाव को कुछ हद तक बनाए रखा जा सकता है: हर बार जब पार्सर को अगले टोकन की आवश्यकता होती है, तो वह इसे लेसर से अनुरोध करता है और लेक्सर को स्वीकार्य टोकन का सेट बताता है। प्रभावी रूप से, हम प्रत्येक स्थिति के लिए लेक्सर व्याकरण के लिए एक नया शीर्ष-स्तरीय नियम बना रहे हैं। यहाँ, यह कॉल में परिणाम देगा nextToken(IDENT), nextToken(" = "), nextToken(REST)
, और सब कुछ ठीक काम करता है। इसके लिए एक ऐसे पार्सर की आवश्यकता होती है, जो प्रत्येक स्थान पर स्वीकार्य टोकन का पूरा सेट जानता हो, जो एलआर की तरह नीचे-ऊपर के पार्सर का अर्थ रखता है।
जब लेसर को राज्य बनाए रखना है। उदाहरण के लिए पायथन भाषा कोड को कर्ली ब्रेसिज़ से नहीं, बल्कि इंडेंटेशन द्वारा ब्लॉक करती है। एक व्याकरण के भीतर लेआउट-संवेदनशील सिंटैक्स को संभालने के तरीके हैं, लेकिन उन तकनीकों को पायथन के लिए ओवरकिल किया जाता है। इसके बजाय, lexer प्रत्येक पंक्ति के इंडेंटेशन की जांच करता है, और एक नया इंडेंट ब्लॉक पाए जाने पर INDENT टोकन का उत्सर्जन करता है, और ब्लॉक समाप्त होने पर DEDENT टोकन। यह मुख्य व्याकरण को सरल करता है क्योंकि यह अब दिखावा कर सकता है कि टोकन घुंघराले ब्रेसिज़ की तरह हैं। लेक्सर को अब राज्य बनाए रखने की आवश्यकता है: वर्तमान इंडेंटेशन। इसका मतलब है कि लेक्सर तकनीकी रूप से अब एक नियमित भाषा का वर्णन नहीं करता है, लेकिन वास्तव में एक संदर्भ-संवेदनशील भाषा है। सौभाग्य से यह अंतर व्यवहार में प्रासंगिक नहीं है, और पायथन का लेक्सर अभी भी O (n) समय में काम कर सकता है।