क्या फ्लेक्स / बाइसन के लिए कोई विकल्प है जो 8-बिट एम्बेडेड सिस्टम पर प्रयोग करने योग्य है?


83

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

अगर मैं इसे अपने लिनक्स बॉक्स पर चलाने के लिए लिखूंगा, तो मैं फ्लेक्स / बाइसन का उपयोग कर सकता हूं। अब जब मैंने अपने आप को एक 8-बिट प्लेटफॉर्म तक सीमित कर लिया, तो मुझे यह सब हाथ से करना होगा, या नहीं?


1
क्या कोई विशिष्ट चिप है जिसे आप उपयोग करने का इरादा रखते हैं? कितना ROM / RAM है?
स्टीव एस

@Mre के लिंक पर अपडेट करें। एम्बेडेड.कॉम ​​ने उनके URL को ट्रैश कर दिया है। ( एम्बेडेड.
com

लगता है कि केवल लैगूज (आगे और सीओ) 2KB रैम पर मौका है, कर्नेल के साथ चमकती है
Jacek Cz

जवाबों:


59

मैंने ATmega328p के लिए लक्षित एक साधारण कमांड भाषा के लिए एक पार्सर लागू किया है । इस चिप में 32k ROM और केवल 2k RAM है। RAM निश्चित रूप से अधिक महत्वपूर्ण सीमा है - यदि आप अभी तक किसी विशेष चिप से बंधे नहीं हैं, तो जितना संभव हो उतना रैम चुनें। इससे आपका जीवन बहुत आसान हो जाएगा।

पहले मैंने फ्लेक्स / बाइसन का उपयोग करने पर विचार किया। मैंने दो प्रमुख कारणों से इस विकल्प के खिलाफ निर्णय लिया:

  • डिफ़ॉल्ट रूप से, फ्लेक्स और बाइसन कुछ मानक पुस्तकालय कार्यों (विशेष रूप से I / O के लिए) पर निर्भर करते हैं जो उपलब्ध नहीं हैं या avr-libc में समान काम नहीं करते हैं। मुझे पूरा यकीन है कि समर्थित वर्कअराउंड हैं, लेकिन यह कुछ अतिरिक्त प्रयास हैं जिन्हें आपको ध्यान में रखना होगा।
  • AVR में हार्वर्ड आर्किटेक्चर है । C को इस बात के लिए डिज़ाइन नहीं किया गया है, इसलिए यहां तक ​​कि निरंतर चर को डिफ़ॉल्ट रूप से RAM में लोड किया जाता है । आपको फ़्लैश और EEPROM में डेटा स्टोर और एक्सेस करने के लिए विशेष मैक्रोज़ / फ़ंक्शंस का उपयोग करना होगा । फ्लेक्स और बाइसन कुछ अपेक्षाकृत बड़े लुकअप टेबल बनाते हैं , और ये आपकी रैम को बहुत जल्दी खा जाएंगे। जब तक मैं गलत नहीं हूं (जो कि बहुत संभव है) आपको विशेष फ्लैश और EEPROM इंटरफेस का लाभ उठाने के लिए आउटपुट स्रोत को संपादित करना होगा।

फ्लेक्स एंड बाइसन को खारिज करने के बाद, मैं अन्य जनरेटर टूल की तलाश में चला गया। यहाँ कुछ हैं जो मैंने माना:

आप विकिपीडिया की तुलना पर एक नज़र डालना चाहते हैं ।

अंत में, मैंने लेसर और पार्सर दोनों को हाथ से कोड करना समाप्त कर दिया।

पार्सिंग के लिए मैंने एक पुनरावर्ती वंशीय पार्सर का उपयोग किया। मुझे लगता है कि इरा बाक्सटर ने पहले ही इस विषय को कवर करने का पर्याप्त काम किया है, और ऑनलाइन बहुत सारे ट्यूटोरियल हैं।

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

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


225

यदि आप पार्सर कोड करने के लिए एक आसान तरीका चाहते हैं, या आप अंतरिक्ष पर तंग हैं, तो आपको एक पुनरावर्ती वंश पार्सर को हाथ से कोड करना चाहिए; ये अनिवार्य रूप से 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:

बहुत सारे लोग हैं जो एक सरल अभिव्यक्ति मूल्यांकनकर्ता लिखना चाहते हैं। आप इसे उसी प्रकार की चीजें करके कर सकते हैं जो ऊपर "एएसटी बिल्डर" लिंक से पता चलता है; बस पेड़ के नोड्स के निर्माण के बजाय अंकगणित करें। यहाँ एक अभिव्यक्ति मूल्यांकनकर्ता ने इस तरह से किया है


2
हाँ, एक सरल भाषा के लिए एक पुनरावर्ती वंश पार्सर को रोल करना बहुत कठिन नहीं है। जब आप कर सकते हैं तो टेल कॉल को ऑप्टिमाइज़ करना याद रखें - जब आप केवल एक किलो किलोबाइट रैम प्राप्त करते हैं तो स्टैक स्पेस बहुत मायने रखता है।
स्टीव एस

2
सभी: हाँ, आप पूंछ कॉल अनुकूलन कर सकते हैं। जब तक आप अपने पार्स किए गए कोड में घोंसले के शिकार की उम्मीद नहीं करते हैं, तब तक वास्तव में गहरी हो जाएगी; एक बेसिक कोड के लिए 10 से अधिक पैराथिशन के भावों को खोजने के लिए इसकी बहुत कठिन रेखा है, और आप हमेशा बूट करने के लिए गहराई सीमा में रख सकते हैं। यह सच है कि एम्बेडेड सिस्टम में स्टैक स्पेस कम होता है, इसलिए कम से कम अपनी पसंद पर ध्यान दें।
इरा बैक्सटर

2
@ मर्क: और यह 2012 हो सकता है, लेकिन 1965 के तकनीकी पेपर का संदर्भ मैं अभी अच्छा हूँ क्योंकि यह तब और इसके बहुत अच्छे थे, खासकर यदि आप इसे नहीं जानते हैं।
इरा बाक्सटर

2
@ मर्क, आह, ठीक है, धन्यवाद! लगता है तारीख रहस्यमय तरीके से तय हो गई। धन्यवाद, समय भगवान।
इरा बाक्सटर

2
मैं खाली तारों को कैसे संभाल सकता हूं?
दांते

11

आप कोड को उत्पन्न करने के लिए अपने देशी जीसीसी के साथ लिनक्स पर फ्लेक्स / बाइसन का उपयोग कर सकते हैं जिसे आप एम्बेडेड लक्ष्य के लिए अपने एवीआर जीसीसी के साथ पार-संकलन करेंगे।


2

जीसीसी विभिन्न प्लेटफार्मों पर क्रॉस-कंपाइल कर सकता है, लेकिन आप उस प्लेटफ़ॉर्म पर फ्लेक्स और बाइसन चलाते हैं जिस पर आप कंपाइलर चला रहे हैं। वे बस सी कोड को थूकते हैं जो कंपाइलर बनाता है। यह देखने के लिए परीक्षण करें कि परिणामी निष्पादन वास्तव में कितना बड़ा है। ध्यान दें कि उन्होंने समय पुस्तकालय ( libfl.aआदि) चलाए हैं कि आपको अपने लक्ष्य को संकलित करना होगा।


मुझे अभी भी उन पुस्तकालयों के आकार की जांच करनी है और इसीलिए मैंने पहली जगह में सवाल पूछा। मैं विशेष रूप से छोटे MCUs की ओर लक्षित कुछ चाहता हूं।
जोहान

-1

बूस्ट का प्रयास करें :: आत्मा। यह एक हेडर-ओनली लाइब्रेरी है, जिसे आप C ++ में पूरी तरह से, बहुत तेजी से, साफ पार्सर में छोड़ सकते हैं और बना सकते हैं। एक विशेष व्याकरण फ़ाइल के बजाय C ++ में ओवरलोडेड ऑपरेटरों का उपयोग किया जाता है।


आपके उत्तर के साथ समस्या यह है कि यह 8-बिट प्लेटफॉर्म की कमी का संज्ञान नहीं है। एक ही समय में बढ़ावा देने और इस तरह के माइनसक्यूल प्लेटफॉर्म का समर्थन करने के लिए एक उपकरण श्रृंखला प्राप्त करने के लिए कड़ी मेहनत की जाएगी।
वासलप

-5

पहिया का फिर से आविष्कार करने के बजाय, LUA: www.lua.org पर एक नज़र डालें । यह एक व्याख्यात्मक भाषा है जिसका अर्थ है अन्य सॉफ्टवेयर में एम्बेडेड होना और छोटे पैमाने पर सिस्टम, जैसे एम्बेडेड सिस्टम पर उपयोग किया जाता है। अंतर्निहित प्रक्रियात्मक वाक्यविन्यास पार्सिंग ट्री, नियंत्रण तर्क, गणित और चर समर्थन - कुछ को फिर से स्थापित करने की आवश्यकता नहीं है जो हजारों अन्य पहले से ही डिबग और उपयोग कर चुके हैं। और यह एक्स्टेंसिबल है, जिसका अर्थ है कि आप अपने स्वयं के सी कार्यों को जोड़कर व्याकरण को जोड़ सकते हैं।


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