क्या आप पायथन के सिंटैक्स में नए कथन जोड़ सकते हैं?


124

आप नए बयान को जोड़ सकते हैं (जैसे print, raise,with अजगर सिंटैक्स का)?

कहने के लिए, अनुमति देने के लिए ..

mystatement "Something"

या,

new_if True:
    print "example"

इतना नहीं अगर आपको चाहिए , बल्कि अगर यह संभव है (अजगर व्याख्याकारों कोड को संशोधित करने की कमी)


10
कुछ हद तक संबंधित नोट पर, एक उपयोग-मामला जहां यह मक्खी पर नए बयान बनाने के लिए आसान हो सकता है (जैसा कि गंभीरता से "भाषा का विस्तार" करने के लिए विरोध किया जाता है) उन लोगों के लिए है जो एक कैलकुलेटर के रूप में इंटरैक्टिव दुभाषिया का उपयोग करते हैं, या यहां तक ​​कि एक ओएस शेल । मैं अक्सर मक्खी पर कुछ फेंकने वाले कार्य करता हूं जो मैं दोहराने जा रहा हूं, और उन स्थितियों में फ़ंक्शन () सिंटैक्स के साथ लंबे नामों को टाइप करने के बजाय मैक्रोज़ या स्टेटमेंट जैसे बहुत संक्षिप्त कमांड बनाना अच्छा होगा। बेशक, जो वास्तव में Py के लिए नहीं है .. लेकिन लोग अंतःक्रियात्मक रूप से इसका उपयोग करने में बहुत समय व्यतीत करते हैं।
Kilo

5
@ किल्लो यह देखने के लायक हो सकता है, जैसे कि यह ipython है - इसमें बहुत सारे शेल'इश फीचर्स हैं, उदाहरण के लिए आप नियमित "ls" और "cd" कमांड का उपयोग कर सकते हैं, टैब-कम्प्लीट कर सकते हैं, बहुत सारे
मैक्रो-

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

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

जवाबों:


153

आपको यह उपयोगी लग सकता है - पायथन इंटर्न: पायथन के लिए एक नया बयान जोड़ना , यहाँ उद्धृत:


यह लेख बेहतर ढंग से समझने का प्रयास है कि पायथन का फ्रंट-एंड कैसे काम करता है। बस दस्तावेज़ीकरण और स्रोत कोड पढ़ना थोड़ा उबाऊ हो सकता है, इसलिए मैं यहां एक हाथ-दृष्टिकोण ले रहा हूं: मैं एक जोड़ने जा रहा हूंuntil पायथन में बयान ।

इस लेख के लिए सभी कोडिंग Python Mercurial repository दर्पण में अत्याधुनिक Py3k शाखा के खिलाफ की गई थी

untilकथन

रूबी जैसी कुछ भाषाओं में एक untilकथन है, जो पूरक while( until num == 0के बराबर while num != 0) है। रूबी में, मैं लिख सकता हूं:

num = 3
until num == 0 do
  puts num
  num -= 1
end

और यह प्रिंट होगा:

3
2
1

इसलिए, मैं पायथन में एक समान क्षमता जोड़ना चाहता हूं। यह है, लिखने में सक्षम होने के नाते:

num = 3
until num == 0:
  print(num)
  num -= 1

एक भाषा-वकालत विषयांतर

यह लेख untilपाइथन के कथन को जोड़ने का सुझाव देने का प्रयास नहीं करता है । हालांकि मुझे लगता है कि इस तरह के बयान से कुछ कोड स्पष्ट हो जाएंगे, और यह लेख दिखाता है कि इसे जोड़ना कितना आसान है, मैं पायथन के अतिसूक्ष्मवाद के दर्शन का पूरा सम्मान करता हूं। मैं यहाँ सब करने की कोशिश कर रहा हूँ, वास्तव में, पायथन के आंतरिक कामकाज में कुछ अंतर्दृष्टि प्राप्त कर रहा है।

व्याकरण को संशोधित करना

पायथन एक कस्टम पार्सर जनरेटर का उपयोग करता है जिसका नाम है pgen। यह एक LL (1) पार्सर है जो पाइथन स्रोत कोड को पार्स ट्री में परिवर्तित करता है। पार्सर जनरेटर का इनपुट फ़ाइल Grammar/Grammar[1] है । यह एक साधारण पाठ फ़ाइल है जो पायथन के व्याकरण को निर्दिष्ट करती है।

[1] : यहाँ से, पायथन स्रोत की फाइलों के संदर्भ स्रोत के पेड़ की जड़ को अपेक्षाकृत दिए गए हैं, जो कि वह निर्देशिका है जहाँ आप कॉन्फ़िगर करते हैं और पायथन बनाने के लिए बनाते हैं।

व्याकरण फ़ाइल में दो संशोधन करने होंगे। पहला untilकथन के लिए एक परिभाषा जोड़ना है । मैंने पाया कि जहां whileकथन परिभाषित किया गया था ( while_stmt), और until_stmtनीचे जोड़ा गया [2] :

compound_stmt: if_stmt | while_stmt | until_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
until_stmt: 'until' test ':' suite

[२] : यह एक सामान्य तकनीक को प्रदर्शित करता है जिसका उपयोग मैं उस स्रोत कोड को संशोधित करता हूं जिससे मैं परिचित नहीं हूं: समानता से काम करें । यह सिद्धांत आपकी सभी समस्याओं को हल नहीं करेगा, लेकिन यह निश्चित रूप से प्रक्रिया को आसान कर सकता है। चूँकि इसके लिए जो कुछ करना पड़ता है वह whileभी करना पड़ता है until, यह एक बहुत अच्छी दिशानिर्देश के रूप में कार्य करता है।

ध्यान दें कि मैंने elseखंड को अपनी परिभाषा से बाहर करने का फैसला किया है until, बस इसे थोड़ा अलग करने के लिए (और क्योंकि स्पष्ट रूप से मुझे नापसंद हैelse छोरों खंड को और ऐसा नहीं लगता कि यह पायथन के ज़ेन के साथ अच्छी तरह से फिट बैठता है)।

दूसरा बदलाव , compound_stmtशामिल करने के लिए नियम को संशोधित करना है until_stmt, जैसा कि आप ऊपर स्निपेट में देख सकते हैं। यह सही है while_stmt, फिर से।

आप समाप्त हो जाता है makeसंशोधित करने के बाद Grammar/Grammar, सूचना है कि pgenकार्यक्रम के लिए चलाया जाता है फिर से उत्पन्न Include/graminit.hऔर Python/graminit.c, और उसके बाद कई फ़ाइलों को फिर से संकलित मिलता है।

एएसटी पीढ़ी कोड को संशोधित करना

पायथन पार्सर के बाद एक पार्स ट्री बनाया गया है, इस पेड़ को एएसटी में बदल दिया गया है, क्योंकि एएसटी संकलन प्रक्रिया के बाद के चरणों में काम करने के लिए बहुत सरल हैं ।

इसलिए, हम Parser/Python.asdlउस यात्रा पर जा रहे हैं जो पायथन के एएसटी की संरचना को परिभाषित करती है और हमारे नए untilविवरण के लिए एएसटी नोड को फिर से नीचे जोड़ती है while:

| While(expr test, stmt* body, stmt* orelse)
| Until(expr test, stmt* body)

यदि आप अब चलाते हैं make, तो ध्यान दें कि फ़ाइलों का एक गुच्छा संकलित करने से पहले, Parser/asdl_c.pyएएसटी परिभाषा फ़ाइल से सी कोड उत्पन्न करने के लिए चलाया जाता है। यह (जैसे Grammar/Grammar) मिनी-भाषा (दूसरे शब्दों में, एक डीएसएल) का उपयोग करके प्रोग्रामिंग को आसान बनाने के लिए पायथन सोर्स-कोड का एक और उदाहरण है। यह भी ध्यान दें कि चूंकि Parser/asdl_c.pyपायथन लिपि है, यह एक प्रकार का बूटस्ट्रैपिंग है - स्क्रैच से पाइथन बनाने के लिए, पाइथन पहले से ही उपलब्ध होना चाहिए।

जबकि Parser/asdl_c.py(फाइलों में हमारे नए परिभाषित एएसटी नोड प्रबंधन करने के लिए कोड उत्पन्न Include/Python-ast.hऔर Python/Python-ast.c), हम अभी भी कोड लिखने के लिए है कि धर्मान्तरित इसे में एक प्रासंगिक पार्स पेड़ नोड हाथ से। यह फ़ाइल में किया जाता है Python/ast.c। वहां, एक फ़ंक्शन नाम ast_for_stmtपार्स ट्री नोड्स को एएसटी नोड्स में बयान के लिए परिवर्तित करता है। फिर से, हमारे पुराने मित्र द्वारा निर्देशित while, हम switchयौगिक कथनों को संभालने के लिए बड़े में दाएं कूदते हैं और इसके लिए एक खंड जोड़ते हैं until_stmt:

case while_stmt:
    return ast_for_while_stmt(c, ch);
case until_stmt:
    return ast_for_until_stmt(c, ch);

अब हमें लागू करना चाहिए ast_for_until_stmt। यह रहा:

static stmt_ty
ast_for_until_stmt(struct compiling *c, const node *n)
{
    /* until_stmt: 'until' test ':' suite */
    REQ(n, until_stmt);

    if (NCH(n) == 4) {
        expr_ty expression;
        asdl_seq *suite_seq;

        expression = ast_for_expr(c, CHILD(n, 1));
        if (!expression)
            return NULL;
        suite_seq = ast_for_suite(c, CHILD(n, 3));
        if (!suite_seq)
            return NULL;
        return Until(expression, suite_seq, LINENO(n), n->n_col_offset, c->c_arena);
    }

    PyErr_Format(PyExc_SystemError,
                 "wrong number of tokens for 'until' statement: %d",
                 NCH(n));
    return NULL;
}

फिर से, इसे बराबर देखते हुए कोड किया गया था, इस ast_for_while_stmtअंतर के साथ कि untilमैंने निर्णय लिया है कि मैं elseखंड का समर्थन नहीं करता । जैसा कि अपेक्षित था, एएसटी को पुन: निर्मित किया जाता है, ast_for_exprस्थिति की अभिव्यक्ति के लिए और बयान ast_for_suiteके शरीर के लिए अन्य एएसटी कार्यों का उपयोग करके until। अंत में, नाम दिया गया एक नया नोड Untilवापस आ गया है।

ध्यान दें कि हम nकुछ मैक्रोज़ जैसे NCHऔर का उपयोग करके पार्स-ट्री नोड का उपयोग करते हैं CHILD। ये समझने लायक हैं - इनका कोड है Include/node.h

पाचन: एएसटी रचना

मैंने untilबयान के लिए एक नए प्रकार का एएसटी बनाने का विकल्प चुना , लेकिन वास्तव में यह आवश्यक नहीं है। मैं कुछ काम बचा सकता था और मौजूदा एएसटी नोड्स की संरचना का उपयोग करके नई कार्यक्षमता को लागू कर सकता था:

until condition:
   # do stuff

कार्यात्मक रूप से इसके बराबर है:

while not condition:
  # do stuff

में Untilनोड बनाने के बजाय ast_for_until_stmt, मैं Notएक Whileनोड को एक बच्चे के रूप में बना सकता था। चूंकि एएसटी कंपाइलर पहले से ही जानता है कि इन नोड्स को कैसे संभालना है, प्रक्रिया के अगले चरणों को छोड़ दिया जा सकता है।

एएसटी को बायटेकोड में संकलित करना

अगला चरण Python bytecode में AST को संकलित कर रहा है। संकलन का एक मध्यवर्ती परिणाम है जो एक सीएफजी (नियंत्रण प्रवाह ग्राफ़) है, लेकिन चूंकि एक ही कोड इसे संभालता है इसलिए मैं इस विवरण को अभी के लिए अनदेखा कर दूंगा और इसे किसी अन्य लेख के लिए छोड़ दूंगा।

अगला कोड हम देखेंगे Python/compile.c। के नेतृत्व के बाद while, हम फ़ंक्शन compiler_visit_stmtको खोजते हैं, जो कि बाइटकोड में बयान संकलन के लिए जिम्मेदार है। हम इसके लिए एक खंड जोड़ते हैं Until:

case While_kind:
    return compiler_while(c, s);
case Until_kind:
    return compiler_until(c, s);

यदि आप आश्चर्य करते हैं कि क्या Until_kindहै, तो यह _stmt_kindएएसटी परिभाषा फ़ाइल से स्वचालित रूप से उत्पन्न एक स्थिर (वास्तव में गणना का मूल्य ) है Include/Python-ast.h। वैसे भी, हम कहते हैं compiler_until, जो अभी भी मौजूद नहीं है। मैं इसे एक पल के लिए मिल जाएगा।

यदि आप मेरी तरह उत्सुक हैं, तो आप ध्यान देंगे कि compiler_visit_stmtअजीब है। grepस्रोत वृक्ष को काटे जाने की कोई मात्रा नहीं बताती है कि इसे कहां कहा जाता है। जब यह मामला होता है, तो केवल एक विकल्प बचता है - सी मैक्रो-फू। वास्तव में, एक छोटी जांच हमें VISITपरिभाषित मैक्रो की ओर ले जाती है Python/compile.c:

#define VISIT(C, TYPE, V) {\
    if (!compiler_visit_ ## TYPE((C), (V))) \
        return 0; \

यह आह्वान करने के लिए प्रयोग किया जाता है compiler_visit_stmtमें compiler_body। हमारे व्यवसाय पर वापस, हालांकि ...

जैसा कि वादा किया गया है, यहाँ है compiler_until:

static int
compiler_until(struct compiler *c, stmt_ty s)
{
    basicblock *loop, *end, *anchor = NULL;
    int constant = expr_constant(s->v.Until.test);

    if (constant == 1) {
        return 1;
    }
    loop = compiler_new_block(c);
    end = compiler_new_block(c);
    if (constant == -1) {
        anchor = compiler_new_block(c);
        if (anchor == NULL)
            return 0;
    }
    if (loop == NULL || end == NULL)
        return 0;

    ADDOP_JREL(c, SETUP_LOOP, end);
    compiler_use_next_block(c, loop);
    if (!compiler_push_fblock(c, LOOP, loop))
        return 0;
    if (constant == -1) {
        VISIT(c, expr, s->v.Until.test);
        ADDOP_JABS(c, POP_JUMP_IF_TRUE, anchor);
    }
    VISIT_SEQ(c, stmt, s->v.Until.body);
    ADDOP_JABS(c, JUMP_ABSOLUTE, loop);

    if (constant == -1) {
        compiler_use_next_block(c, anchor);
        ADDOP(c, POP_BLOCK);
    }
    compiler_pop_fblock(c, LOOP, loop);
    compiler_use_next_block(c, end);

    return 1;
}

मेरे पास बनाने के लिए एक स्वीकारोक्ति है: यह कोड पायथन बाइटकोड की गहरी समझ के आधार पर नहीं लिखा गया था। बाकी लेख की तरह, यह परिजनों के compiler_whileकार्य के अनुकरण में किया गया था । हालाँकि, इसे ध्यान से पढ़ते हुए, यह ध्यान में रखते हुए कि पायथन वीएम स्टैक-आधारित है, और disमॉड्यूल के प्रलेखन में झलक रहा है , जिसमें विवरण के साथ पायथन बायोटेक्कोड की सूची है , यह समझना संभव है कि क्या हो रहा है।

यही है, हम कर रहे हैं ... हम नहीं हैं?

सभी परिवर्तन करने और चलाने के बाद make, हम नए संकलित अजगर को चला सकते हैं और हमारे नए untilकथन को आजमा सकते हैं :

>>> until num == 0:
...   print(num)
...   num -= 1
...
3
2
1

वोइला, यह काम करता है! आइए देखें कि नए स्टेटमेंट के लिए disमॉड्यूल द्वारा निम्न प्रकार से बनाया गया बायटेकोड देखें :

import dis

def myfoo(num):
    until num == 0:
        print(num)
        num -= 1

dis.dis(myfoo)

यहाँ परिणाम है:

4           0 SETUP_LOOP              36 (to 39)
      >>    3 LOAD_FAST                0 (num)
            6 LOAD_CONST               1 (0)
            9 COMPARE_OP               2 (==)
           12 POP_JUMP_IF_TRUE        38

5          15 LOAD_NAME                0 (print)
           18 LOAD_FAST                0 (num)
           21 CALL_FUNCTION            1
           24 POP_TOP

6          25 LOAD_FAST                0 (num)
           28 LOAD_CONST               2 (1)
           31 INPLACE_SUBTRACT
           32 STORE_FAST               0 (num)
           35 JUMP_ABSOLUTE            3
      >>   38 POP_BLOCK
      >>   39 LOAD_CONST               0 (None)
           42 RETURN_VALUE

सबसे दिलचस्प ऑपरेशन 12 नंबर है: यदि स्थिति सच है, तो हम लूप के बाद कूदते हैं। यह सही शब्दार्थ है until। यदि कूद को अंजाम नहीं दिया जाता है, तो लूप बॉडी तब तक चलती रहती है, जब तक वह ऑपरेशन 35 की स्थिति में वापस कूद जाती है।

अपने परिवर्तन के बारे में अच्छा महसूस करते हुए, मैंने इसके बाद myfoo(3)अपने बायोटेक को दिखाने के बजाय फ़ंक्शन (निष्पादित ) को चलाने की कोशिश की । परिणाम उत्साहजनक से कम था:

Traceback (most recent call last):
  File "zy.py", line 9, in
    myfoo(3)
  File "zy.py", line 5, in myfoo
    print(num)
SystemError: no locals when loading 'print'

वाह ... यह अच्छा नहीं हो सकता। तो क्या गलत हुआ?

लापता प्रतीक तालिका का मामला

जब पायल कंपाइलर एक कदम रखता है तो एएसटी का संकलन उस कोड के लिए प्रतीक तालिका बनाता है। करने के लिए कॉल PySymtable_Buildमें PyAST_Compileप्रतीक तालिका मॉड्यूल (में कॉल Python/symtable.c) है, जो एक तरह से कोड पीढ़ी कार्यों के लिए इसी तरह के एएसटी चलता है। प्रत्येक स्कोप के लिए एक प्रतीक तालिका होने से कंपाइलर को कुछ महत्वपूर्ण जानकारी का पता लगाने में मदद मिलती है, जैसे कि कौन-सी चर वैश्विक हैं और जो एक स्कोप के लिए स्थानीय हैं।

इस समस्या को ठीक करने के लिए, हम संशोधित करने के लिए है symtable_visit_stmtमें समारोह Python/symtable.c, से निपटने के लिए कोड जोड़ने until, बयानों के लिए इसी तरह के कोड के बाद whileबयान [3] :

case While_kind:
    VISIT(st, expr, s->v.While.test);
    VISIT_SEQ(st, stmt, s->v.While.body);
    if (s->v.While.orelse)
        VISIT_SEQ(st, stmt, s->v.While.orelse);
    break;
case Until_kind:
    VISIT(st, expr, s->v.Until.test);
    VISIT_SEQ(st, stmt, s->v.Until.body);
    break;

[३] : वैसे, इस कोड के बिना इसके लिए एक कंपाइलर चेतावनी है Python/symtable.c। कंपाइलर ने नोटिस किया कि Until_kindएन्यूमरेशन वैल्यू को स्विच स्टेटमेंट में हैंडल नहीं किया गया है symtable_visit_stmtऔर शिकायत करता है। कंपाइलर चेतावनियों की जांच करना हमेशा महत्वपूर्ण होता है!

और अब हम वास्तव में कर रहे हैं। इस परिवर्तन के बाद स्रोत को संकलित करना myfoo(3)अपेक्षित रूप से कार्य का निष्पादन करता है।

निष्कर्ष

इस लेख में मैंने दिखाया है कि पायथन में एक नया बयान कैसे जोड़ा जाए। यद्यपि पाइथन संकलक के कोड में काफी छेड़छाड़ की आवश्यकता होती है, परिवर्तन को लागू करना मुश्किल नहीं था, क्योंकि मैंने एक दिशानिर्देश के रूप में एक समान और मौजूदा बयान का उपयोग किया था।

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

संदर्भ

मैंने इस लेख के निर्माण के लिए कुछ उत्कृष्ट संदर्भों का उपयोग किया। यहां वे किसी खास क्रम में नहीं हैं:

  • PEP 339: CPython संकलक का डिज़ाइन - शायद पायथन संकलक के लिए आधिकारिक प्रलेखन का सबसे महत्वपूर्ण और व्यापक टुकड़ा है । बहुत छोटा होने के कारण, यह दर्द से पाइथन के इंटर्नल के अच्छे प्रलेखन की कमी को प्रदर्शित करता है।
  • "पायथन कंपाइलर इंटर्नल्स" - थॉमस ली का एक लेख
  • "पायथन: डिज़ाइन एंड इम्प्लीमेंटेशन" - गुइडो वैन रोसुम की एक प्रस्तुति
  • पाइथन (2.5) वर्चुअल मशीन, ए गाइडेड टूर - पीटर ट्रेजर द्वारा एक प्रस्तुति

मूल स्रोत


7
उत्कृष्ट लेख (/ ब्लॉग), धन्यवाद! यह स्वीकार करने के बाद से यह पूरी तरह से सवाल का जवाब देता है, और "ऐसा नहीं करते" / "कोडिंग: मायलैंग" के उत्तर पहले से ही बहुत उच्च स्तर के हैं, इसलिए यह अच्छी तरह से क्रम में दिखाई देगा \
_

1
लेकिन दुर्भाग्य से, यह एक जवाब नहीं है। लिंक किया गया लेख है, लेकिन आप इसे स्वीकार या स्वीकार नहीं कर सकते। पूरी तरह से सिर्फ एक लिंक से युक्त उत्तर हतोत्साहित किया जाता है।
अल्फ

6
@ शेल्फ: यह दो साल पहले पोस्ट किया गया था, 16 पाठकों द्वारा स्वीकार और + लिया गया था। ध्यान दें कि यह मेरे अपने ब्लॉग पोस्ट से लिंक है, और StackOverflow में एक बड़े लेख की नकल करना कुछ ऐसा नहीं है जो मैं करना चाहता हूं। पुलिस के बजाय एक उपयोगी संपादन में ऐसा करने के लिए स्वतंत्र महसूस करें।
एली बेंडरस्की

2
@EliBendersky उपयोगी उस लेख के लिए काफी समझ में आता है। अजगर इन चीजों को वास्तव में कैसे काम करता है, इसके बारे में बहुत कुछ समझाने के लिए धन्यवाद। इसने मुझे एएसटी को समझने में वास्तव में मदद की है, जो मेरे वर्तमान काम के लिए प्रासंगिक है। ** भी मामले में आप उत्सुक, कर रहे हैं की मेरी संस्करण untilहै isa/ isanमें के रूप में if something isa dict:याif something isan int:
Inversus

5
सू, यह उत्तर है "अपनी भाषा को स्रोत से लिखें और संकलित करें, अजगर से कांटा"
ThorSummoner

53

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

उदाहरण के लिए, हम कहते हैं कि हम एक "myprint" स्टेटमेंट पेश करना चाहते हैं, जो स्क्रीन पर प्रिंट करने के बजाय एक विशिष्ट फाइल में लॉग इन करता है। अर्थात:

myprint "This gets logged to file"

के बराबर होगा

print >>open('/tmp/logfile.txt','a'), "This gets logged to file"

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

import tokenize

LOGFILE = '/tmp/log.txt'
def translate(readline):
    for type, name,_,_,_ in tokenize.generate_tokens(readline):
        if type ==tokenize.NAME and name =='myprint':
            yield tokenize.NAME, 'print'
            yield tokenize.OP, '>>'
            yield tokenize.NAME, "open"
            yield tokenize.OP, "("
            yield tokenize.STRING, repr(LOGFILE)
            yield tokenize.OP, ","
            yield tokenize.STRING, "'a'"
            yield tokenize.OP, ")"
            yield tokenize.OP, ","
        else:
            yield type,name

(यह myprint को प्रभावी ढंग से एक कीवर्ड बनाता है, इसलिए एक चर के रूप में उपयोग कहीं और समस्याओं की संभावना पैदा करेगा)

फिर समस्या यह है कि इसका उपयोग कैसे किया जाए ताकि आपका कोड अजगर से प्रयोग करने योग्य हो। एक तरीका बस अपने स्वयं के आयात समारोह को लिखना होगा, और इसे अपनी कस्टम भाषा में लिखे गए कोड को लोड करने के लिए उपयोग करना होगा। अर्थात:

import new
def myimport(filename):
    mod = new.module(filename)
    f=open(filename)
    data = tokenize.untokenize(translate(f.readline))
    exec data in mod.__dict__
    return mod

हालांकि आपको अपने अनुकूलित कोड को सामान्य अजगर मॉड्यूल से अलग तरीके से संभालना होगा। अर्थात "some_mod = myimport("some_mod.py") " के बजाय " import some_mod"

एक और काफी साफ सुथरा (यद्यपि हैकी) समाधान एक कस्टम एन्कोडिंग ( पीईपी 263 देखें ) बनाने के लिए है क्योंकि यह नुस्खा प्रदर्शित करता है। आप इसे इस प्रकार लागू कर सकते हैं:

import codecs, cStringIO, encodings
from encodings import utf_8

class StreamReader(utf_8.StreamReader):
    def __init__(self, *args, **kwargs):
        codecs.StreamReader.__init__(self, *args, **kwargs)
        data = tokenize.untokenize(translate(self.stream.readline))
        self.stream = cStringIO.StringIO(data)

def search_function(s):
    if s!='mylang': return None
    utf8=encodings.search_function('utf8') # Assume utf8 encoding
    return codecs.CodecInfo(
        name='mylang',
        encode = utf8.encode,
        decode = utf8.decode,
        incrementalencoder=utf8.incrementalencoder,
        incrementaldecoder=utf8.incrementaldecoder,
        streamreader=StreamReader,
        streamwriter=utf8.streamwriter)

codecs.register(search_function)

अब इस कोड के चलने के बाद (जैसे। आप इसे अपने .pythonrc या site.py में रख सकते हैं) किसी भी कोड को "# कोडिंग: मायलैंग" से शुरू करके स्वचालित रूप से उपरोक्त प्रीप्रोसेसिंग चरण के माध्यम से अनुवादित किया जाएगा। जैसे।

# coding: mylang
myprint "this gets logged to file"
for i in range(10):
    myprint "so does this : ", i, "times"
myprint ("works fine" "with arbitrary" + " syntax" 
  "and line continuations")

चेतावनियां:

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


12
अच्छा है! इसके बजाय 'डन नहीं किया जा सकता' कहने के बजाय, आप वास्तव में कुछ अच्छे उत्तर देते हैं (जो कि 'आप वास्तव में ऐसा नहीं करना चाहते हैं' के लिए उबलते हैं)
c0m4

मुझे यकीन नहीं है कि मैं समझता हूं कि पहला उदाहरण कैसे काम करता है - myimportएक मॉड्यूल पर उपयोग करने की कोशिश करना, जिसमें बस print 1कोड पैदावार की एक ही पंक्ति है=1 ... SyntaxError: invalid syntax
ओलामुंडो

@ नहीं: निश्चित नहीं है कि आपके लिए क्या विफल हो रहा है - यहाँ मुझे उम्मीद के अनुसार "1" छपा है। (यह 2 ब्लॉक "आयात टोकन शुरू करने" और "नया आयात करने के लिए" के साथ फ़ाइल थिंकपैड में डाल दिया गया है, साथ ही साथ " b=myimport("b.py")", और b.py जिसमें सिर्फ " print 1" है। त्रुटि के लिए और कुछ भी नहीं है (स्टैक ट्रेस। आदि);
ब्रायन

3
Python3 यह अनुमति देने के लिए प्रतीत नहीं होता है, हालांकि उद्देश्य पर जरूरी नहीं है; मुझे BOM त्रुटि मिलती है।
तोबू

ध्यान दें कि importबिल्टिन का उपयोग करता है __import__, इसलिए यदि आप ओवरराइट करते हैं ( संशोधित आयात की आवश्यकता वाले मॉड्यूल को आयात करने से पहले ), तो आपको एक अलग की आवश्यकता नहीं हैmyimport
टोबैस किंजलर

21

हां, कुछ हद तक यह संभव है। वहाँ एक मॉड्यूल है जो sys.settrace()कार्यान्वयन gotoऔर comefrom"कीवर्ड" का उपयोग करता है :

from goto import goto, label
for i in range(1, 10):
  for j in range(1, 20):
    print i, j
    if j == 3:
      goto .end # breaking out from nested loop
label .end
print "Finished"

4
हालांकि यह वास्तव में नया वाक्यविन्यास नहीं है ... यह सिर्फ ऐसा दिखता है।
हंस नावाक

3
-1: लिंक किए गए पृष्ठ में यह शीर्षक है: "गोटो 'मॉड्यूल एक अप्रैल फूल का मज़ाक था, 1 अप्रैल 2004 को प्रकाशित किया गया था। हाँ, यह काम करता है, लेकिन फिर भी यह एक मजाक है। कृपया वास्तविक कोड में इसका इस्तेमाल न करें!"
जिम

6
@ जिम -1 पर पुनर्विचार कर सकता है। यह आपको कार्यान्वयन तंत्र पर संकेत देता है। अच्छी शुरुआत के साथ।
n611x007

14

स्रोत कोड (जो है) को बदलने और पुन: स्थापित करने की कमी खुले स्रोत के साथ संभव है) , आधार भाषा को बदलना वास्तव में संभव नहीं है।

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

हालाँकि, मुझे यकीन नहीं है कि आप क्यों करना चाहते हैं। पायथन के ऑब्जेक्ट-ओरिएंटेड फीचर्स भाषा के साथ समान परिणाम प्राप्त करने के लिए इसे सरल बनाते हैं क्योंकि यह खड़ा है।


2
मैं एक बिंदु पर असहमत हूं। यदि आप नए कीवर्ड जोड़ते हैं तो मुझे लगता है कि यह अभी भी पायथन होगा। यदि आप मौजूदा कीवर्ड बदलते हैं , तो जैसा कि आप कहते हैं, बस हैक किया गया है।
छिपकली

9
यदि आप नए कीवर्ड जोड़ते हैं, तो यह एक पायथन-व्युत्पन्न भाषा होगी। यदि आप कीवर्ड बदलते हैं, तो यह पायथन-असंगत भाषा होगी।
tzot

1
यदि आप कीवर्ड जोड़ते हैं, तो आपको "सरल आसान-से-सीखें वाक्यविन्यास" और "व्यापक पुस्तकालयों" की बात याद आ रही होगी। मुझे लगता है कि भाषा की विशेषताएं लगभग हमेशा एक गलती होती हैं (उदाहरण में COBOL, पर्ल और PHP शामिल हैं)।
एस.लूट

5
नए कीवर्ड पायथन कोड को तोड़ देंगे जो उन्हें पहचानकर्ता के रूप में उपयोग करता है।
एकैहोला

12

सामान्य उत्तर: आपको अपनी स्रोत फ़ाइलों को प्रीप्रोसेस करने की आवश्यकता है।

अधिक विशिष्ट उत्तर: EasyExtend स्थापित करें , और निम्न चरणों से

i) एक नया लैंगलेट बनाएं (विस्तार भाषा)

