2015-03-15 7 views
8

मैं एक प्रोग्राम बनाने की कोशिश कर रहा हूं जो उपयोगकर्ता इनपुट को iex या erl जैसा करता है (उदा। जब पिछले इतिहास को नेविगेट करने के लिए अनुमति कुंजी दबाते हैं)।Elixir के लिए कोई भी रेडलाइन बाध्यकारी समर्थन?

तो मानक IO.gets इस प्रकार प्रयोग किया जाता है,

IO.gets "user> " 

कंसोल के साथ निम्न में जब ऊपर से अनुमति देते हैं दबाया जाता है समाप्त होता है।

user> ^[[A^[[A 

क्या कोई फ़ंक्शन/लाइब्रेरी है जो रीडलाइन क्षमता है जिसका उपयोग इलीक्सिर कोड के अंदर किया जा सकता है?


मैं अब तक है कि क्या जांच की है,

  • कुछ भाषाओं ReadLine पुस्तकालयों के लिए समर्थन के लिए बाध्य है, लेकिन मैं अमृत के लिए इसी क्षमता ढूँढने में सक्षम नहीं किया गया है।
  • iex कार्यान्वयन erl (/lib/iex/history.ex को केवल इतिहास की सूची प्रबंधित करने के लिए लगता है) को इस क्षमता का प्रतिनिधित्व करने लगता है, लेकिन मैं erlang पक्ष में इसी कार्यक्षमता को खोजने में सक्षम नहीं हूं।
  • मैंने erl_ddll.load को आजमाया है, लेकिन निम्न से आगे जाने में विफल रहा है।
आईईएक्स पर

iex(4)> :erl_ddll.load('/usr/lib', 'libreadline') 
{:error, {:open_error, -10}} 
iex(5)> :erl_ddll.format_error({:open_error, -10}) 
'dlopen(/usr/lib/libreadline.so, 2): image not found' 

मैं पर OSX हूँ और homebrew के माध्यम से libreadline स्थापित है, और मैं /usr/lib में libreadline.dylib पा सकते हैं

,।


[उद्देश्य के बारे में अतिरिक्त नोट्स]

मैं अमृत है, जो एक तुतलाना विभिन्न भाषाओं में लागू (लेकिन अमृत/erlang साथ नहीं) repl है साथ निम्नलिखित (मल) पर प्रयोग किया गया था।

https://github.com/kanaka/mal

कदम का हिस्सा इतिहास के साथ repl लागू कर रहा है, और कुछ भाषाओं ReadLine बाध्यकारी पुस्तकालयों का उपयोग कर रहे वहाँ देशी एक नहीं है, तो।

[एक छोटे से अधिक अद्यतन - 2015/03/22]

मैं एनआईएफ दृष्टिकोण के साथ कोशिश कर रहा था (encurses रूप में इसी तरह के रूप में) ReadLine पुस्तकालय का उपयोग करें। मैं इसे erlang (erl) पर कुछ हद तक काम कर सकता था, लेकिन elixir पक्ष पर फंस गया। सी पुस्तकालयों (रीडलाइन या सिर्फ सादा स्कैनफ़) से इनपुट पढ़ने पर, "मिश्रण रन-ए" या "आईएक्सएक्स" थोड़ा अजीब व्यवहार करता है (कुछ इनपुट छोड़ देता है या अनदेखा करता है), लेकिन कारण नहीं मिला। encurses समान व्यवहार व्यवहार लगता है।

निम्नलिखित मेरे परीक्षण थे।

https://github.com/parroty/ereadline

https://github.com/parroty/readline

मैं rlwrap जैसे अधिक सामान्य दृष्टिकोण के साथ हो रहा है।

+2

संभावित डुप्लिकेट [निरंतर कमांड इतिहास प्राप्त करने के लिए आईईएक्स शैल के आउटपुट को कैसे सहेजना/लॉग करना है?] (Http://stackoverflow.com/questions/22426716/how-to-save-log-the-output- ऑफ-द-आईएसएक्स-शैल-टू-गेट-पर्सिस्टेंट-कमांड-हिस्ट्री) –

+2

मुझे किसी भी रीडलाइन एकीकरण से अवगत नहीं है, लेकिन क्या आप जो हासिल करना चाहते हैं उसके साथ मदद करेंगे? https://github.com/jzellner/encurses –

+0

संदर्भों के लिए धन्यवाद। मैं आईईएक्स के बाहर इतिहास को लागू करने की तलाश में था (मैंने प्रश्न में पृष्ठभूमि को अपडेट किया है)। लेकिन, कुछ तकनीक लागू हो सकती है। मैं उन पर और अधिक जांच करूँगा। मैं भी ncurses की जांच करेंगे। – parroty

उत्तर

6

अस्वीकरण: मैं किसी के बोली लगाने के लिए जूरी-रिगिंग एरलांग के खोल कोड के विषय पर कोई विशेषज्ञ नहीं हूं। इस उत्तर में सुधार या स्पष्टीकरण बहुत स्वागत है। यह भी मेरे लिए एक सीखने की प्रक्रिया थी।

tl; डॉ: erl और iexedlin मॉड्यूल और उनके ReadLine की तरह सुविधाओं को लागू करने tty_sl बंदरगाह के बीच सहयोग पर निर्भर हैं। आप इनका भी उपयोग कर सकते हैं, हालांकि ऐसा करने के लिए आधिकारिक दस्तावेज कम से कम कहने की कमी है।


एरलांग का खोल (जिस पर एलिक्सीर बनाया गया है) एक सामान्य आरईपीएल की तुलना में काफी जटिल है। एक सामान्य आरईपीएल के विपरीत, यह केवल इनपुट पर लूपिंग नहीं कर रहा है और इसका मूल्यांकन कर रहा है; यह वास्तव में एक सामान्य ओटीपी आवेदन की तरह बनाया गया है, पर्यवेक्षण पेड़ों के साथ सभी तरह से नीचे।

This article by the author of Learn You Some Erlang for Great Good! पूरे एरलांग खोल के आर्किटेक्चर पर और जानकारी देता है। संक्षेप में:

  • एक प्लेटफ़ॉर्म-विशिष्ट TTY ड्राइवर user_drv
  • user_drv लिए उपयोगकर्ता इनपुट गुजरता है या तो (अगर यह ^G या ^C प्राप्त करता है) या करने के लिए वर्तमान में चयनित group इनपुट गुजरता है (वहाँ कर सकते हैं खोल प्रबंधन मोड में चला जाता है कई group रों और इसलिए shell रों हो, जब आप ^G दबाते हैं, तो आप आदि) एक साथ की एक पंक्ति डाल करने के लिए और अधिक group रों बनाकर, मौजूदा group रों को स्विच करने का विकल्प, edlin साथ
  • group काम करता है मूल्यांकन के लिए कोड; बार यह एक लाइन है, यह इसके shell
  • shell वास्तविक मूल्यांकन, तो group के परिणाम, groupuser_drv के लिए चीजों को भेजने हैं जो user_drv
  • के परिणाम भेजता भेजता करता है सक्रिय समूह, तो user_drv के लिए भेजता है इसे टीटीवी चालक (और इस प्रकार उपयोगकर्ता को) पास करता है; अन्यथा, यह म्यूट

इस प्रक्रिया का हिस्सा edlin है, जो रीडलाइन जैसी कार्यक्षमता का एरलांग कार्यान्वयन है। edlin, है दुर्भाग्य से, जहाँ तक मैं बता सकता है विशेष रूप से अच्छी तरह से प्रलेखित नहीं, पर अमृत में इसका उपयोग करने का सार (क्या मैं lib/kernel/src/group.erl में से बटोरने के लिए कर सकती हूं पर आधार पर) निम्नलिखित है:

  • कॉल :edlin.initedlin का उपयोग कर रहे प्रक्रिया में (यह "मारने की अंगूठी" की Emacs भावना में "मारने वाला बफर" सेट करता है)
  • जब आप एक पंक्ति पढ़ने के लिए तैयार होते हैं, तो {:more_chars,continuation,requests} = :edlin.start(prompt) पर कॉल करें, जहां prompt एक चार्टलिस्ट का प्रतिनिधित्व करता है - आपने अनुमान लगाया - आपके खोल के कमांड लाइन प्रॉम्प्ट
  • हैंडल requests, जोके रूप में होना चाहिए; Erlang खोल में, "संभाल" का अर्थ है "मुद्रित user_drv को भेजने के लिए किया जा", लेकिन आपके मामले में यह अलग

इस बिंदु, पाशन शुरू होता है पर हो सकता है। प्रत्येक पुनरावृत्ति में, आप :edlin.edit_line(characters,continuation) पर कॉल करेंगे (जहां characters एक वर्ण सूची है, यानी उपयोगकर्ता इनपुट से)।

  • {:done,line,rest,requests}: प्रत्येक कॉल आप निम्नलिखित tuples में से एक दे देंगे edlin एक नई पंक्ति का सामना करना पड़ा और अपने पंक्ति की प्रक्रिया किया जाता है; इस बिंदु पर, आप जो भी चाहें line (आपकी लाइन) और rest (आपकी लाइन के बाद के सभी वर्ण)
  • {:more_chars,continuation,requests}: edlin को और अधिक वर्णों की आवश्यकता है; फोन :edlin.edit_line(characters,continuation)
  • {:blink,continuation,requests}: मैं% 100 यकीन है कि यहाँ हूँ, लेकिन मैं इस
  • {:undefined,character,rest,continuation,requests} (यदि आप टाइप ) जब कर्सर एक मेल ( लिए कूदता है) की तरह edlin पर प्रकाश डाला पात्रों के साथ क्या करना है लगता है: नहीं 100% सुनिश्चित करें कि यहाँ, या तो है, लेकिन मुझे लगता है कि यह आदेश इतिहास

तरह से निपटने बातें सभी मामलों में से कोई लेना देना नहीं है, requests, वर्ण लिख चलती तरह user_drv के लिए निर्देश के लिए इसी बातों के लिए आम तौर पर, tuples की एक सूची हो जाएगा कर्सर, आदि


अगला टीटीई से निपटने का सवाल है। user_drv.erl इसे tty_sl नामक किसी चीज़ के साथ करता है, जो विंडोज और यूनिक्स के विभिन्न संस्करणों के साथ एक एरलांग पोर्ट (यानी एक एरलंग प्रक्रिया की तरह व्यवहार करने के लिए डिज़ाइन किया गया एक बाहरी प्रोग्राम है) है। बुनियादी प्रक्रिया (फिर से, Elixirified):

  • निम्नलिखित को परिभाषित करें (हम इसे बाद में की आवश्यकता होगी):

    def put_int16(num, tail) do # we need this in a bit 
        use Bitwise # because macros 
        [num |> bsr(8) |> band(255), num |> band(255) | tail] 
    end 
    
  • कॉल port = Port.open {:spawn,'tty_sl -c -e'} ("गूंज" के लिए -e, जो कुछ भी के लिए -c "कैनन " माध्यम); user_drv के लिए इस चरण में थोड़ा और त्रुटि-जांच है, जाहिर है कि यह पुराने user को शुरू कर सकता है (जो - उपर्युक्त आलेख के अनुसार - एर्लांग खोल का पुराना संस्करण प्रतीत होता है)

  • स्पिन अप edlin उपयोग करना ऊपर वर्णित प्रक्रिया और उस में की दुकान, कहते हैं, shell
  • प्रारंभ से अनुरोधों को हैंडल करने पाशन (user_drv एक गुच्छा अधिक सामग्री सेटअप कई group रों करने के लिए यहाँ करता है) अपने shell

फिर, लूप में:

  • एक request
  • (वास्तव में एक request ऊपर प्रति रों की सूची) प्राप्त कुछ है कि tty_sl समझता में कनवर्ट प्रत्येक request:

    command = case request do 
        {:put_chars,:unicode,chars} ->   # OP_PUTC 
        {:command, [0|:unicode.characters_to_binary(chars,:utf8)]} 
        {:move_rel,count} ->      # OP_MOVE 
        {:command, [1|put_int16(count, [])]} 
        {:insert_chars,:unicode,chars} ->   # OP_INSC 
        {:command, [2|:unicode.characters_to_binary(chars,:utf8)]} 
        {:delete_chars,count} ->     # OP_DELC 
        {:command, [3|put_int16(count, [])]} 
        :beep ->         # OP_BEEP 
        {:command, [4]} 
        {:put_chars_sync,:unicode,chars,reply} -> # OP_PUTC_SYNC 
        {{:command, [5|:unicode.characters_to_binary(chars,:utf8)]}, reply} 
        else -> 
        else 
    end 
    
  • TTY को command भेजें:

    result = case command do 
        {:requests,requests} -> 
        # Handle more requests 
        {:command,_} = command -> 
        send port, command 
        :ok 
        {command,reply} -> 
        send port, command 
        reply 
        _ -> 
        :ok 
    end 
    

आपको टीटीवी से सामान प्राप्त करने की भी आवश्यकता होगी।user_drv के मामले में, TTY प्रक्रियाओं के रूप में उसी user_drv प्रक्रिया को संदेश भेजता है। किसी भी मामले में, आप edlin द्वारा group के माध्यम से भेजे गए अनुरोध के अलावा कुछ अतिरिक्त संदेशों को संभालने के लिए चाहता हूँ:

  • {port,{:data,bytes}}: अक्षर में बदलें bytes और अपने खोल करने के लिए भेज देते हैं। चूंकि हम एलिक्सीर-भूमि में हैं, इसलिए हमें रूपांतरण करने की भी आवश्यकता नहीं है।
  • {port,:eof}: इसी तरह का सौदा; user_drv से नहीं 100% इस बारे में निश्चित है, लेकिन मेरा मानना ​​है कि यह :put_chars_sync आदेश के साथ क्या करना है, यह देखते हुए कि user_drv में कोड एक Reply चर के साथ और कहा कि इस संदेश को सौदों को संभालने के लिए: अपने खोल
  • {port,:ok} को :eof भेज (: दोनों tty_sl और विभिन्न group रों के लिए प्रक्रिया रास्ते से निपटने अर्थात्) केवल tty_sl एक उत्तर से जुड़े आदेश :put_chars_sync

संदेशों user_drv में संभाला के बाकी पर्यवेक्षण पेड़ से संबंधित है।


बेशक

, वहाँ शायद इस सब के लिए एक बहुत सरल जवाब है: बस user_drv का उपयोग करें और अपने आप को एक नया shell पैदा करते हैं। यह किया जा सकता है (मुझे लगता है; यहां 100% निश्चित नहीं है) user_drv_pid = :user_drv.start('tty_sl -c -e', {MyShell,:start}) की लाइनों के साथ कुछ के साथ। ऐसा लगता है कि iex काम करता है (देखें IEx.CLI.start/0)।

+0

वाह - एक बहुत ही विस्तृत उत्तर, कुछ ऐसा करने के लिए जो मैं उम्मीद करता हूं कि elixir लोगों को डिफ़ॉल्ट रूप से लंबे समय से पहले से ही हल कर लिया होगा। ऐसा लगता है कि वे इंटरैक्टिव आईईएक्स का उपयोग नहीं करते हैं। – shevy

संबंधित मुद्दे