您可以在Python的语法中添加新语句(例如print
,raise
,with
)吗?
说,允许..
mystatement "Something"
或者,
new_if True:
print "example"
如果你应该,那不是很多,而是如果可能的话(没有修改python解释器代码)
答案 0 :(得分:51)
执行此类操作的一种方法是预处理源并对其进行修改,将添加的语句转换为python。这种方法会带来各种各样的问题,我不推荐它用于一般用法,但对于语言实验或特定目的的元编程,它偶尔会有用。
例如,假设我们要引入一个“myprint”语句,而不是打印到屏幕而是登录到特定文件。即:
myprint "This gets logged to file"
等同于
print >>open('/tmp/logfile.txt','a'), "This gets logged to file"
有关如何进行替换的各种选项,从正则表达式替换到生成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
这要求您处理自定义代码的方式与普通python模块不同。即“some_mod = myimport("some_mod.py")
”而不是“import some_mod
”
另一个相当简洁(尽管是hacky)的解决方案是创建一个自定义编码(请参阅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预处理器,您可能会熟悉它。主要是调试。所有python看到的都是预处理文件,这意味着在堆栈跟踪等中打印的文本将引用该文件。如果您执行了重要的翻译,这可能与您的源文本有很大不同。上面的例子不会改变行号等,因此不会有太大的不同,但是你改变的越多,就越难以弄明白。
答案 1 :(得分:20)
是的,在某种程度上是可能的。有一个module使用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"
答案 2 :(得分:14)
如果没有更改和重新编译源代码(使用开源可能 ),则无法更改基本语言。
即使你重新编译源代码,它也不会是python,只是你的hacked-up更改版本,你需要非常小心,不要引入bug。
但是,我不确定你为什么要这样做。 Python的面向对象特性使得使用现有语言获得类似结果非常简单。
答案 3 :(得分:12)
一般答案:您需要预处理源文件。
更具体的答案:安装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编程来定义诸如中缀运算符或小语句等方便的东西。对于更复杂的事情,例如通过构建langlet在Python中嵌入整个语言,无法绕过完整的语法方法。
答案 4 :(得分:9)
这是一种非常简单但糟糕的方式来添加新语句,仅在解释模式下。我正在使用它来进行小的单字母命令,仅使用sys.displayhook来编辑基因注释,但我只能回答这个问题,我也为语法错误添加了sys.excepthook。后者非常难看,从readline缓冲区获取原始代码。好处是,以这种方式添加新语句非常容易。
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
答案 5 :(得分:4)
我找到了添加新语句的指南:
https://troeger.eu/files/teaching/pythonvm08lab.pdf
基本上,要添加新语句,您必须编辑Python/ast.c
(以及其他内容)并重新编译python二进制文件。
虽然有可能,但不要。你可以通过函数和类来实现几乎所有东西(不需要人们重新编译python就可以运行你的脚本了。)
答案 6 :(得分:3)
可以使用EasyExtend:
执行此操作EasyExtend(EE)是一个预处理器 发生器和元编程 用纯Python和Python编写的框架 与CPython集成。主要的 EasyExtend的目的是创造 扩展语言,即添加 Python的自定义语法和语义。
答案 7 :(得分:2)
不是没有修改解释器。我知道过去几年中有很多语言被描述为“可扩展”,但不是你所描述的方式。您可以通过添加函数和类来扩展Python。
答案 8 :(得分:2)
有一种基于python的语言Logix,您可以使用它来执行此类操作。它暂时没有开发,但是您要求使用最新版本的功能。
答案 9 :(得分:2)
装饰器可以完成一些事情。我们举个例子假设,Python没有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.
和装饰器通常需要修改而不是执行函数。
我之前在脚本中使用过这样的方法,我必须暂时为多个函数设置工作目录。
答案 10 :(得分:2)
这并不是在语言语法中添加新语句,但宏是一个强大的工具:https://github.com/lihaoyi/macropy
答案 11 :(得分:0)
十年前你不能,我怀疑这种情况有所改变。但是,如果您准备重新编译python,那么修改语法并不难,我怀疑它是否已经改变了。