import EasyExtend
EasyExtend.new_langlet("mystmts", prompt = "my> ", source_ext = "mypy")

अतिरिक्त विनिर्देशन के बिना EasyExtend / langlets / mystmts / के तहत फाइलों का एक गुच्छा बनाया जाएगा।

ii) रहस्य / parsedef / Grammar.ext खोलें और निम्नलिखित पंक्तियाँ जोड़ें

small_stmt: (expr_stmt | print_stmt  | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | exec_stmt | assert_stmt | my_stmt )

my_stmt: 'mystatement' expr

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

iii) अब किसी को कथन का शब्दार्थ जोड़ना होगा। इसके लिए किसी को msytmts / langlet.py संपादित करना होगा और एक my_stmt नोड आगंतुक जोड़ना होगा।

 def call_my_stmt(expression):
     "defines behaviour for my_stmt"
     print "my stmt called with", expression

 class LangletTransformer(Transformer):
       @transform
       def my_stmt(self, node):
           _expr = find_node(node, symbol.expr)
           return any_stmt(CST_CallFunc("call_my_stmt", [_expr]))

 __publish__ = ["call_my_stmt"]

iv) ldts / मिस्टमेट्स और प्रकार के लिए सीडी

python run_mystmts.py

अब एक सत्र शुरू किया जाएगा और नए परिभाषित बयान का इस्तेमाल किया जा सकता है:

