Можно ли добавить новые операторы к синтаксису Python?

117
задан dreftymac 26 June 2018 в 22:19
поделиться

9 ответов

Один способ сделать вещи как это состоит в том, чтобы предварительно обработать источник и изменить его, переведя Ваш добавленный оператор в Python. Существуют различные проблемы, которые принесет этот подход, и я не рекомендовал бы его для общего использования, но для экспериментирования с языком или метапрограммирования определенной цели, это может иногда быть полезно.

, Например, позволяет, говорят, что мы хотим представить "myprint" оператор, который вместо того, чтобы печатать на экран вместо этого регистрируется в определенный файл. т.е.:

myprint "This gets logged to file"

было бы эквивалентно [1 112]

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

существуют различные варианты относительно того, как сделать замену, от regex замены до генерации AST, к записи Вашего собственного синтаксического анализатора в зависимости от того, как близко Ваш синтаксис соответствует существующему Python. Хороший промежуточный подход должен использовать модуль токенизатора. Это должно позволить Вам добавлять новые ключевые слова, управляющие структуры и т.д. при интерпретации источника так же к интерпретатору Python, таким образом предотвращении, чтобы сырая нефть поломки regex решения вызвала бы. Для вышеупомянутого "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

Это требует, чтобы Вы обработали свой специализированный код по-другому по сравнению с нормальными модулями Python как бы то ни было. т.е." some_mod = myimport("some_mod.py")", а не" import some_mod"

Другой довольно аккуратный (хотя hacky) решение состоит в том, чтобы создать пользовательское кодирование (См. PEP 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), любой код, запускающийся с комментария "# кодирование: 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. Основной отлаживает. Весь Python видит, предварительно обработанный файл, что означает, что текст, распечатанный в отслеживании стека и т.д., будет относиться к этому. При выполнении значительного перевода это может очень отличаться от исходного текста. Пример выше не изменяет номера строки и т.д., так не будет слишком отличаться, но чем больше Вы изменяете его, тем тяжелее он должен будет выяснить.

52
ответ дан Brian 24 November 2019 в 02:06
поделиться

Да, в некоторой степени это возможно. Существует модуль там, который использует 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"
21
ответ дан Constantin 24 November 2019 в 02:06
поделиться

За исключением изменения и перекомпиляции исходного кода (который возможны с открытым исходным кодом), изменяя основной язык не действительно возможно.

Даже при перекомпиляции источника это не был бы Python, просто взломанная измененная версия, в которую необходимо очень бояться представлять ошибки.

Однако я не уверен, почему Вы хотели бы. Объектно-ориентированные функции Python делают довольно простым достигнуть подобных результатов с языком как есть.

13
ответ дан paxdiablo 24 November 2019 в 02:06
поделиться

Общий ответ: необходимо предварительно обработать исходные файлы.

более определенный ответ: установка EasyExtend, и проходит следующие шаги

, i) Создают новый 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

, Это достаточно для определения синтаксиса нового оператора. small_stmt нетерминальной является часть грамматики Python, и это - место, где новый оператор сцепляется в. Синтаксический анализатор теперь распознает новый оператор, т.е. исходный файл, содержащий его, будет проанализирован. Компилятор отклонит его хотя, потому что это все еще должно быть преобразовано в действительный Python.

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

Довольно много шагов для прибытия в тривиальный оператор, правильно? Еще нет API, который позволяет, каждый определяет простые вещи, не имея необходимость заботиться о грамматиках. Но EE очень надежен по модулю некоторые ошибки. Таким образом, это - просто вопрос времени, что API появляется, который позволяет программистам определить удобный материал как инфиксные операторы или маленькие операторы, использующие просто удобное программирование OO. Для более сложных вещей как встраивание целых языков в Python посредством создания langlet нет никакого способа обойти полный подход грамматики.

12
ответ дан 24 November 2019 в 02:06
поделиться

Я нашел руководство по добавлению новых операторов:

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

В основном, для добавления новых операторов необходимо отредактировать Python/ast.c (среди прочего) и перекомпилировать двоичный файл Python.

, В то время как это возможно, не делать. Можно достигнуть почти всего через функции и классы (какая привычка требуют, чтобы люди перекомпилировали Python только для выполнения сценария..)

4
ответ дан James 24 November 2019 в 02:06
поделиться

Возможно сделать это использование EasyExtend:

EasyExtend (EE) является генератором препроцессора и платформой метапрограммирования, записанной в чистом Python и интегрированной с CPython. Основной целью EasyExtend является создание дополнительных языков т.е. добавление пользовательского синтаксиса и семантики к Python.

3
ответ дан Matthew Trevor 24 November 2019 в 02:06
поделиться

Не изменяя интерпретатор. Я знаю, что много языков за прошлые несколько лет было описано как "расширяемое", но не в способе, которым Вы описываете. Вы расширяете Python путем добавления функций и классов.

1
ответ дан Bill the Lizard 24 November 2019 в 02:06
поделиться

Существует язык на основе Python, названного Logix, с которым Вы CAN делают такие вещи. Это не разрабатывалось некоторое время, но функции, которые Вы попросили , действительно работают с последней версией.

1
ответ дан Claudiu 24 November 2019 в 02:06
поделиться

Десять лет назад Вы не могли, и я сомневаюсь, что это изменяется. Однако не случалось так, что трудно для изменения синтаксиса тогда, если Вы были готовы перекомпилировать Python, и я сомневаюсь, что это изменяется, также.

0
ответ дан Alex Coventry 24 November 2019 в 02:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: