टाइनी लिस्प, छोटे दुभाषिया


33

लिस्प प्रोग्रामर्स का दावा है कि लिस्प एक शक्तिशाली भाषा है, जिसे आदिम परिचालनों के बहुत छोटे समूह से बनाया जा सकता है । आइए उस विचार को एक बोली के लिए दुभाषिया को गोल्फ द्वारा अभ्यास में लाएं tinylisp

भाषा विनिर्देशन

इस विनिर्देशन में, किसी भी स्थिति जिसका परिणाम "अपरिभाषित" के रूप में वर्णित है, वह आपके दुभाषिया में कुछ भी कर सकता है: दुर्घटना, चुपचाप विफल, यादृच्छिक gobbldegook का उत्पादन, या अपेक्षा के अनुसार काम करें। पायथन 3 में एक संदर्भ कार्यान्वयन उपलब्ध है यहां है

वाक्य - विन्यास

Tinylisp में टोकन हैं (, )या कोष्ठक या अंतरिक्ष को छोड़कर एक या अधिक प्रिंट योग्य ASCII वर्ण के किसी भी स्ट्रिंग। (यानी निम्नलिखित regex:। [()]|[^() ]+) कोई भी टोकन जिसमें पूरी तरह से अंक होते हैं , पूर्णांक शाब्दिक होता है। (अग्रणी शून्य ठीक हैं।) किसी भी टोकन है कि गैर अंक हैं, एक प्रतीक है भी सांख्यिक दिखने उदाहरण की तरह है 123abc, 3.14और -10। सभी व्हाट्सएप (एक न्यूनतम, एएससीआईआई अक्षर 32 और 10 में) को नजरअंदाज कर दिया जाता है, इंसोफर को छोड़कर क्योंकि यह टोकन को अलग करता है।

एक टिनिइलिस प्रोग्राम में अभिव्यक्तियों की एक श्रृंखला होती है। प्रत्येक अभिव्यक्ति या तो एक पूर्णांक, एक प्रतीक, या एक एस-अभिव्यक्ति (सूची) है। कोष्ठक में लिपटे शून्य या अधिक अभिव्यक्तियों से संबंधित सूची। वस्तुओं के बीच किसी विभाजक का उपयोग नहीं किया जाता है। यहाँ अभिव्यक्ति के उदाहरण हैं:

4
tinylisp!!
()
(c b a)
(q ((1 2)(3 4)))

अभिव्यक्तियाँ जो अच्छी तरह से गठित नहीं होती हैं (विशेष रूप से, जिसमें बेजोड़ कोष्ठक होते हैं) अपरिभाषित व्यवहार देती हैं। (संदर्भ कार्यान्वयन ऑटो खुले हुए पार्न्स को बंद कर देता है और बेजोड़ करीबी पार्न्स पर पार्स करना बंद कर देता है।)

जानकारी का प्रकार

टिनिस्प के डेटा प्रकार पूर्णांक, प्रतीक और सूची हैं। अंतर्निहित कार्यों और मैक्रोज़ को भी एक प्रकार माना जा सकता है, हालांकि उनका आउटपुट स्वरूप अपरिभाषित है। एक सूची में किसी भी प्रकार के मूल्यों की संख्या हो सकती है और मनमाने ढंग से गहराई से घोंसला बनाया जा सकता है। पूर्णांक को कम से कम -2 ^ 31 से 2 ^ 31-1 तक समर्थित होना चाहिए।

खाली सूची - ()नालो को शून्य के रूप में संदर्भित किया जाता है - और पूर्णांक 0एकमात्र मान हैं जो तार्किक रूप से गलत माना जाता है; अन्य सभी पूर्णांकों, गैर-रिक्त सूचियों, बिल्डिंस और सभी प्रतीकों तार्किक रूप से सत्य हैं।

मूल्यांकन

एक कार्यक्रम में अभिव्यक्तियों का मूल्यांकन क्रम में किया जाता है और प्रत्येक के परिणाम स्टडआउट को भेजे जाते हैं (बाद में आउटपुट स्वरूपण पर अधिक)।

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

अंतर्निहित कार्यों और मैक्रोज़

टिनिइलिश में सात अंतर्निहित कार्य हैं। एक फ़ंक्शन उनके कुछ कार्यों को लागू करने और परिणाम को वापस करने से पहले इसके प्रत्येक तर्क का मूल्यांकन करता है।

  • c- वाणिज्यदूत [सूची सूची]। दो तर्क, एक मूल्य और एक सूची लेता है, और सूची के सामने मूल्य जोड़कर प्राप्त एक नई सूची देता है।
  • h- सिर ( कार , लिस्प शब्दावली में)। एक सूची लेता है और इसमें पहला आइटम देता है, या शून्य दिए जाने पर शून्य कर देता है।
  • t- पूंछ ( सीडीआर , लिस्प शब्दावली में)। एक सूची लेता है और एक नई सूची देता है जिसमें सभी आइटम होते हैं, लेकिन पहले आइटम या शून्य दिए जाते हैं।
  • s- घटाना। दो पूर्णांकों को लेता है और पहले ऋण को दूसरा देता है।
  • l- से कम। दो पूर्णांक लेता है; 1 रिटर्न अगर पहले दूसरे से कम है, तो 0।
  • e- बराबरी का। एक ही प्रकार के दो मान लेता है (दोनों पूर्णांक, दोनों सूची, या दोनों प्रतीक); 1 लौटाता है यदि दोनों समान हैं (या प्रत्येक तत्व में समान), 0 अन्यथा। समानता के लिए परीक्षण निर्माण अपरिभाषित है (संदर्भ कार्यान्वयन अपेक्षित रूप से कार्य करता है)।
  • v- eval। एक सूची, पूर्णांक या प्रतीक लेता है, एक अभिव्यक्ति का प्रतिनिधित्व करता है, और इसका मूल्यांकन करता है। जैसे करना (v (q (c a b)))वैसा ही करना (c a b); (v 1)देता है 1

यहां "मान" में किसी भी सूची, पूर्णांक, प्रतीक, या बिलिन शामिल हैं, जब तक कि अन्यथा निर्दिष्ट न हो। यदि किसी फ़ंक्शन को विशिष्ट प्रकारों के रूप में सूचीबद्ध किया जाता है, तो विभिन्न प्रकारों को पास करना अपरिभाषित व्यवहार होता है, जैसा कि गलत संख्या में तर्कों को पारित करना है (संदर्भ कार्यान्वयन आमतौर पर क्रैश होता है)।