__________________________________________________________________________________

 mystmts

 On Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)]
 __________________________________________________________________________________

 my> mystatement 40+2
 my stmt called with 42

एक तुच्छ बयान में आने के लिए कुछ कदम काफी है, है ना? कोई एपीआई नहीं है जो व्याकरण की परवाह किए बिना सरल चीजों को परिभाषित करने की अनुमति देता है। लेकिन ईई कुछ बग्स पर बहुत विश्वसनीय मोडुलो है। तो यह केवल समय की बात है कि एक एपीआई उभरता है जो प्रोग्रामर को सुविधाजनक सामान की तरह परिभाषित करता है जैसे कि infix ऑपरेटर्स या छोटे सुविधाजनक OO प्रोग्रामिंग का उपयोग करके छोटे स्टेटमेंट। पैंथन में पूरी भाषाओं को एम्बेड करने जैसी अधिक जटिल चीजों के लिए एक लैंगलेट के निर्माण के माध्यम से एक पूर्ण व्याकरण दृष्टिकोण के आसपास जाने का कोई रास्ता नहीं है।


11

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


jcomeau@intrepid:~/$ cat demo.py; ./demo.py
#!/usr/bin/python -i
'load everything needed under "package", such as package.common.normalize()'
import os, sys, readline, traceback
if __name__ == '__main__':
    class t:
        @staticmethod
        def localfunction(*args):
            print 'this is a test'
            if args:
                print 'ignoring %s' % repr(args)

    def displayhook(whatever):
        if hasattr(whatever, 'localfunction'):
            return whatever.localfunction()
        else:
            print whatever

    def excepthook(exctype, value, tb):
        if exctype is SyntaxError:
            index = readline.get_current_history_length()
            item = readline.get_history_item(index)
            command = item.split()
            print 'command:', command
            if len(command[0]) == 1:
                try:
                    eval(command[0]).localfunction(*command[1:])
                except:
                    traceback.print_exception(exctype, value, tb)
        else:
            traceback.print_exception(exctype, value, tb)

    sys.displayhook = displayhook
    sys.excepthook = excepthook
