
PLY क्या है?
PLY अभिव्यक्ति का पहला अक्षर संक्षिप्त नाम है:
P ython
L ex-
Y acc।
वास्तव में, यह एक सुंदर आवरण में अजगर में लेक्स और याक के उपयोगिताओं का बंदरगाह है।
प्लाई के साथ काम करना बहुत सरल है और इसका उपयोग शुरू करने के लिए प्रवेश सीमा लगभग शून्य है।
यह शुद्ध अजगर में लिखा गया है और एक
एलएएलआर (1) पार्सर है , लेकिन कौन परवाह करता है?
मैं स्वभाव से एक अभ्यासी हूँ (आप में से अधिकांश की तरह) तो हम युद्ध में चले गए!
हम क्या करेंगे?
साइट में एक और कैलकुलेटर लिखने
का एक उदाहरण है , इसलिए हम इसे दोहराएंगे नहीं। और चलो PHP के एक बहुत ही संकीर्ण सबसेट के एक पार्सर की तरह कुछ करते हैं :)
लेख के अंत में हमारा काम इस तरह के उदाहरण के लिए एक वाक्यविन्यास वृक्ष का निर्माण करना है:
<?php $val = 5; $result = substr( "foobar", 2*(7-$val) ); echo " : $result";
उदाहरण बहुत छोटा है और छत से लिया गया है। लेकिन कोड के एक पेड़ का निर्माण करने के लिए आपको बहुत आवश्यकता है और अभियान के लिए हम राज्य के रूप में इस तरह के पीएलवाई तंत्र का उपयोग करते हैं।
लेक्रस
लेक्स एक ऐसी चीज़ है जो टेक्स्ट को किसी भाषा के मूल तत्वों में तोड़ देती है। ठीक है, या मूल तत्वों में पाठ को समूहित करता है। कुछ इस तरह।
बेकार कोड के अलावा हम यहाँ क्या देखते हैं? हम टोकन (मूल तत्व) देखते हैं:
PHP_START - '<? Php'
PHP_VAR - '$ परिणाम'
PHP_EQUAL - '='
PHP_NAME - 'मूल'
PHP_OPEN - '('
PHP_STRING - "फ़ॉबोर", 'यह हमारा परिणाम है: $ परिणाम'
PHP_NUM - '2', '7'
PHP_CLOSE - ')'
PHP_ECHO - "इको"
PHP_COLON - ';'
PHP_COMA - ','
PHP_PLUSMINUS - '-'
PHP_DIVMUL - '*'
PLY में टोकन पर पाठ को पार्स करने के लिए,
ply.lex है । और यह नियमित अभिव्यक्ति के साथ काम करता है।
हम कोड में जो कुछ भी लिखते हैं, उसके बारे में वह बहुत चुस्त हैं इसमें टोकन नामक एक सरणी की उपस्थिति की आवश्यकता होती है।
इस सरणी के प्रत्येक तत्व के लिए, हमें कोड में t_ELEMENT नाम के साथ एक नियमित कोड या फ़ंक्शन होना चाहिए।
वह इतना अयोग्य क्यों है? क्योंकि यह प्रोग्राम के निष्पादन के दौरान सीधे काम नहीं करता है, लेकिन प्रोग्राम के पहले शुरू में केवल एक बार निष्पादित होता है और lextab.py फ़ाइल बनाता है, जो रूपांतरण तालिका और नियमितता का वर्णन करता है। अगली बार जब प्रोग्राम शुरू होता है, तो यह इस फ़ाइल की उपस्थिति के लिए जाँच करता है, और यदि यह मौजूद है, तो यह अब टेबल को फिर से बनाने की कोशिश नहीं करता है, लेकिन उत्पन्न लोगों का उपयोग करता है।
कोड पर वापस जाएं।
यदि PHP सिंटैक्स ऊपर सूचीबद्ध तेरह टोकन तक सीमित था, तो हम निम्नलिखित लिखेंगे:
कोड की टिप्पणियों में, मैंने मोटे तौर पर वर्णन किया कि वहां क्या चल रहा था। मैं केवल इस बात पर ध्यान देता हूं कि टोकन को परिभाषित करने वाले नियमित के बजाय,
आप ऐसे कार्य लिख सकते हैं , जिसमें समान नियमित होंगे, लेकिन इसके अलावा आप अपने स्वयं के कुछ और भी दर्ज कर सकते हैं। उदाहरण के लिए:
def t_PHPVAR(t): r'\$[a-zA-Z]\w*' print ', ' + t.value
वैसे, ऊपर लिखे गए उदाहरण का आउटपुट इस तरह होगा:
लेक्सटन (PHPSTART, '<? Php', 1,1)
लेक्सटन (PHPVAR, '$ वैल', 1,7)
लेक्सटन (PHPEQUAL, '=', 1,12)
लेक्सटन (PHPNUM, '5', 1.14)
लेक्सटन (PHPCOLON, ';', 1.15)
LexToken (PHPVAR, '$ परिणाम', 1.17)
LexToken (PHPEQUAL, '=', 1.25)
LexToken (PHPFUNC, 'मूल', 1.27)
LexToken (PHPOPEN, '(', 1.33)
लेक्सटन (PHPSTRING, '' फोब्बर '', 1.35)
लेक्सटन (PHPCOMA, ',', 1.43)
लेक्सटन (PHPNUM, '2', 1.45)
लेक्सटन (DIVMUL, '*', 1.46)
LexToken (PHPOPEN, '(', 1.47)
लेक्सटन (PHPNUM, '7', 1.48)
लेक्सटन (PLUSMINUS, '-', 1.49)
लेक्सटन (PHPVAR, '$ वैल', 1.50)
LexToken (PHPCLOSE, ')', 1.54)
LexToken (PHPCLOSE, ')', 1.56)
लेक्सटन (PHPCOLON, ';', 1.57)
LexToken (PHPFUNC, 'इको', 1.59)
LexToken (PHPSTRING, "" \ xd1 \ x8d \ xd1 \ x82 \ xd0 \ xd0 \ xd0 \ xd0 \ xb0 \ xd1 \ xd1 \ xd1 \ xd1 \ xd5 \ xd5 \ xd1 \ xd1 \ xd1 \ _d83 \ xd1 \ x8c \ xd1 \ x82 \ xd0 \ xb0 \ xd1 \ x82: $ परिणाम "', 1.64)
लेक्सटन (PHPCOLON, ';', 1,107)
अंत में संख्या रेखा संख्या और वर्ण संख्या हैं। जैसा कि आप देख सकते हैं, लाइन नंबर नहीं बदलता है। इसे खुद से बदलने की जरूरत है। कैसे? जब एक नई लाइन के लिए संक्रमण के साथ एक टोकन गुजर रहा है। ऐसा करने के लिए, अनदेखे टोकन से नई पंक्ति वर्ण हटाएं और नया t_newline फ़ंक्शन जोड़ें:
t_ignore = ' \r\t\f' def t_newline(t): r'\n+' t.lexer.lineno += len(t.value)
अब हमारे लिए सब कुछ काम करता है:
लेक्सटन (PHPSTART, '<? Php', 2.1)
लेक्सटन (PHPVAR, '$ वैल', 3.7)
लेक्सटन (PHPEQUAL, '=', 3,12)
लेक्सटन (PHPNUM, '5', 3.14)
लेक्सटन (PHPCOLON, ';', 3.15)
लेक्सटन (PHPVAR, '$ परिणाम', 4.17)
लेक्सटन (PHPEQUAL, '=', 4.25)
LexToken (PHPFUNC, 'रूट', 4.27)
LexToken (PHPOPEN, '(', 4.33)
लेक्सटन (PHPSTRING, '' फोब्बर '', 4.35)
लेक्सटन (PHPCOMA, ',', 4.43)
लेक्सटन (PHPNUM, '2', 4.45)
लेक्सटन (DIVMUL, '*', 4.46)
लेक्सटन (PHPOPEN, '(', 4.47)
लेक्सटन (PHPNUM, '7', 4.48)
लेक्सटन (PLUSMINUS, '-', 4.49)
लेक्सटन (PHPVAR, '$ वैल', 4.50)
LexToken (PHPCLOSE, ')', 4.54)
लेक्सटन (PHPCLOSE, ')', 4.56)
लेक्सटन (PHPCOLON, ';'; 4.57)
LexToken (PHPFUNC, 'गूंज', 5.59)
LexToken (PHPSTRING, "" \ xd1 \ x8d \ xd1 \ x82 \ xd0 \ xd0 \ xd0 \ xd0 \ xb0 \ xd1 \ xd1 \ xd1 \ xd1 \ xd5 \ xd5 \ xd1 \ xd1 \ xd1 \ _d83 \ xd1 \ x8c \ xd1 \ x82 \ xd0 \ xb0 \ xd1 \ x82: $ परिणाम "', 5.64)
लेक्सटन (PHPCOLON, ';', 5,107)
यह काम करता है, लेकिन पूरी तरह से नहीं। हमारी लाइन "यह हमारा परिणाम है: $ परिणाम" जैसा है वैसा ही लीकर ने लौटाया है। और हम इसे कैसे पार्स करेंगे? एक अलग लिकर बनाएं? नहीं। PLY में (और सिस्टम लेक्स में) स्टेट जैसी चीज के लिए सपोर्ट है।
राज्य एक अन्य प्रकार के टोकन का स्विच है। यह वही है जो हम उपयोग करेंगे।
राज्य
इसलिए, एक नया राज्य बनाने के लिए, हमें इसे पहले घोषित करने की आवश्यकता है:
states = ( ('string','exclusive'), )
'स्ट्रिंग' नए राज्य का नाम है, और अनन्य प्रकार है। सामान्य तौर पर, दो प्रकार के राज्य हो सकते हैं: अनन्य और समावेशी। विशेष - सामान्य टोकन दिखाई नहीं देते हैं (सामान्य, ये वही हैं जो हमने पहले लिखे थे, वे डिफ़ॉल्ट रूप से INITIAL राज्य में हैं)। समावेशी - सामान्य टोकन दिखाई देते हैं और वर्तमान स्थिति में हम सिर्फ उनके लिए अतिरिक्त टोकन जोड़ते हैं।
हमारी रेखा के मामले में, हमें अन्य टोकन की आवश्यकता नहीं है, क्योंकि हमारे अंदर सब कुछ अलग है। हालांकि, हम एक जोड़े को उधार लेंगे। हमें PHPVAR टोकन की आवश्यकता है, क्योंकि हमारा चर स्ट्रिंग के बाहर और अंदर दोनों समान दिखता है, और हमें एक और टोकन की आवश्यकता है, जिसे आप नीचे देखेंगे।
टोकन को सामान्य बनाने के लिए, हमें इसे किसी भी उपसर्ग को असाइन करना होगा, लेकिन टोकन को केवल एक निश्चित स्थिति के लिए दृश्यमान बनाना होगा - <राज्य> _। निम्नलिखित संशोधित टोकन हैं।
वैसे, क्या आपने इस पर ध्यान दिया:
लेक्सटोकन (PHPFUNC, 'इको', 5.59) ?
तालिका बनाने से पहले PLY आरोही क्रम में नियमित भावों को छाँटते हैं और पार्सिंग के दौरान सबसे लंबे समय तक जीतते हैं। यही कारण है कि गूंज PHPECHO की तरह पार्स नहीं करता है। इसके आसपास कैसे पहुंचें? आसानी से। फ़ंक्शन में दिए गए टोकन के प्रकार को बस बदलें।
इस तरह:
@TOKEN(ident) def t_PHPFUNC(t): if t.value.lower() == 'echo': t.type = 'PHPECHO' return t
अब आवश्यकतानुसार प्रतिध्वनि लौटती है।
TOKEN डेकोरेटर की बात: फ़ंक्शन बॉडी की शुरुआत में एक नियमित लिखने के बजाय, आप बस इसे एक चर में रख सकते हैं और इसे डेकोरेटर के रूप में फ़ंक्शन पर लागू कर सकते हैं। जो हमने किया।
यहाँ। अब सब कुछ होने लगा है। हाँ, बिलकुल नहीं।
टिप्पणियों को अनदेखा करना अच्छा होगा।
ठीक है, लेसर में एक और छोटी फ़ंक्शन जोड़ें:
def t_comment(t): r'(/\*(.|\n)*?\*/)|(//.*)' pass
पूर्ण lexer स्रोत कोड (lexer.py) अब parser (parser.py) पर जाएं।
Yacc
Yacc एक चीज़ (पार्सर) है जिसमें हम टोकन स्थानांतरित करते हैं और उनके कनेक्शन (व्याकरण) के नियमों का वर्णन करते हैं। जैसा कि यह कार्यक्रम काम करता है, हम एक अमूर्त पेड़ बना सकते हैं या कार्यक्रम को तुरंत निष्पादित कर सकते हैं (जैसा कि साइट पर कैलकुलेटर के साथ उदाहरण में किया गया था)।
PLY में व्याकरण का वर्णन करने के लिए
ply.yacc वर्ग मौजूद है।
प्लाई में व्याकरण का वर्णन करना बहुत सरल है और यह कुछ खुशी भी देता है। विवरण के लिए, हमारे पास फिर से विशेष कार्य p_name फ़ंक्शंस हैं, जहां डॉक्टर-लाइन्स में हम इस बहुत ही व्याकरण का वर्णन करते हैं।
आइए php के साथ हमारे सार उदाहरण के लिए एक बहुत ही सरल व्याकरण लिखने का प्रयास करें:
php -> [PHPSTART phpbody]?
phpbody -> [phpline phpcolons] *
phpcolons -> [PHPCOLON] +
phpline -> असाइन करें | दुर्गंध | [PHPECHO args]
असाइन करें -> PHPVAR PHPEQUAL expr
expr -> [तथ्य | expr PLUSMINUS तथ्य]
तथ्य -> [पद | तथ्य DIVMUL शब्द]
पद -> [arg | PHPOPEN expr PHPCLOSE]
func -> PHPFUNC PHPOPEN PHPCLOSE के पास है
args -> [expr [PHPCOMA expr] *]?
arg -> तार | phpvar | PHPNUM | समारोह
string -> PHPSTRING str PHPSTRING
str -> [एसटीआर | str phpvar]?
phpvar -> PHPVAR
बहुत सारे पाठ पहले ही लिखे जा चुके हैं, और आप अभी भी बहुत लंबे समय तक बात कर सकते हैं। लेकिन ply.yacc की मूल बातें समझने के लिए, एक उदाहरण पर्याप्त है। और आगे मैं पार्सर के स्रोत कोड को बाहर करूंगा।
तो, पार्सर से एक फटा हुआ टुकड़ा:
def p_str(p): '''str : | STR | str phpvar''' if len(p) == 1: p[0] = Node('str', ['']) elif len(p) == 2: p[0] = Node('str', [p[1]]) else: p[0] = p[1].add_parts([p[2]])
क्रम में। पार्सिंग शीर्ष पर पहले फ़ंक्शन से शुरू होता है पैटर्न p_ <फ़ंक्शन> के साथ। यानी उदाहरण के लिए, p_php स्रोत में पहला है, इसलिए पार्सर इसमें (ऊपर से नीचे) काम करना शुरू कर देगा। पार्सर डॉक-लाइनों में नियमों के साथ काम करता है, और उनके मैच के बाद नियमों के साथ फ़ंक्शन पर नियंत्रण लौटता है और उसी समय चर पी। चर parsing के परिणाम को संग्रहीत करता है। इसके अलावा, प्रसंस्करण के बाद, हमें p [0] में अपने परिणामों को भेजना चाहिए। पी [1] से शुरू होने वाले तत्व, यह हमारे नियमों का सही हिस्सा है - अनियंत्रित टोकन और नियम।
'''str : | STR | str phpvar'''
वास्तव में, उपरोक्त नियम एक संयुक्त (विलय) नियम है। '|' जैसा कि आप अनुमान लगा सकते हैं, यह एक टोकन के लिए OR, अच्छी तरह से या विभिन्न विकल्प हैं।
बृहदान्त्र और बाएँ / दाएँ पक्ष के बीच एक स्थान आवश्यक है। तो प्लाई प्यार करता है। यदि दाहिने हाथ की ओर खाली हो सकता है, तो बृहदान्त्र के बाद हम कुछ भी नहीं लिखते हैं। टोकन को बड़े अक्षरों में लिखा जाना चाहिए, और छोटे अक्षरों में नियम, बिना p_ उपसर्ग के। यानी ऊपर के उदाहरण में, नियम p_str और p_phpvar का उपयोग किया गया था।
उदाहरण के लिए, यदि हमारे पास "" (एक खाली स्ट्रिंग) है, तो p में हमारा केवल एक ही मान होगा - p [0], और यह परिभाषित नहीं है और हमें इसे भरना होगा।
यदि हमारे पास "हेलो $ वास्य पॉटकिन" स्ट्रिंग है, तो यह फ़ंक्शन तीन बार निष्पादित किया जाएगा। एसटीआर मूल्य के लिए पहली बार, और हम इस मूल्य को पी [0] पर वापस कर देंगे, दूसरी बार $ वास्य के लिए, और इसे पिछले मूल्य में जोड़ देंगे (पी [1] में वह नोड होगा जिसमें हम पिछले पुनरावृत्ति पर पी [0] पर लौट आए। ), और तीसरी बार हम आउटपुट में "पिल्किन" जोड़ते हैं। संभवतः मैंने वर्णन किया है कि आपको गड़बड़ करना है, लेकिन आपको इसे छूना होगा। रनटाइम खोलें और चारों ओर खेलें :)
वैसे, कभी-कभी विभाजित विकल्पों वाला विकल्प अधिक सुविधाजनक होता है (टैफ्टोलॉजी, सॉरी):
def p_str_empty(p): '''str :''' p[0] = Node('str', ['']) def p_str_raw(p): '''str : STR''' p[0] = Node('str', [p[1]]) def p_str_var(p): '''str : str phpvar''' p[0] = p[1].add_parts([p[2]])
यह कोड के पिछले संस्करण के समान है। बस लगा-टिप पेन अलग हैं। फ़ंक्शंस के अलग-अलग नाम हैं (क्योंकि आपको उन्हें अजगर में अलग करने की आवश्यकता है), लेकिन उपसर्ग समान (str) हैं, जो प्लाई को एक ही नियम के वेरिएंट के रूप में एक साथ समूहित करने के लिए मजबूर करते हैं।
एक पेड़ के निर्माण की सुविधा के लिए, मैंने ऐसे सरल और प्रभावी वर्ग का उपयोग किया:
class Node: def parts_str(self): st = [] for part in self.parts: st.append( str( part ) ) return "\n".join(st) def __repr__(self): return self.type + ":\n\t" + self.parts_str().replace("\n", "\n\t") def add_parts(self, parts): self.parts += parts return self def __init__(self, type, parts): self.type = type self.parts = parts
यह एक साथ आपकी ज़रूरत की सभी चीज़ों को संग्रहीत करता है, और इस निष्कर्ष की संरचना करता है कि यह डीबगिंग के लिए बहुत सुविधाजनक है।
मुख्य फाइल जहां से हम सब कुछ कहते हैं सिंटेक्स ट्री आउटपुट रेखा:
आवंटित:
$ वैल
आर्ग:
5
आवंटित:
$ परिणाम
आर्ग:
समारोह:
substr
आर्ग:
आर्ग:
str:
foobar
*:
आर्ग:
2
-:
आर्ग:
7
आर्ग:
var:
$ वैल
गूंज:
आर्ग:
आर्ग:
str:
यह हमारा परिणाम है:
आर्ग:
var:
$ परिणाम
निष्कर्ष
यदि कोई टिप्पणी या सुझाव हैं, तो मैं ख़ुशी से पोस्ट को जोड़ / बदलूंगा।
यहाँ अनुच्छेद कोडखैर, अगर मुझे आश्चर्य होता है कि मैंने अचानक प्लाई -
पीफॉक्स के बारे में लिखने का फैसला क्यों किया। मैं अजगर में चुपचाप प्रसंस्करण सीएसएस कर रहा हूं। बल्कि, सीएसएस पार्सर लिखा है, लेकिन डोम के माध्यम से चलना अभी तक पूरी तरह से लागू नहीं हुआ है (सभी छद्म चयनकर्ता काम नहीं)। अब विशेष रूप से नथ-बच्चे के साथ समस्या। सीएसएस स्तर पर नहीं, लेकिन प्रभावी मिलान के स्तर पर - डोम में पड़ोसियों के बीच संख्या के रूप में ऐसी कोई संपत्ति नहीं है, लेकिन आप पिछले पड़ोसियों की गिनती नहीं करना चाहते हैं। जाहिरा तौर पर HTMLParser को अनुकूलित करना होगा। शायद कोई मेरे साथ जुड़ने का फैसला करे? सभी का स्वागत है :)