टिनिइस्प में तीन अंतर्निहित मैक्रोज़ हैं। एक मैक्रो, एक फ़ंक्शन के विपरीत, उन्हें ऑपरेशन लागू करने से पहले इसके तर्कों का मूल्यांकन नहीं करता है।

  • q- बोली। एक अभिव्यक्ति लेता है और इसे बिना शर्त लौटाता है। उदाहरण के लिए, मूल्यांकन (1 2 3)करने में त्रुटि होती है क्योंकि यह कॉल करने का प्रयास करता है1 एक फ़ंक्शन या मैक्रो के रूप में , लेकिन (q (1 2 3))सूची को लौटाता है (1 2 3)। मूल्यांकन aकरने से नाम को बाध्य मान मिलता है a, लेकिन (q a)नाम ही देता है।
  • i- अगर। तीन अभिव्यक्ति लेता है: एक शर्त, एक iftrue अभिव्यक्ति, और एक iffalse अभिव्यक्ति। पहले स्थिति का मूल्यांकन करता है। यदि परिणाम गलत है ( 0या शून्य), मूल्यांकन करता है और iffalse अभिव्यक्ति देता है। अन्यथा, iftrue अभिव्यक्ति का मूल्यांकन और रिटर्न करता है। ध्यान दें कि जो अभिव्यक्ति वापस नहीं हुई है, उसका मूल्यांकन कभी नहीं किया जाता है।
  • d- हार। एक प्रतीक और एक अभिव्यक्ति लेता है। अभिव्यक्ति का मूल्यांकन करता है और उसे वैश्विक स्तर पर एक नाम के रूप में दिए गए प्रतीक को बांधता है , फिर प्रतीक लौटाता है। एक नाम को फिर से परिभाषित करने की कोशिश करना चाहिए (चुपचाप, संदेश के साथ, या दुर्घटनाग्रस्त होने से; संदर्भ कार्यान्वयन एक त्रुटि संदेश प्रदर्शित करता है)। नोट: नाम को पास करने से पहले उसे उद्धृत करना आवश्यक नहीं है d, हालांकि अभिव्यक्ति को उद्धृत करना आवश्यक है यदि यह एक सूची या प्रतीक है जिसका आप मूल्यांकन नहीं करना चाहते हैं: जैसे (d x (q (1 2 3))),।

एक मैक्रो को तर्कों की गलत संख्या पास करना अपरिभाषित व्यवहार (संदर्भ कार्यान्वयन क्रैश) है। ऐसा कुछ पास करना जो एक प्रतीक नहीं है क्योंकि इसका पहला तर्क dअपरिभाषित व्यवहार है (संदर्भ कार्यान्वयन त्रुटि नहीं देता है, लेकिन मूल्य बाद में संदर्भित नहीं किया जा सकता है)।

उपयोगकर्ता-परिभाषित कार्य और मैक्रोज़

इन दस बिल्ट-इन से शुरू करके, नए कार्यों और मैक्रोज़ का निर्माण करके भाषा को बढ़ाया जा सकता है। इनका कोई समर्पित डेटा प्रकार नहीं है; वे एक निश्चित संरचना के साथ बस सूचियाँ हैं:

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

उदाहरण के लिए, निम्न अभिव्यक्ति एक फ़ंक्शन है जो दो पूर्णांक जोड़ता है:

(q               List must be quoted to prevent evaluation
 (
  (x y)          Parameter names
  (s x (s 0 y))  Expression (in infix, x - (0 - y))
 )   
)

और एक मैक्रो जो किसी भी संख्या में तर्क लेता है और पहले का मूल्यांकन करता है और वापस करता है:

(q
 (
  ()
  args
  (v (h args))
 )
)

फ़ंक्शंस और मैक्रोज़ को सीधे कहा जा सकता है, उपयोग करने वाले नामों के लिए बाध्य है d, और अन्य कार्यों या मैक्रोज़ को पारित किया गया है।

चूंकि फ़ंक्शन बॉडी को परिभाषा समय पर निष्पादित नहीं किया जाता है, इसलिए पुनरावर्ती कार्य आसानी से निश्चित हैं:

(d len
 (q (
  (list)
  (i list                      If list is nonempty
   (s 1 (s 0 (len (t list))))  1 - (0 - len(tail(list)))
   0                           else 0
  )
 ))
)

ध्यान दें, हालांकि, ऊपर एक लंबाई फ़ंक्शन को परिभाषित करने का एक अच्छा तरीका नहीं है क्योंकि यह उपयोग नहीं करता है ...

टेल-कॉल पुनरावृत्ति

लिस्प में टेल-कॉल पुनरावृत्ति एक महत्वपूर्ण अवधारणा है। यह कुछ प्रकार के पुनरावृत्ति को छोरों के रूप में लागू करता है, इस प्रकार कॉल स्टैक को छोटा रखता है। आपके टिनिइलिस्प दुभाषिया को उचित टेल-कॉल पुनरावर्तन लागू करना चाहिए !

  • यदि उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो की वापसी अभिव्यक्ति किसी अन्य उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो के लिए एक कॉल है, तो आपके दुभाषिए को उस कॉल का मूल्यांकन करने के लिए पुनरावर्तन का उपयोग नहीं करना चाहिए। इसके बजाय, यह वर्तमान फ़ंक्शन और तर्कों को नए फ़ंक्शन और तर्कों और लूप से बदल देना चाहिए जब तक कि कॉल की श्रृंखला हल न हो जाए।
  • यदि उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो की वापसी अभिव्यक्ति कॉल है i, तो उस शाखा का तुरंत मूल्यांकन न करें जो चयनित है। इसके बजाय, जांचें कि क्या यह किसी अन्य उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो के लिए कॉल है। यदि हां, तो ऊपर दिए गए फ़ंक्शन और तर्कों को स्वैप करें। यह मनमाने ढंग से गहरी नेस्टेड घटनाओं के लिए लागू होता है i

टेल रिकर्शन को डायरेक्ट रिकर्सन (एक फंक्शन कॉल खुद) और इनडायरेक्ट रिकर्सन (फंक्शन aकॉल फंक्शन bजिसे कॉल करता है [आदि] जिसे फंक्शन कहते हैं) दोनों के लिए काम करना चाहिए a

एक पूंछ-पुनरावर्ती लंबाई समारोह (एक सहायक कार्य के साथ len*):