>>> t
this is a test
>>> t t
command: ['t', 't']
this is a test
ignoring ('t',)
>>> ^D

4

मुझे नए कथन जोड़ने पर एक मार्गदर्शिका मिली है:

https://troeger.eu/files/teaching/pythonvm08lab.pdf

मूल रूप से, नए बयानों को जोड़ने के लिए, आपको Python/ast.c(अन्य बातों के अलावा) को संपादित करना होगा और अजगर बाइनरी को फिर से जोड़ना होगा ।

हालांकि यह संभव है, नहीं। आप फ़ंक्शंस और क्लासेस के माध्यम से लगभग सब कुछ हासिल कर सकते हैं (जो अभ्यस्त लोगों को आपकी स्क्रिप्ट चलाने के लिए अजगर को फिर से जोड़ने की आवश्यकता होती है ..)


पीडीएफ के लिए वास्तविक लिंक - कि "ऑटोनोवर्सन" टूट गया है और भगवान के लिए टूट गया है अब लंबे समय से जानते हैं: troeger.eu/files/teaching/pythonvm00000ab.pdf
ZXX

3

EasyExtend का उपयोग करके ऐसा करना संभव है :

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


1
उस लिंक के बाद अब एक पेज दिया गया है: "EasyExtend मर चुका है। जो लोग EE में रुचि रखते हैं उनके लिए Langscape नाम की एक उत्तराधिकारी परियोजना है, जिसे पूरा नाम दिया गया है, एक ही यात्रा।" चूंकि यह खतरा है कि यह सूचना पृष्ठ मृत हो सकता है, हो सकता है कि उत्तर को अपडेट करना एक अच्छा विचार हो।
celtschk

