syntax tutorial - هل يمكنك إضافة عبارات جديدة إلى بنية Python؟



basics pdf (12)

هل يمكنك إضافة عبارات جديدة (مثل print ، raise ، with ) إلى بناء جملة بايثون؟

قل ، للسماح ..

mystatement "Something"

أو،

new_if True:
    print "example"

ليس كثيرًا إذا كان يجب عليك ذلك ، بل بالأحرى إذا كان ذلك ممكنًا (قصراً في تعديل شفرة مترجم الشفرات)


Answers

إنه لا يضيف عبارات جديدة إلى صيغة اللغة بالضبط ، لكن وحدات الماكرو هي أداة قوية: https://github.com/lihaoyi/macropy


قبل عشر سنوات لم تستطع ، وأشك في أن هذا تغير. ومع ذلك ، لم يكن من الصعب تعديل بناء الجملة في ذلك الوقت إذا كنت مستعدًا لإعادة ترجمة python ، وأشك في أن هذا قد تغير أيضًا.


هناك لغة مبنية على كلمة python تسمى Logix والتي يمكنك من خلالها تنفيذ مثل هذه الأشياء. لم يكن قيد التطوير لفترة من الوقت ، ولكن الميزات التي طلبتها تعمل مع أحدث إصدار.


لقد عثرت على دليل حول إضافة عبارات جديدة ، تم تحويلها من PDF إلى HTML بواسطة Google:

http://209.85.173.104/search?q=cache:IjUb82taSq0J:www.troeger.eu/teaching/pythonvm08lab.pdf+python+add+statement&hl=en&ct=clnk&cd=10

في الأساس ، لإضافة عبارات جديدة ، يجب عليك تحرير Python/ast.c (من بين أشياء أخرى) وإعادة ترجمة ثنائي python.

في حين أنه من الممكن ، لا تفعل ذلك. يمكنك تحقيق كل شيء تقريبًا عبر الوظائف والدروس (التي لن تتطلب من الناس إعادة ترجمة python لمجرد تشغيل البرنامج النصي الخاص بك ..)


إحدى الطرق لفعل أشياء كهذه هي المعالجة المسبقة للمصدر وتعديله ، وترجمة العبارة المضافة إلى python. هناك العديد من المشاكل التي سيأتي بها هذا النهج ، ولا أوصي باستخدامها بشكل عام ، ولكن بالنسبة للتجريب باللغة ، أو البرمجة المبنية لغرض محدد ، يمكن أن يكون مفيدًا في بعض الأحيان.

على سبيل المثال ، دعنا نقول أننا نريد تقديم عبارة "myprint" ، بدلاً من الطباعة على الشاشة بدلاً من ذلك ، سجِّل الدخول إلى ملف معين. أي:

myprint "This gets logged to file"

سيكون معادلا ل

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

هناك العديد من الخيارات حول كيفية إجراء الاستبدال ، من استبدال regex إلى إنشاء AST ، لكتابة المحلل اللغوي الخاص بك اعتمادًا على مدى قرب بناء الجملة الخاص بك يطابق python الموجودة. نهج وسيط جيد هو استخدام وحدة tokenizer. هذا يجب أن يسمح لك بإضافة كلمات رئيسية جديدة ، هياكل تحكم إلخ أثناء تفسير المصدر بشكل مشابه لمترجم python ، وبالتالي تجنب حلول تكسير الخام الخام قد يسبب. للحصول على "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 كلمة رئيسية بشكل فعال ، لذا فإن الاستخدام كمتغير في مكان آخر سيسبب مشاكل على الأرجح)

المشكلة إذن هي كيفية استخدامه بحيث يكون كودك قابلاً للاستخدام من python. إحدى الطرق ستكون فقط لكتابة دالة الاستيراد الخاصة بك ، واستخدامها لتحميل التعليمات البرمجية المكتوبة بلغتك المخصصة. أي:

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 "

حل آخر أنيق إلى حد ما (وإن كان على ما يرام) هو إنشاء تشفير مخصص (انظر PEP 263 ) كما توضح this الوصفة. يمكنك تنفيذ ذلك على النحو التالي:

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" من خلال خطوة المعالجة السابقة. على سبيل المثال.

# 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")

المحاذير:

هناك مشاكل في نهج المعالجة المسبقة ، حيث قد تكون على دراية إذا كنت قد عملت مع المعالج الأولي C. الرئيسي هو تصحيح الأخطاء. يرى جميع الثعبان هو الملف الذي تم تجهيزه مسبقًا والذي يعني أن النص المطبوع في تتبع المكدس سيشير إلى ذلك. إذا كنت قد أنجزت ترجمة كبيرة ، فقد يكون هذا مختلفًا تمامًا عن النص المصدر. المثال أعلاه لا يغير أرقام الأسطر إلخ ، لذا لن يكون الأمر مختلفًا تمامًا ، ولكن كلما تغيّرت أكثر ، كلما كان من الصعب معرفة ذلك.