(d len*
 (q (
  (list accum)
  (i list
   (len*
    (t list)
    (s 1 (s 0 accum))
   )
   accum
  )
 ))
)
(d len
 (q (
  (list)
  (len* list 0)
 ))
)

यह कार्यान्वयन मनमाने ढंग से बड़ी सूचियों के लिए काम करता है, जो केवल अधिकतम पूर्णांक आकार द्वारा सीमित है।

क्षेत्र

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

(d x 42)
(d f
 (q (
  (x)
  (s x 1)
 ))
)
(f 6)

हालाँकि, निम्न कोड 41 देता है, क्योंकि xकॉल स्तर 1 कॉल स्तर 2 से सुलभ नहीं है:

(d x 42)
(d f
 (q (
  (x)
  (g 15)
 ))
)
(d g
 (q (
  (y)
  (s x 1)
 ))
)
(f 6)

किसी भी समय के दायरे में केवल नाम 1) वर्तमान में निष्पादित फ़ंक्शन के स्थानीय नाम, यदि कोई हो, और 2) वैश्विक नाम हैं।

प्रस्तुत करने की आवश्यकताएँ

इनपुट और आउटपुट

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

  • इंटेगर को आपके कार्यान्वयन की भाषा के सबसे प्राकृतिक प्रतिनिधित्व में आउटपुट होना चाहिए। ऋणात्मक पूर्णांकों का उत्पादन किया जा सकता है, जिसमें प्रमुख ऋण चिन्ह होते हैं।
  • प्रतीकों को स्ट्रिंग के रूप में आउटपुट किया जाना चाहिए, जिसमें कोई आसपास के उद्धरण या भाग न हों।
  • सूची को सभी वस्तुओं के साथ आउटपुट किया जाना चाहिए जो अलग-अलग हैं और कोष्ठक में लिपटे हुए हैं। कोष्ठकों के अंदर एक स्थान वैकल्पिक है: (1 2 3)और( 1 2 3 ) दोनों स्वीकार्य प्रारूप हैं।
  • निर्मित कार्यों और मैक्रो का आउटपुट अपरिभाषित व्यवहार है। (संदर्भ व्याख्या उन्हें प्रदर्शित करती है <built-in function>।)

अन्य

संदर्भ दुभाषिया में एक REPL पर्यावरण और अन्य फ़ाइलों से टिनिइल मॉड्यूल को लोड करने की क्षमता शामिल है; ये सुविधा के लिए प्रदान किए जाते हैं और इस चुनौती के लिए आवश्यक नहीं हैं।

परीक्षण के मामलों

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

यदि आपने टेल-कॉल पुनरावृत्ति को ठीक से लागू किया है, तो अंतिम (बहु-भाग) परीक्षण मामला स्टैक ओवरफ्लो पैदा किए बिना वापस आ जाएगा। संदर्भ कार्यान्वयन मेरे लैपटॉप पर लगभग छह सेकंड में गणना करता है।


"कोई भी टोकन जिसमें पूरी तरह से अंक होते हैं, एक पूर्णांक शाब्दिक होता है। (लीडिंग शून्य ठीक है।) कोई भी टोकन जिसमें गैर-अंक होते हैं, एक प्रतीक है, यहां तक ​​कि संख्यात्मक-उदाहरण जैसे 123abc, 3.14, और -10 भी।" विरोधाभास लगता है "इंटर्ज़ को कम से कम -2 ^ 31 से 2 ^ 31-1 तक समर्थित होना चाहिए।"
msh210

3
@ msh210 वास्तव में नहीं है, क्योंकि पूर्व टोकन के बारे में बात कर रहा है जबकि बाद के मूल्यों के बारे में बात कर रहा है । हालांकि प्रवेश करने का कोई सीधा रास्ता नहीं है -1, फिर भी मैं मूल्य -1 को उत्पन्न कर सकता हूं (s 0 1)
DLosc

1
@coredump प्रासंगिक विकिपीडिया लेख पढ़ने के बाद , मैंने निष्कर्ष निकाला है कि कार्यान्वयन वास्तव में गतिशील के करीब है, लेकिन कोई गुंजाइश नहीं है। फ़ंक्शन में वेरिएबल्स फ़ंक्शन Fमें उपलब्ध नहीं हैं Gयदि Fकॉल G(डायनेमिक स्कूपिंग के साथ), लेकिन वे फ़ंक्शन में भी उपलब्ध नहीं हैं Hयदि Hकोई नेस्टेड फ़ंक्शन अंदर F(लेक्सिकल स्कूपिंग के साथ) के रूप में परिभाषित किया गया है - परीक्षण केस देखें 5. तो इसे "लेक्सिकल “भ्रामक हो सकता है।
DLosc

1
इसे दूसरे तरीके से रखने के लिए: गुंजाइश घोंसले के शिकार की कमी के कारण, एक कार्यान्वयन या तो एक गतिशील या एक लेक्सिकल स्कोपिंग रणनीति का उपयोग कर सकता है और एक ही परिणाम के साथ आ सकता है। किसी भी समय के दायरे में केवल नाम 1) वर्तमान में निष्पादित फ़ंक्शन के स्थानीय नाम, यदि कोई हो, और 2) वैश्विक नाम हैं। क्लोजर समर्थित नहीं हैं। (संदर्भ कार्यान्वयन कॉल स्टैक के अनुरूप नाम बाइंडिंग का एक स्टैक रखता है - एक गतिशील-शैली का दृष्टिकोण, जो मुझे लगता है कि इसे लागू करना सबसे आसान होगा।)
18

1
अप्रचलित Xkcd
mnnxomaτ

जवाबों:


11

अजगर 2, 685 675 660 657 646 642 640 बाइट्स

import sys,re
E=[]
G=zip("chtsle",[eval("lambda x,y=0:"+f)for f
in"[x]+y (x+[E])[0] x[1:] x-y +(x<y) +(x==y)".split()])
def V(e,L=E):
 while 1:
    try:return e and int("0%s"%e)
    except:A=e[1:]
    if""<e:return dict(G+L).get(e,e)
    f=V(e[0],L)
    if""<f:
     if f in"iv":t=V(A[0],L);e=(e[~bool(t)],t)[f>"u"];continue
     if"e">f:G[:]+=(A[0],V(A[1],L)),
     return A[0]
    if[]>f or f[0]:A=[V(a,L)for a in A]
    if[]>f:return f(*A)
    P,e=f[-2:];L=([(P,A)],zip(P,A))[P<""]
