Python pyparsing-大计算冻结intepreter

Python pyparsing-大计算冻结intepreter,python,python-3.x,parsing,pyparsing,Python,Python 3.x,Parsing,Pyparsing,下面是一个解析器,取自: 从pyparsing导入( 字面意义的, 单词 集团,, 向前地 阿尔法, 阿尔法努姆斯, 正则表达式, 语法异常, Caselesskyword, 抑制, 分隔列表 ) 输入数学 进口经营者 exprStack=[] def推送装置优先(toks): exprStack.append(toks[0]) def推力一元减(toks): 对于toks中的t: 如果t==“-”: exprStack.append(“一元-”) 其他: 打破 bnf=无 def BNF():

下面是一个解析器,取自:

从pyparsing导入(
字面意义的,
单词
集团,,
向前地
阿尔法,
阿尔法努姆斯,
正则表达式,
语法异常,
Caselesskyword,
抑制,
分隔列表
)
输入数学
进口经营者
exprStack=[]
def推送装置优先(toks):
exprStack.append(toks[0])
def推力一元减(toks):
对于toks中的t:
如果t==“-”:
exprStack.append(“一元-”)
其他:
打破
bnf=无
def BNF():
"""
expop::“^”
multop::“*”|“/”
addop::“+”|“-”
整数::['+'|'-']'0'..'9'+
原子::π| E | real | fn'('expr')'|'('expr')'
因子::原子[expop因子]*
术语::因子[多顶因子]*
expr::term[addop term]*
"""
全球bnf
如果不是bnf:
#将CaselessKeyword用于e和pi,以避免意外匹配
#以“e”或“pi”开头的函数(如“exp”);关键词
#Caselesskyword只匹配整个单词
e=Caselesskyword(“e”)
pi=Caselesskyword(“pi”)
#fnumber=组合(单词(“+-”+nums,nums)+
#可选(“.”+可选(单词(nums)))+
#可选(e+字(“+-”+nums,nums)))
#或者使用提供的pyu common.number,但转换回str:
#fnumber=ppc.number().addParseAction(lambda t:str(t[0]))
fnumber=Regex(r“[+-]?\d+(?:\。\d*)?(?:[eE][+-]?\d+)?)
ident=Word(字母,字母+“$”)
加、减、多、div=map(文字,+-*/)
lpar,rpar=map(抑制“()”)
addop=正|负
multop=mult | div
expop=Literal(“^”)
expr=Forward()
expr_list=分隔符列表(组(expr))
#添加用(名称、参数数)元组替换函数标识符的解析操作
def insert_fn_argcount_元组(t):
fn=t.pop(0)
num_args=len(t[0])
t、 插入(0,(fn,num_参数))
fn_call=(ident+lpar-Group(expr_list)+rpar).setParseAction(
插入\u fn\u argcount\u元组
)
原子=(
addop[…]
+ (
(fn|u call | pi | e | fnumber | ident).setParseAction(先推)
|组(lpar+expr+rpar)
)
).setParseAction(按一元减)
#通过将指数定义为“atom[^factor]…”而不是“atom[^atom]…”,我们可以从右向左
#指数,而不是从左到右,即2^3^2=2^(3^2),而不是(2^3)^2。
因子=正向()
因子输入fourFn
>>>fourFn.BNF().parseString(“9+9”,parseAll=True)
>>>val=fourFn.evaluate_堆栈(fourFn.exprStack[:])
>>>打印(val)
18
>>> 
但是当我尝试像
“9^9^9”
这样的大型计算时,程序在尝试计算
val
时会冻结

导入fourFn >>>fourFn.BNF().parseString(“9^9^9”,parseAll=True) >>>val=fourFn.evaluate_堆栈(fourFn.exprStack[:])
如果计算太大,是否可以停止并抛出错误?

这可能是不可能的,因为Python太慢,而不是您的解释器。您正试图计算
9**387420489
,这是一个巨大的数字(Python的整数实际上是无界的,因此它将继续计算该整数,直到内存耗尽)。由于Python一次只能运行一个线程,并且整数求幂是用C实现的,因此纯Python代码无法知道解释器在计算某些东西时“卡住”了


您可以设置一些其他代码来监视此代码的执行,如果它没有在
n
秒内终止,则将其杀死,但这可能是杀伤力过大。顺便说一句,我已经计算了这个数字5分钟了,但仍然没有答案。

请查看
plusminus
包,这是一个基于pyparsing的包,用于评估不受信任的算术表达式。它包括防止此类拒绝服务攻击的措施。目前还有一个现场演示,为什么不总是将数字视为一个
float
,而先尝试一个
int
?或者编写您自己的
pow
版本,首先进行健全性检查看看pyparsing派生的
plusminus
包。它包含一些先发制人的逻辑,试图避免这些类型的攻击表达式。@TheOneMusic,那么您需要一个不同的整数类型。一种选择是通过使用以下内容忽略溢出(因此,在溢出的情况下,会很快产生错误的结果);另一种选择可能是使用试探法并检查表达式是否会爆炸,如
加减
;或者直接实现您自己的按位慢-一个**整数加法/乘法,它将捕获溢出。顺便说一句,
9**387420489
已经运行了20分钟,消耗了250 MB的RAM并保持going@ForceBru:在许多情况下,当你做这么大的求幂运算时,它是在做模运算(例如模拟RSA)时进行的,在这种情况下,求幂运算之后总是伴随着模降。在这种情况下,Python通过将所有三个参数(base、index、module)传递给
pow
函数来直接支持它。当然,
9**9**9
需要永远,但是
pow(9,9**9,1234567890)
是即时的。可能不适用于OP的情况,但如果不适用,则OP的情况可能不正常。@ForceBru:仅供参考:如果您的python可执行文件达到250MB的RAM,则可能已经达到一半。在我的机器上,它大约需要45分钟,但我没有世界上最快的机器,峰值内存不足5G。但是,如果您要求Python打印该值(可能只需在CLI中键入999),则将该数字转换为十进制数字字符串大约需要很长时间。(或者至少是一对