यदि आप पार्सर कोड करने के लिए एक आसान तरीका चाहते हैं, या आप अंतरिक्ष पर तंग हैं, तो आपको एक पुनरावर्ती वंश पार्सर को हाथ से कोड करना चाहिए; ये अनिवार्य रूप से LL (1) पार्सर्स हैं। यह उन भाषाओं के लिए विशेष रूप से प्रभावी है जो मूल के रूप में "सरल" हैं। (मैंने 70 के दशक में इनमें से कई को वापस किया था!)। अच्छी खबर यह है कि इसमें कोई पुस्तकालय कोड नहीं है; बस आप क्या लिखते हैं।
वे कोड के लिए बहुत आसान हैं, अगर आपके पास पहले से ही एक व्याकरण है। सबसे पहले, आपको बाएं पुनरावर्ती नियमों से छुटकारा पाना होगा (जैसे, एक्स = एक्सवाई)। यह आमतौर पर करने के लिए बहुत आसान है, इसलिए मैं इसे एक अभ्यास के रूप में छोड़ देता हूं। (आपको सूची बनाने के नियमों के लिए ऐसा करने की आवश्यकता नहीं है; नीचे चर्चा देखें)।
यदि आपके पास फॉर्म का BNF नियम है:
X = A B C ;
नियम (X, A, B, C) में प्रत्येक आइटम के लिए एक सबरूटीन बनाएं जो एक बूलियन यह कहते हुए लौटाता है कि "मैंने संबंधित सिंटैक्स निर्माण देखा"। X के लिए, कोड:
subroutine X()
if ~(A()) return false;
if ~(B()) { error(); return false; }
if ~(C()) { error(); return false; }
// insert semantic action here: generate code, do the work, ....
return true;
end X;
इसी तरह ए, बी, सी के लिए।
यदि टोकन एक टर्मिनल है, तो कोड लिखें जो टर्मिनल बनाने वाले पात्रों की स्ट्रिंग के लिए इनपुट स्ट्रीम की जांच करता है। उदाहरण के लिए, एक संख्या के लिए, इनपुट स्ट्रीम में अंक होते हैं और अंकों से अतीत के इनपुट स्ट्रीम कर्सर को आगे बढ़ाते हैं। यह विशेष रूप से आसान है यदि आप एक बफर से बाहर पार्स कर रहे हैं (मूल रूप से, आप समय पर एक पंक्ति प्राप्त करते हैं) केवल बफर स्कैन पॉइंटर को आगे बढ़ाने या आगे बढ़ाने से नहीं। यह कोड मूल रूप से पार्सर का लेक्सर हिस्सा है।
यदि आपका बीएनएफ नियम पुनरावर्ती है ... चिंता न करें। बस पुनरावर्ती कॉल कोड। यह व्याकरण के नियमों को संभालता है जैसे:
T = '(' T ')' ;
इसे इस प्रकार कोडित किया जा सकता है:
subroutine T()
if ~(left_paren()) return false;
if ~(T()) { error(); return false; }
if ~(right_paren()) { error(); return false; }
// insert semantic action here: generate code, do the work, ....
return true;
end T;
यदि आपके पास एक विकल्प के साथ BNF नियम है:
P = Q | R ;
फिर वैकल्पिक विकल्पों के साथ P कोड करें:
subroutine P()
if ~(Q())
{if ~(R()) return false;
return true;
}
return true;
end P;
कभी-कभी आप सूची बनाने के नियमों का सामना करेंगे। इनका पुनरावर्ती होना छोड़ दिया जाता है, और यह मामला आसानी से निपट जाता है। मूल विचार पुनरावृत्ति के बजाय पुनरावृत्ति का उपयोग करना है, और यह उस अनंत पुनरावृत्ति से बचा जाता है जिसे आप "स्पष्ट" तरीके से कर रहे हैं। उदाहरण:
L = A | L A ;
आप इसे निम्न का उपयोग करके इसे कोड कर सकते हैं:
subroutine L()
if ~(A()) then return false;
while (A()) do { /* loop */ }
return true;
end L;
आप इस तरह से एक या दो दिन में कई सौ व्याकरण नियम कोड कर सकते हैं। इसमें भरने के लिए अधिक विवरण हैं, लेकिन यहां मूल बातें पर्याप्त से अधिक होनी चाहिए।
यदि आप वास्तव में अंतरिक्ष पर तंग हैं, तो आप एक वर्चुअल मशीन बना सकते हैं जो इन विचारों को लागू करती है। यही कारण है कि मैंने 70 के दशक में वापस किया, जब 8K 16 बिट शब्द वह था जो आपको मिल सकता था।
यदि आप इसे हाथ से कोड नहीं करना चाहते हैं, तो आप इसे मेटाकॉम्पलर ( मेटा II ) के साथ स्वचालित कर सकते हैं जो अनिवार्य रूप से एक ही चीज का उत्पादन करता है। ये तकनीकी रूप से मज़ेदार हैं और वास्तव में बड़े व्याकरणों के लिए भी यह काम करने से बचते हैं।
अगस्त 2014:
मुझे "पार्सर के साथ एएसटी का निर्माण कैसे करें" के लिए बहुत सारे अनुरोध मिलते हैं। इस विवरण के लिए, जो अनिवार्य रूप से इस उत्तर को विस्तृत करता है, मेरे अन्य SO उत्तर देखें https://stackoverflow.com/a/251066/12/1263
जुलाई 2015:
बहुत सारे लोग हैं जो एक सरल अभिव्यक्ति मूल्यांकनकर्ता लिखना चाहते हैं। आप इसे उसी प्रकार की चीजें करके कर सकते हैं जो ऊपर "एएसटी बिल्डर" लिंक से पता चलता है; बस पेड़ के नोड्स के निर्माण के बजाय अंकगणित करें। यहाँ एक अभिव्यक्ति मूल्यांकनकर्ता ने इस तरह से किया है ।