F=lambda x:V<x<""and"(%s)"%" ".join(map(F,x))or"%s"%x
for t in re.sub("([()])"," \\1 ",sys.stdin.read()).split():
 if")"==t:t=E.pop()
 if"("==t:E+=[],
 elif E:E[-1]+=t,
 else:print F(V(t))

STDIN से इनपुट पढ़ता है और STDOUT में आउटपुट लिखता है।

हालांकि कड़ाई की आवश्यकता नहीं है, दुभाषिया अशक्त कार्यों और मैक्रोज़ का समर्थन करता है, और टेल कॉल के माध्यम से निष्पादित करता है v

व्याख्या

पदच्छेद

इनपुट को पार्स करने के लिए, हम सबसे पहले (और )रिक्त स्थान के साथ प्रत्येक घटना को घेरते हैं , और परिणामस्वरूप स्ट्रिंग को शब्दों में विभाजित करते हैं; यह हमें टोकन की सूची देता है। हम एक अभिव्यक्ति स्टैक बनाए रखते हैं E, जो शुरू में खाली है। हम क्रम में टोकन स्कैन करते हैं:

  • यदि हमारा सामना होता है (, तो हम अभिव्यक्ति स्टैक के शीर्ष पर एक खाली सूची को धक्का देते हैं;
  • अगर हमारा सामना होता है ), तो हम अभिव्यक्ति स्टैक के शीर्ष पर मूल्य पॉप करते हैं, और इसे उस सूची में जोड़ते हैं जो पहले स्टैक पर नीचे थी;
  • अन्यथा, हम अभिव्यक्ति स्टैक के शीर्ष पर सूची में एक स्ट्रिंग के रूप में वर्तमान टोकन को जोड़ते हैं, (हम पूर्णांक को इस स्तर पर स्ट्रिंग के रूप में रखते हैं, और मूल्यांकन के दौरान उन्हें पार्स करते हैं।)

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

मूल्यांकन

हम वैश्विक गुंजाइश बनाए रखते हैं G, कुंजी / मूल्य जोड़े की सूची के रूप में। प्रारंभ में, इसमें केवल अंतर्निहित कार्य शामिल हैं (लेकिन मैक्रोज़ नहीं v, और नहीं , जिसे हम एक मैक्रो के रूप में मानते हैं), जिसे लैम्ब्डा के रूप में लागू किया जाता है।

मूल्यांकन अंदर होता है V(), जो मूल्यांकन करने के लिए अभिव्यक्ति लेता है e, और स्थानीय गुंजाइश, Lजो कि, भी, कुंजी / मूल्य जोड़े की एक सूची है (जब एक शीर्ष-स्तरीय अभिव्यक्ति का मूल्यांकन किया जाता है, तो स्थानीय गुंजाइश खाली है।) V()जीने की हिम्मत। एक अनंत लूप के अंदर, जो कि हम पूंछ-कॉल अनुकूलन (TCO) कैसे करते हैं, जैसा कि बाद में बताया गया है।

हम eइसके प्रकार के अनुसार प्रक्रिया करते हैं :

  • यदि यह खाली सूची है, या एक स्ट्रिंग एक int के लिए परिवर्तनीय है, हम इसे तुरंत वापस कर देते हैं (संभवतः int में रूपांतरण के बाद); अन्यथा,

  • यदि यह एक तार है, तो हम इसे वैश्विक और स्थानीय दायरे के संयोजन से निर्मित एक शब्दकोश में देखते हैं। यदि हम एक संबद्ध मूल्य पाते हैं, तो हम इसे वापस करते हैं; अन्यथा, eअंतर्निहित मैक्रो (यानी के नाम होना चाहिए q, i, dया v), और हम इसे अपरिवर्तित लौटने। अन्यथा, यदि eकोई स्ट्रिंग नहीं है,

  • eएक (गैर-रिक्त) सूची है, अर्थात, एक फ़ंक्शन कॉल। हम सूची के पहले तत्व का मूल्यांकन करते हैं, अर्थात्, फ़ंक्शन अभिव्यक्ति, V()पुनरावर्ती कॉल करके (वर्तमान स्थानीय क्षेत्र का उपयोग करके); हम परिणाम कहते हैं f। बाकी सूची, Aतर्कों की सूची है। fकेवल एक स्ट्रिंग हो सकती है, जिस स्थिति में यह एक बिलियन मैक्रो (या फ़ंक्शन v) है, एक लैम्ब्डा, जिस स्थिति में यह एक बिल्डिन फ़ंक्शन, या एक सूची है, जिस स्थिति में यह उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो है।

    यदि fएक स्ट्रिंग, यानी, एक बिलियन मैक्रो है, तो हम इसे जगह में संभालते हैं। यदि यह मैक्रो है iया v, हम इसके पहले ऑपरेंड का मूल्यांकन करते हैं, और या तो के मामले में दूसरे या तीसरे ऑपरेंड का चयन करते हैं i, या के मामले में पहले ऑपरेंड के परिणाम का उपयोग करते हैं v; पुनरावर्ती रूप से चयनित अभिव्यक्ति का मूल्यांकन करने के बजाय, जो TCO को हरा देगा, हम बस eउक्त अभिव्यक्ति के साथ प्रतिस्थापित करते हैं, और लूप की शुरुआत में कूदते हैं। यदि fमैक्रो है d, तो हम एक जोड़ी को जोड़ते हैं, जिसका पहला तत्व पहला ऑपरेंड है, और जिसका दूसरा तत्व दूसरे ऑपरेंड के मूल्यांकन का परिणाम है, वैश्विक दायरे में G, और पहला ऑपरेंड वापस लौटाता है। अन्यथा, fवह मैक्रो है q, जिस स्थिति में हम सीधे उसके ऑपरेंड को सीधे लौटाते हैं।

    Othrtwise, अगर fएक लैम्ब्डा है, या एक सूची जिसका पहला तत्व नहीं है (), तो यह एक गैर-शून्य फ़ंक्शन है, मैक्रो नहीं है, जिस स्थिति में हम इसके तर्कों का मूल्यांकन करते हैं, अर्थात, और तत्वों के परिणाम के साथ Aप्रतिस्थापित Aकरते हैं।

    यदि fएक लंबोदर है, तो हम इसे अनपैक किए गए तर्कों को पारित करते हुए कहते हैं A, और परिणाम लौटाते हैं।

    अन्यथा, fएक सूची है, यानी, उपयोगकर्ता द्वारा परिभाषित फ़ंक्शन या मैक्रो; इसकी पैरामीटर सूची दूसरा-से-अंतिम तत्व है, और इसका शरीर अंतिम तत्व है। जैसे कि मैक्रोज़ के मामले में iऔर v, TCO प्रदर्शन करने के लिए, हम पुनरावर्ती रूप से शरीर का मूल्यांकन नहीं करते हैं, बल्कि eशरीर के साथ प्रतिस्थापित करते हैं और अगले पुनरावृत्ति को जारी रखते हैं। विपरीत iऔर v, फिर भी, हम भी स्थानीय गुंजाइश की जगह, L, समारोह के नए स्थानीय क्षेत्र के साथ। यदि पैरामीटर सूची, Pवास्तव में, एक सूची है, Pतो तर्क सूची के साथ , पैरामीटर सूची को ज़िप करके नए स्थानीय क्षेत्र का निर्माण किया जाता है A; अन्यथा, हम एक वैरेडिक फ़ंक्शन के साथ काम कर रहे हैं, जिस स्थिति में नए स्थानीय दायरे में केवल एक तत्व है, जोड़ी (P, A)

