NodeJS पर एक CLI लिखना


सभी को शुभ संध्या।


आपके इमर्सिव CLI को node.js. पर लिखने में समस्या थी पहले इस उद्देश्य के लिए vorpal का उपयोग किया जाता था। इस बार मैं अनावश्यक निर्भरता के बिना करना चाहता था, और, इसके अलावा, मैंने कमांड के तर्क को अलग तरीके से लेने की संभावना पर विचार किया।


Vorpal के साथ, कमांड निम्नानुसार लिखे गए थे:


setValue -s 1 -v 0 

सहमत, लेखन -s हर बार बहुत सुविधाजनक नहीं है।


अंत में, टीम निम्नलिखित में बदल गई:


 set 1: 0 

इसे कैसे लागू किया जा सकता है - कटौती के तहत


  1. इसके अलावा, एक अच्छा बोनस मानों की सूची के रूप में कई तर्कों का स्थानांतरण होता है, जो एक स्थान और एक सरणी के रूप में अलग-अलग होते हैं।

पाठ इनपुट


मैं टेक्स्ट दर्ज करने के लिए readline का उपयोग करता readline । निम्नलिखित तरीके से हम स्वतः पूर्णता के लिए समर्थन के साथ एक इंटरफ़ेस बनाते हैं:


  let commandlist = []; commandlist.push("set", "get", "stored", "read", "description"); commandlist.push("watch", "unwatch"); commandlist.push("getbyte", "getitem", "progmode"); commandlist.push("ping", "state", "reset", "help"); function completer(line) { const hits = commandlist.filter(c => c.startsWith(line)); // show all completions if none found return [hits.length ? hits : commandlist, line]; } /// init repl const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: "bobaos> ", completer: completer }); const console_out = msg => { process.stdout.clearLine(); process.stdout.cursorTo(0); console.log(msg); rl.prompt(true); }; 

console.log उम्मीद के console.log काम करता है, यानी वर्तमान लाइन पर पाठ प्रदर्शित करता है और लाइन को लपेटता है, और यदि यह किसी भी बाहरी घटना से शुरू होता है जो पाठ के इनपुट से स्वतंत्र है, तो डेटा को इनपुट लाइन पर प्रदर्शित किया जाएगा। इसलिए, हम कंसोल_आउट फ़ंक्शन का उपयोग करते हैं, जो कंसोल के आउटपुट के बाद रीडलाइन इनपुट लाइन को कॉल करता है।


पार्सर


ऐसा लगता है कि आप स्ट्रिंग को रिक्त स्थान में विभाजित कर सकते हैं, अलग-अलग हिस्सों को अलग कर सकते हैं और इसे संसाधित कर सकते हैं। लेकिन फिर एक स्थान वाले स्ट्रिंग मापदंडों को पारित करना असंभव होगा; और किसी भी मामले में, अतिरिक्त स्थान और टैब को हटाने के लिए आवश्यक होगा।


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


व्याकरण


हम व्याकरण फ़ाइल में आदेशों और तर्कों का वर्णन करते हैं।
आरंभ करने के लिए, निम्नलिखित को परिभाषित करें:


  1. अभिव्यक्ति में एक पंक्ति होती है। हमें दो से अधिक लाइनों को संसाधित करने की आवश्यकता नहीं है।
  2. अभिव्यक्ति की शुरुआत में कमांड पहचानकर्ता है। आगे तर्क।
  3. सीमित संख्या में कमांड हैं, इसलिए उनमें से प्रत्येक को व्याकरण फ़ाइल में लिखा गया है।

प्रवेश बिंदु इस प्रकार है:


 command ::= (set|get|stored|read|description|getbyte|watch|unwatch|ping|state|reset|getitem|progmode|help) WS* 

WS * का अर्थ है व्हाट्सएप - स्थान या टैब वर्ण। यह इस प्रकार वर्णित है:


 WS ::= [#x20#x09#x0A#x0D]+ 

जिसका अर्थ है वर्ण स्थान, टैब या रेखा विराम, एक बार और अधिक घटित होना।


टीमों के लिए आगे बढ़ते हैं।
बिना तर्क के सबसे सरल:


 ping ::= "ping" WS* state ::= "state" WS* reset ::= "reset" WS* help ::= "help" WS* 

इसके अलावा, कमांड जो एक स्थान, या एक सरणी द्वारा अलग किए गए प्राकृतिक संख्याओं की एक सूची लेते हैं।


 BEGIN_ARRAY ::= WS* #x5B WS* /* [ left square bracket */ END_ARRAY ::= WS* #x5D WS* /* ] right square bracket */ COMMA ::= WS* #x2C WS* /* , comma */ uint ::= [0-9]* UIntArray ::= BEGIN_ARRAY (uint WS* (COMMA uint)*) END_ARRAY UIntList ::= (uint WS*)* get ::= "get" WS* ( UIntList | UIntArray ) 

इस प्रकार, निम्न उदाहरण प्राप्त कमांड के लिए सही हैं:


 get 1 get 1 2 3 5 get [1, 2, 3, 5, 10] 

अगला, सेट कमांड, जो एक इनपुट जोड़ी आईडी लेता है: मान, या मानों की एक सरणी।


 COLON ::= WS* ":" WS* Number ::= "-"? ("0" | [1-9] [0-9]*) ("." [0-9]+)? (("e" | "E") ( "-" | "+" )? ("0" | [1-9] [0-9]*))? String ::= '"' [^"]* '"' | "'" [^']* "'" Null ::= "null" Bool ::= "true" | "false" Value ::= Number | String | Null | Bool DatapointValue ::= uint COLON Value DatapointValueArray ::= BEGIN_ARRAY (DatapointValue WS* (COMMA DatapointValue)*)? END_ARRAY set ::= "set" WS* ( DatapointValue | DatapointValueArray ) 

इस प्रकार, सेट कमांड के लिए, निम्नलिखित नोटेशन फॉर्म सही हैं:


 set 1: true set 2: 255 set 3: 21.42 set [1: false, 999: "hello, friend"] 

js में प्रक्रिया


हम फ़ाइल पढ़ते हैं, पार्सर ऑब्जेक्ट बनाते हैं।


 const grammar = fs.readFileSync(`${__dirname}/grammar`, "utf8"); const parser = new Grammars.W3C.Parser(grammar); 

आगे, डेटा दर्ज करते समय, रीडलाइन ऑब्जेक्ट का एक उदाहरण लाइन ईवेंट को इंगित करता है, जिसे निम्नलिखित फ़ंक्शन द्वारा संसाधित किया जाता है:


 let parseCmd = line => { let res = parser.getAST(line.trim()); if (res.type === "command") { let cmdObject = res.children[0]; return processCmd(cmdObject); } }; 

यदि कमांड सही ढंग से लिखा गया था, तो पार्सर एक पेड़ देता है, जहां प्रत्येक तत्व का एक प्रकार, बच्चों का क्षेत्र और एक पाठ क्षेत्र होता है। प्रकार फ़ील्ड वर्तमान तत्व का प्रकार मान लेता है। यानी अगर हम पिंग कमांड को पारसर के पास देते हैं, तो पेड़ एक ट्रेस की तरह दिखेगा। जिस तरह से:


 { "type": "command", "text": "ping", "children": [{ "type": "ping", "text": "ping", "children": [] }] } 

हम फॉर्म में लिखते हैं:


 command ping Text = "ping" 

कमांड के लिए "1 2 3 प्राप्त करें",


 command get UIntList uint Text = "1" uint Text = "2" uint Text = "3" 

अगला, हम प्रत्येक कमांड को संसाधित करते हैं, आवश्यक कार्रवाई करते हैं और कंसोल में परिणाम प्रदर्शित करते हैं।


परिणाम एक बहुत ही सुविधाजनक इंटरफ़ेस है जो न्यूनतम निर्भरता के साथ काम को गति देता है। मैं समझाऊंगा:


ग्रुप एड्रेस (उदाहरण के लिए) पढ़ने के लिए ग्राफिकल इंटरफ़ेस (ETS) में, आपको इनपुट फ़ील्ड में एक ग्रुप एड्रेस दर्ज करना होगा, फिर एक अनुरोध भेजने के लिए माउस बटन (या कई TAB) का उपयोग करना होगा।


Vorpal के माध्यम से लागू किए गए इंटरफ़ेस में, कमांड निम्नानुसार है:


 readValue -s 1 

या तो:


 readValues -s "1, 3" 

पार्सर का उपयोग करके, आप अतिरिक्त -s और उद्धरण चिह्नों से बच सकते हैं।


 read 1 3 

संदर्भ


  1. https://github.com/bobaoskit/bobaos.tool - प्रोजेक्ट रिपॉजिटरी। आप कोड को देख सकते हैं।
  2. http://menduz.com/ebnf-highlighter/ - आप मक्खी पर व्याकरण को संपादित और जांच सकते हैं।

Source: https://habr.com/ru/post/hi426615/


All Articles