نعم ، إلى حد ما ممكن. هناك module هناك تستخدم sys.settrace() لتطبيق goto و comefrom "keywords":

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"

إليك طريقة بسيطة جدًا ولكنها كريبة لإضافة عبارات جديدة ، في الوضع التفسري فقط . أنا أستخدمه لأوامر 1-حرف صغيرة لتحرير التعليقات الجينية باستخدام sys.displayhook فقط ، ولكن فقط حتى أتمكن من الإجابة على هذا السؤال أضفت sys.excepthook للأخطاء النحوية كذلك. هذا الأخير هو حقا قبيحة ، وجلب رمز الخام من المخزن المؤقت للقراءة. الفائدة هي أنه من السهل إضافة عبارات جديدة بهذه الطريقة.


[email protected]:~/$ 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


ليس بدون تعديل المترجم. أعرف أن الكثير من اللغات في السنوات الماضية قد وصفت بأنها "قابلة للتوسعة" ، ولكن ليس بالطريقة التي تصفها. يمكنك تمديد بايثون عن طريق إضافة الوظائف والفصول الدراسية.


من الممكن القيام بذلك باستخدام EasyExtend :

EasyExtend (EE) هو مولد preprocessor وإطار metaprogramming مكتوب في بيثون نقية ومتكاملة مع CPython. الغرض الرئيسي من EasyExtend هو إنشاء لغات الإمتداد أي إضافة بناء جملة مخصصة وعلم الدلالة إلى Python.


باختصار من تغيير وإعادة ترجمة التعليمات البرمجية المصدر (وهو أمر ممكن مع المصدر المفتوح) ، فإن تغيير اللغة الأساسية غير ممكن حقًا.

حتى إذا قمت بإعادة ترجمة المصدر ، فإنه لن يكون python ، فقط النسخة التي قمت باختراقها التي تم تغييرها والتي تحتاج إلى توخي الحذر الشديد لعدم إدخال الخلل فيها.

ومع ذلك ، لست متأكدًا من سبب رغبتك في ذلك. تجعل ميزات بيثون الموجهة للكائنات من السهل جدًا تحقيق نتائج مشابهة مع اللغة كما هي.


إجابة عامة: تحتاج إلى معالجة الملفات المصدر مسبقًا.

إجابة أكثر تحديدًا: قم بتثبيت EasyExtend ، EasyExtend الخطوات التالية

ط) إنشاء langlet (لغة الإرشاد) جديدة

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

دون مواصفات إضافية يجب إنشاء مجموعة من الملفات تحت EasyExtend / langlets / mystmts /.

ii) فتح mystmts / 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

هذا يكفي لتحديد بنية الجملة الجديدة الخاصة بك. تعتبر non-terminal الصغيرة جزءًا من قواعد Python وهو المكان الذي يتم فيه إرفاق العبارة الجديدة. سيتعرف المحلل اللغوي الآن على العبارة الجديدة أي أنه سيتم تحليل ملف المصدر الذي يحتوي عليه. سوف يرفض المترجم على الرغم من ذلك لأنه لا يزال يتعين تحويله إلى بايثون صالحة.

3) الآن يجب على المرء إضافة دلالات البيان. لهذا واحد لديه لتحرير 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) cd إلى langlets / mystmts والنوع

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

خطوات قليلة جدا للقدوم إلى بيان تافه ، أليس كذلك؟ لا توجد واجهة برمجة تطبيقات حتى الآن تتيح للمرء إمكانية تحديد أشياء بسيطة دون الحاجة إلى الاهتمام بالقواعد النحوية. لكن EE موثوق للغاية modulo بعض الخلل. لذا ، فإن الأمر مجرد مسألة وقت تظهر فيه واجهة برمجة التطبيقات (API) التي تسمح للمبرمجين بتعريف أشياء ملائمة مثل مشغلات ال infix أو عبارات صغيرة باستخدام برمجة OO المريحة. لأشياء أكثر تعقيدا مثل تضمين لغات كاملة في بايثون عن طريق بناء لانغليت لا توجد طريقة للالتفاف على نهج القواعد الكاملة.


حسنًا ، أرى أنه في cin الثاني قمت بالتبديل من cin إلى scanf ، وهو أول اقتراح سأقوم به (cin هو sloooooooooooow). الآن ، إذا قمت بالتبديل من scanf إلى fgets ، سترى دفعة أخرى في الأداء: fgets هي أسرع وظيفة C ++ لإدخال السلسلة.

راجع للشغل ، لم أكن أعرف شيئا عن ذلك المزامنة ، لطيفة. ولكن لا يزال عليك محاولة fgets .





python syntax