आरईपीएल

यदि आप इसके साथ खेलना चाहते हैं, तो दुभाषिया का एक REPL संस्करण है। यह प्रतीकों को पुनर्परिभाषित करने, और कमांड लाइन के तर्कों, या (import <filename>)मैक्रो के माध्यम से फ़ाइलों को आयात करने का समर्थन करता है । दुभाषिया से बाहर निकलने के लिए, इनपुट (आमतौर पर, Ctrl + D या Ctrl + Z) को समाप्त करें।

और यहाँ एक उदाहरण सत्र है, जो मर्ज प्रकार को लागू कर रहा है:


आप zlib का उपयोग करके कभी भी कुछ छोटा कर सकते हैं :) अपने कोड को बाइट्स में परिवर्तित करें, और इसे प्रतिस्थापित करें:import zlib;exec(zlib.decompress(your_code_compressed_in_bytes))
Labo

आप A[0]ब्लॉक को छोड़कर कुछ एक-चर चर को असाइन करके दो बाइट्स बचा सकते हैं
हेंस कारपिला

@HannesKarppila यह सही है, लेकिन यह अशक्त कार्यों को तोड़ देगा (क्योंकि Aइस मामले में खाली है), और मैं "पुनः प्राप्त" नहीं करना चाहता।
एलएल

4

सी (जीएनयू), 1095 बाइट्स

ज्यादातर कार्रवाई विशाल में होती है v समारोह में होती है। स्पष्ट रूप से पूंछ प्रत्यावर्तन को लागू करने के बजाय, vइसलिए से कॉल की है कि कई संरचित है vकरने के लिए vजीसीसी की पूंछ प्रत्यावर्तन अनुकूलन द्वारा नियंत्रित किया जाएगा। कोई कचरा संग्रह नहीं है।

यह जीसीसी एक्सटेंशन का भारी उपयोग करता है, इसलिए इसे केवल जीसीसी के साथ संकलित किया जा सकता है (कमांड का उपयोग करें gcc -w -Os tl.c)। यह कुछ scanfएक्सटेंशनों का भी उपयोग करता है जो विंडोज पर उपलब्ध नहीं थे, जिन्हें मैं आमतौर पर उपयोग करता हूं। मानक के साथ पार्सर लिखने की संभावना scanfइतनी भयानक थी कि मैंने प्रोग्राम का परीक्षण करने के लिए लिनक्स वीएम का उपयोग किया। scanfचरित्र वर्गों के बिना पार्सिंग ने शायद 100+ बाइट्स जोड़े होंगे।

#define O(...)({o*_=malloc(32);*_=(o){__VA_ARGS__};_;})
#define P printf
#define F(I,B)({for(I;x->c;x=x->l)B;})
#define Z return
typedef struct o{struct o*n,*l,*c;int i,t;}o;E(o a,o b){Z
a.n?!strcmp(a.n,b.n):a.c?b.c&&E(*a.c,*b.c)&E(*a.l,*b.l):!b.c&a.i==b.i;}p(o*x){x->t?P("%d ",x->i):x->n?P("%s ",x->n):F(P("("),p(x->c);P(")"));}o*x,G,N;*C(o*h,o*t){Z
O(c:h,l:t);}o*v(o*x,o*e){o*W(o*l,o*e){Z
l->c?C(v(l->c,e),W(l->l,e)):&N;}o*y,*a,*f;int t;Z
x->c?y=v(x->c,e),x=x->l,t=y->i,t?9/t?a=v(x->c,e),t>7?(t>8?a->c:a->l)?:a:t>6?v(a,e):t<6?x=v(x->l->c,e),t>4?C(a,x):O(t:1,i:t>3?E(*a,*x):t>2?a->i<x->i:a->i-x->i):v((a-&N&&!a->t|a->i?x:x->l)->l->c,e):(t&1&&d(x->c->n,v(x->l->c,e)),x->c):(y->l->l->l?y=y->l:(x=W(x,e)),a=y->c,v(y->l->c,a->n?O(n:a->n,c:x,l:&G):F(f=&G,(f=O(n:a->c->n,c:x->c,l:f),a=a->l);f))):x->n?e->n?strcmp(x->n,e->n)?v(x,e->l):e->c:e:x;}d(o*n,o*x){*v(O(n:""),&G)=(o){n:n,c:x,l:O()};}*R(h){char*z,*q;Z
scanf(" %m[^ \n()]",&q)>0?h=strtoul(q,&z,10),C(*z?O(n:q):O(t:1,i:h),R()):~getchar()&1?q=R(),C(q,R()):&N;}main(i){for(;++i<12;)d(strndup("slecivthqd"+i-2,1),O(i:i));F(x=R(),p(v(x->c,&G)));}

अर्द्ध ungolfed

typedef struct o o;
struct o {
    char* n;
    o* l, //next in this list
     * c; 
    int i,
        t;
} ;



#define O(...)({o*_=malloc(32);*_=(o){__VA_ARGS__};_;})

E(o a, o b) { //tests equality 
    return
        a.n ? !strcmp(a.n,b.n) :
        a.t ? a.i==b.i :
        a.c ? b.c && E(*a.c,*b.c)&E(*a.l,*b.l) :
        !b.c
    ;
}