3

यह भाषा वाक्य रचना में नए बयानों को जोड़ नहीं रहा है, लेकिन मैक्रोज़ एक शक्तिशाली उपकरण हैं: https://github.com/lihaoyi/macropy


1

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


1

अजगर पर आधारित एक भाषा है जिसे लोगिक्स कहा जाता है जिसके साथ आप ऐसी चीजें कर सकते हैं। यह कुछ समय के लिए विकास के अधीन नहीं है, लेकिन जिन विशेषताओं के लिए आपने नवीनतम संस्करण के साथ काम करने के लिए कहा है ।


दिलचस्प लगता है, लेकिन लगता है कि 2009 के आसपास मृत्यु हो गई है: web.archive.org/web/20090107014050/http://livelogix.net/logix
टोबीस किंजलर

1

डेकोरेटर के साथ कुछ चीजें की जा सकती हैं। चलो मान लेते हैं, पायथन का कोई withबयान नहीं था । हम फिर इस तरह के एक समान व्यवहार को लागू कर सकते हैं:

# ====== Implementation of "mywith" decorator ======

def mywith(stream):
    def decorator(function):
        try: function(stream)
        finally: stream.close()
    return decorator

# ====== Using the decorator ======

@mywith(open("test.py","r"))
def _(infile):
    for l in infile.readlines():
        print(">>", l.rstrip())

यह एक सुंदर अशुद्ध समाधान है, हालांकि यहां किया गया है। विशेष रूप से व्यवहार जहां डेकोरेटर फ़ंक्शन को कॉल करने और सेट _करने के लिए Noneअप्रत्याशित है। स्पष्टीकरण के लिए: यह डेकोरेटर लिखने के बराबर है

def _(infile): ...
_ = mywith(open(...))(_) # mywith returns None.

और सज्जाकार आमतौर पर संशोधित करने की अपेक्षा करते हैं, कार्य करने के लिए नहीं।

मैंने एक स्क्रिप्ट में पहले ऐसी विधि का उपयोग किया था जहां मुझे कई कार्यों के लिए अस्थायी रूप से कार्यशील निर्देशिका निर्धारित करनी थी।


0

दस साल पहले आप नहीं कर सकते थे, और मुझे संदेह है कि यह बदल गया है। हालाँकि, वाक्यविन्यास को संशोधित करना कठिन नहीं था, अगर आप अजगर को फिर से तैयार करने के लिए तैयार थे, और मुझे संदेह है कि यह बदल गया है, या तो।

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