#define P printf


p(o*x){
    x->t?P("%d ",x->i):x->n?P("%s ",x->n):({for(P("(");x->c;x=x->l)p(x->c);P(")");});
}


o*_,G,N; //N = nil



o*C(o*h,o*t){return O(c:h,l:t);}


/*
        2 3 4 5 6 7 8 9 10 11
        s l e c i v t h d  q
    */


o* v(o* x, o* e) { //takes list, int, or name
    o*W(o* l, o* e) { //eval each item in list
        return l->c ? C(v(l->c ,e), W(l->l, e)) : &N;
    }

    o*y,*a,*f;int t;
    return x->c ? //nonempty list = function/macro call
        y = v(x->c,e), //evals to function/macro
        x = x->l,   //list position of first arg (if it exists)
        (t=y->t)?   //builtin no., if any
             t>9 ?
              t&1 ? x->c // 11 = q
                : //10 = d
                (d(x->c,v(x->l->c,e)),x->c)
           : (a = v(x->c,e), //eval'd first arg
             t)>7 ? // t/h
                (t > 8 ? a->c : a->l) ?: a
           : t>6 ? //v
                v(a,e)
           : (x = x->l, //position of 2nd arg in list
             t)>5 ? //i
                v( (a->n||a->l||a->i|a->t>1 ? x : x->l)->c, e)
           : (x = v(x->c,e), //evaluated 2nd arg
             t)>4 ? // c
                C(a,x)
           : O(t:1,i:
                t>3 ? E(*a,*x) :  //e
                t>2 ? a->i<x->i : //l
                      a->i-x->i   //s
              )
        :
        (
            y->l->l->l ? //whether this is macro
                y = y->l :
                (x = W(x,e)),  //eval args
            a = y->c,  //a = arg list
            //a = a->n ? x=C(x, &N), C(a, &N) : a, //transform variadic style to normal
            v(y->l->c,
               a->n ? //variadic
                O(n:a->n,c:x,l:&G)
              : ({
                   for(f=&G; a->c; a=a->l,x=x->l)
                      f=O(n:a->c->n, c: x->c, l:f);
                   f;
                })
            )
        )
    :
    x->n ? // name
        e->n ?
            strcmp(x->n,e->n) ?
                v(x,e->l)
            : e->c
        : e
     : x; //int or nil
}

d(o*n,o*x){
    * v(O(n:""),&G) =
        (o){n:n->n,c:x,l:O()};
}


;
o*R(){
    char*z,*q;int h;
return scanf(" %m[^ \n()]",&q)>0?
    h=strtoul(q,&z,10),
    C(*z ? O(n:q) : O(t:1,i:h), R())
: getchar()&1?&N:(q=R(),C(q,R()));
}
main(i) {

    for(;++i<12;) d(O(n:strndup("slecivthdq"+i-2,1)),O(t:i));

    o *q;
    for(q=R(); q->c; q=q->l) p(v(q->c,&G));

}

संकलित निष्पादन योग्य का उपयोग क्या है? क्या यह REPL है? क्या यह इनपुट के रूप में फ़ाइल नाम लेता है?
ckjbgames

@ckjbgames यह स्टड से एक कार्यक्रम पढ़ता है।
feersum

ठीक है। मुझे लगता है कि आपको अपना उत्तर संपादित करना चाहिए और ध्यान देना चाहिए।
ckjbgames

1

सीलोन, 2422 बाइट्स

(मुझे लगता है कि यह मेरा अब तक का सबसे लंबा गोल्फ कार्यक्रम है।)

import ceylon.language{sh=shared,va=variable,fo=formal,O=Object}import ceylon.language.meta.model{F=Function}interface X{sh fo V v(S t);sh fo G g;}class G(va Map<S,V>m)satisfies X{v(S t)=>m[t]else nV;g=>this;sh S d(S s,V v){assert(!s in m);m=map{s->v,*m};return s;}}V nV=>nothing;class LC(G c,Map<S,V>m)satisfies X{g=>c;v(S t)=>m[t]else g.v(t);}alias C=>V|Co;interface Co{sh fo C st();}interface V{sh fo C l(X c,V[]a);sh default Boolean b=>0<1;sh fo C vO(X c);sh default V vF(X c){va C v=vO(c);while(is Co n=v){v=n.st();}assert(is V r=v);return r;}}class L(sh V*i)satisfies V{vO(X c)=>if(nonempty i)then i[0].vF(c).l(c,i.rest)else this;equals(O o)=>if(is L o)then i==o.i else 1<0;b=>!i.empty;string=>"(``" ".join(i)``)";hash=>i.hash;sh actual C l(X c,V[]p){value[h,ns,x]=i.size<3then[f,i[0],i[1]]else[m,i[1],i[2]];value n=if(is L ns)then[*ns.i.narrow<S>()]else ns;assert(is S|S[]n,is V x);V[]a=h(c,p);LC lC=if(is S n)then LC(c.g,map{n->L(*a)})else LC(c.g,map(zipEntries(n,a)));return object satisfies Co{st()=>x.vO(lC);};}}class S(String n)satisfies V{vO(X c)=>c.v(this);l(X c,V[]a)=>nV;equals(O o)=>if(is S o)then n==o.n else 1<0;hash=>n.hash;string=>n;}class I(sh Integer i)satisfies V{vO(X c)=>this;l(X c,V[]a)=>nV;equals(O o)=>if(is I o)then i==o.i else 1<0;hash=>i;b=>!i.zero;string=>i.string;}V[]f(X c,V[]a)=>[for(v in a)v.vF(c)];V[]m(X c,V[]a)=>a;L c(X c,V h,L t)=>L(h,*t.i);V h(X c,L l)=>l.i[0]else L();V t(X c,L l)=>L(*l.i.rest);I s(X c,I f,I s)=>I(f.i-s.i);I l(X c,I f,I s)=>I(f.i<s.i then 1else 0);I e(X c,V v1,V v2)=>I(v1==v2then 1else 0);C v(X c,V v)=>v.vO(c);V q(X c,V a)=>a;C i(X c,V d,V t,V f)=>d.vF(c).b then t.vO(c)else f.vO(c);S d(X c,S s,V x)=>c.g.d(s,x.vF(c));class B<A>(F<C,A>nat,V[](X,V[])h=f)satisfies V given A satisfies[X,V+]{vO(X c)=>nV;string=>nat.declaration.name;l(X c,V[]a)=>nat.apply(c,*h(c,a));}{<S->V>*}b=>{S("c")->B(`c`),S("h")->B(`h`),S("t")->B(`t`),S("s")->B(`s`),S("l")->B(`l`),S("e")->B(`e`),S("v")->B(`v`),S("q")->B(`q`,m),S("i")->B(`i`,m),S("d")->B(`d`,m)};[V*]p(String inp){value ts=inp.split(" \n()".contains,1<0,1<0);va[[V*]*]s=[];va[V*]l=[];for(t in ts){if(t in" \n"){}else if(t=="("){s=[l,*s];l=[];}else if(t==")"){l=[L(*l.reversed),*(s[0]else[])];s=s.rest;}else if(exists i=parseInteger(t),i>=0){l=[I(i),*l];}else{l=[S(t),*l];}}return l.reversed;}sh void run(){va value u="";while(exists l=process.readLine()){u=u+l+"\n";}V[]e=p(u);X c=G(map(b));for(v in e){print(v.vF(c));}}

मैं कुछ बाइट्स को और अधिक बढ़ा सकता था, क्योंकि मैंने कुछ स्थानों पर कुछ दो-अक्षर पहचानकर्ताओं का उपयोग किया था, लेकिन मैं उन लोगों के लिए कुछ सार्थक एकल पत्रों से बाहर चला गया हूं। हालांकि इस तरह भी यह सीलोन की तरह नहीं दिखता है ...

यह एक वस्तु-उन्मुख है कार्यान्वयन है।

हमारे पास Vवर्गों को लागू करने के साथ एक मूल्य इंटरफ़ेस है L(सूची - केवल सीलोन अनुक्रमिक के चारों ओर एक आवरण V), S(प्रतीक - एक स्ट्रिंग के चारों ओर आवरण), I(पूर्णांक - एक सीलोन पूर्णांक के चारों ओर आवरण) और B(अंतर्निहित फ़ंक्शन या मैक्रो, एक चारों ओर आवरण) सीलोन समारोह)।

मैं equalsविधि (और hashविशेषता, जो वास्तव में केवल प्रतीकों के लिए आवश्यक है), और stringआउटपुट के लिए मानक विशेषता को लागू करके मानक सीलोन समानता अंकन का उपयोग करता हूं ।

हमारे पास बूलियन विशेषता है b(जो डिफ़ॉल्ट रूप से सत्य है, खाली सूची के लिए ओवरराइड किया गया है Iऔर Lगलत है), और दो विधियाँ l(कॉल, अर्थात इस ऑब्जेक्ट को फ़ंक्शन के रूप में उपयोग करें) और vO(एक चरण का मूल्यांकन करें)। दोनों या तो एक मान या एक निरंतरता वस्तु को लौटाते हैं जो तब एक और कदम के लिए मूल्यांकन की अनुमति देता है, और vF(पूरी तरह से मूल्यांकन) तब तक लूप करता है जब तक कि परिणाम एक निरंतरता नहीं है।

एक संदर्भ इंटरफ़ेस चर तक पहुंच की अनुमति देता है। Gवैश्विक संदर्भ के लिए दो कार्यान्वयन हैं, (जो कि dबेसिन का उपयोग करके चर जोड़ने की अनुमति देता है ) और LCएक स्थानीय संदर्भ के लिए, जो उपयोगकर्ता फ़ंक्शन की अभिव्यक्ति का मूल्यांकन करते समय सक्रिय होता है (यह वैश्विक संदर्भ में वापस आता है)।

प्रतीक मूल्यांकन संदर्भ तक पहुँचता है, सूचियाँ (यदि खाली नहीं हैं) पहले उनके पहले तत्व का मूल्यांकन करके और फिर उसकी कॉल विधि का मूल्यांकन करती हैं। कॉल को केवल सूचियों और बिल्डिंस द्वारा कार्यान्वित किया जाता है - यह पहले तर्क का मूल्यांकन करता है (यदि एक फ़ंक्शन, यदि एक मैक्रो नहीं है) और फिर वास्तविक दिलचस्प सामान करता है - बिल्डिंस के लिए बस क्या हार्डकोड है, सूचियों के लिए यह एक नया स्थानीय संदर्भ बनाता है और एक रिटर्न देता है उस के साथ जारी है।

बिल्डरों के लिए मैंने अपने शिफ्ट इंटरप्रेटर में जो कुछ भी इस्तेमाल किया था, उसके समान एक ट्रिक का उपयोग किया , जो मुझे उन्हें उन तर्क प्रकारों के साथ परिभाषित करने की अनुमति देता है जिनकी उन्हें आवश्यकता होती है, लेकिन उन्हें प्रतिबिंब का उपयोग करके एक सामान्य अनुक्रम के साथ कॉल करें (प्रकार कॉल समय पर जांचे जाएंगे)। यह फ़ंक्शन / मैक्रोज़ के अंदर प्रकार रूपांतरण / जोरदार परेशानी से बचा जाता है, लेकिन शीर्ष-स्तर के कार्यों की आवश्यकता होती है ताकि मुझे उनके मेटा-मॉडल Functionऑब्जेक्ट मिल सकें ।

p(पार्स) समारोह रिक्त स्थान, नई-पंक्तियों और कोष्ठक में स्ट्रिंग विभाजन, तो टोकन से अधिक लूप और एक ढेर और चालू सूची का उपयोग करके सूचियां बनाता है।

दुभाषिया ( runविधि में, जो कि प्रवेश बिंदु है) फिर अभिव्यक्ति की इस सूची को लेता है (जो सिर्फ मूल्य हैं), उनमें से प्रत्येक का मूल्यांकन करता है, और परिणाम को प्रिंट करता है।


नीचे टिप्पणियों के साथ एक संस्करण है और एक फ़ॉर्मेटर के माध्यम से चलता है।

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

//  Tiny Lisp, tiny interpreter
//
// An interpreter for a tiny subset of Lisp, from which most of the
// rest of the language can be bootstrapped.
//
// Question:   https://codegolf.stackexchange.com/q/62886/2338
// My answer:  https://codegolf.stackexchange.com/a/63352/2338
//
import ceylon.language {
    sh=shared,
    va=variable,
    fo=formal,
    O=Object
}
import ceylon.language.meta.model {
    F=Function
}

// context

interface X {
    sh fo V v(S t);
    sh fo G g;
}
// global (mutable) context, with the buildins 
class G(va Map<S,V> m) satisfies X {
    // get entry throws error on undefined variables. 
    v(S t) => m[t] else nV;
    g => this;
    sh S d(S s, V v) {
        // error when already defined
        assert (!s in m);
        // building a new map is cheaper (code-golf wise) than having a mutable one.
        m = map { s->v, *m };
        return s;
    }
}

// This is simply a shorter way of writing "this is not an allowed operation".
// It will throw an exception when trying to access it.
// nV stands for "no value".
V nV => nothing;

// local context
class LC(G c, Map<S,V> m) satisfies X {
    g => c;
    v(S t) => m[t] else g.v(t);
    // sh actual String string => "[local: ``m``, global: ``g``]";
}

// continuation or value
alias C => V|Co;

// continuation
interface Co {
    sh fo C st();
}

// value
interface V {
    // use this as a function and call with arguments.
    // will not work for all types of stuff.
    sh fo C l(X c, V[] a);
    // check the truthiness. Defaults to true, as
    // only lists and integers can be falsy.
    sh default Boolean b => 0 < 1;
    // evaluate once (return either a value or a continuation).
    // will not work for all kinds of expression.
    sh fo C vO(X c);
    /// evaluate fully
    sh default V vF(X c) {
        va C v = vO(c);
        while (is Co n = v) {
            v = n.st();
        }
        assert (is V r = v);
        return r;
    }
}
class L(sh V* i) satisfies V {

    vO(X c) => if (nonempty i) then i[0].vF(c).l(c, i.rest) else this;
    equals(O o) => if (is L o) then i == o.i else 1 < 0;
    b => !i.empty;
    string => "(``" ".join(i)``)";
    hash => i.hash;

    sh actual C l(X c, V[] p) {
        value [h, ns, x] =
                i.size < 3
                then [f, i[0], i[1]]
                else [m, i[1], i[2]];
        // parameter names – either a single symbol, or a list of symbols.
        // If it is a list, we filter it to throw out any non-symbols.
        // Should throw an error if there are any, but this is harder.
        value n = if (is L ns) then [*ns.i.narrow<S>()] else ns;
        assert (is S|S[] n, is V x);
        V[] a = h(c, p);

        // local context
        LC lC = if (is S n) then
            LC(c.g, map { n -> L(*a) })
        else
            LC(c.g, map(zipEntries(n, a)));
        // return a continuation instead of actually
        // calling it here, to allow stack unwinding.
        return object satisfies Co {
            st() => x.vO(lC);
        };
    }
}

// symbol
class S(String n) satisfies V {
    // evaluate: resolve
    vO(X c) => c.v(this);
    // call is not allowed
    l(X c, V[] a) => nV;
    // equal if name is equal
    equals(O o) => if (is S o) then n == o.n else 1 < 0;
    hash => n.hash;
    string => n;
}

// integer
class I(sh Integer i) satisfies V {

    vO(X c) => this;
    l(X c, V[] a) => nV;
    equals(O o) => if (is I o) then i == o.i else 1 < 0;
    hash => i;
    b => !i.zero;
    string => i.string;
}

// argument handlers for functions or macros
V[] f(X c, V[] a) => [for (v in a) v.vF(c)];
V[] m(X c, V[] a) => a;

// build-in functions
// construct
L c(X c, V h, L t) => L(h, *t.i);
// head
V h(X c, L l) => l.i[0] else L();
// tail
V t(X c, L l) => L(*l.i.rest);
// subtract
I s(X c, I f, I s) => I(f.i - s.i);
// lessThan
I l(X c, I f, I s) => I(f.i < s.i then 1 else 0);
// equal
I e(X c, V v1, V v2) => I(v1 == v2 then 1 else 0);
// eval (returns potentially a continuation)
C v(X c, V v) => v.vO(c);

// build-in macros
// quote
V q(X c, V a) => a;
// if (also returns potentially a continuation)
C i(X c, V d, V t, V f) => d.vF(c).b then t.vO(c) else f.vO(c);
// define symbol in global context
S d(X c, S s, V x) => c.g.d(s, x.vF(c));

// buildin function or macro, made from a native function and an argument handler
class B<A>(F<C,A> nat, V[](X, V[]) h = f)
        satisfies V
        given A satisfies [X, V+] {
    vO(X c) => nV;
    string => nat.declaration.name;
    // this "apply" is a hack which breaks type safety ...
    // but it will be checked at runtime.
    l(X c, V[] a) => nat.apply(c, *h(c, a));
}

// define buildins
{<S->V>*} b => {
    S("c") -> B(`c`),
    S("h") -> B(`h`),
    S("t") -> B(`t`),
    S("s") -> B(`s`),
    S("l") -> B(`l`),
    S("e") -> B(`e`),
    S("v") -> B(`v`),
    S("q") -> B(`q`, m),
    S("i") -> B(`i`, m),
    S("d") -> B(`d`, m)
};

// parses a string into a list of expressions.
[V*] p(String inp) {
    // split string into tokens (retain separators, don't group them –
    // whitespace and empty strings will be sorted out later in the loop)
    value ts = inp.split(" \n()".contains, 1 < 0, 1 < 0);
    // stack of not yet finished nested lists, outer most at bottom
    va [[V*]*] s = [];
    // current list, in reverse order (because appending at the start is shorter)
    va [V*] l = [];
    for (t in ts) {
        if (t in " \n") {
            // do nothing for empty tokens
        } else if (t == "(") {
            // push the current list onto the stack, open a new list.
            s = [l, *s];
            l = [];
        } else if (t == ")") {
            // build a lisp list from the current list,
            // pop the latest list from the stack, append the created lisp list. 
            l = [L(*l.reversed), *(s[0] else [])];
            s = s.rest;
        } else if (exists i = parseInteger(t), i >= 0) {
            // append an integer to the current list.
            l = [I(i), *l];
        } else {
            // append a symbol to the current list.
            l = [S(t), *l];
        }
    }
    return l.reversed;
}

// Runs the interpreter.
// This handles input and output, calls the parser and evaluates the expressions.
sh void run() {
    va value u = "";
    while (exists l = process.readLine()) {
        u = u + l + "\n";
    }
    V[] e = p(u);
    // create global context
    X c = G(map(b));
    // iterate over the expressions, ...
    for (v in e) {
        // print("  '``v``' → ...");
        // ... evaluate each (fully) and print the result.
        print(v.vF(c));
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.