[英]Pyparsing - Finding Nested Polynomials
我正在搜索一些代數,並嘗試匹配該形式的所有表達式:
(subexp)^C
其中C是整數,而subexp可以是兩件事之一:
a)可以是(subexp)^ C形式的另一個表達式b)可以是var1 op var2 op var3 ... op varn形式的表達式
其中var是字母數字形式的數字,例如l2,cd3,hello53等,而op是-,*或+。 第二個選項也可以將術語按括號進行分組。
(沒有空格,為了清楚起見,我只是在某些地方添加了空格)
因此,例如:
(a12 + c33 + d34)^2
(a12 * c33 +-d34)^2
((a12 * c33)^5 + c3)^2
等等
這種形式的表達式將嵌入一行文本中。 我需要找到(subexp)^ C的所有實例,並用pow(subexp,C)替換它們。 簡而言之,我正在嘗試將一些計算機代數轉換為可運行的C代碼。
我最初使用regexp進行此操作,但意識到它不是正則表達式。 對於非嵌套的情況,正則表達式為:
line = re.sub(r'\(([a-zA-Z_]+[0-9]+\+?-?\*?)+\)\^[0-9]+', replace_with_pow, line)
這里line是具有嵌入式多項式的行,而replace_with_pow是執行替換的函數。
不幸的是,當表達式可以嵌套時,它就是CFG。
我研究了pyparsing,但發現示例很難解析,並且缺少文檔。 不過,這似乎是推薦的庫。
誰能提供一個示例腳本來說明如何查找嵌套表達式並替換它們? (如果您希望我可以對此進行簡化,則可能是一個簡化的問題)
編輯:更新:通過pyparsing,我現在可以使用以下代碼來解析所有具有{填充(...)^ C填充}形式的嵌套表達式:
closing = pyparsing.Word( ")^" + pyparsing.nums )
thecontent = pyparsing.Word(pyparsing.alphanums) | '+' | '-' | '*' | ','
parens = pyparsing.nestedExpr( '(', closing, content=thecontent)
thecontent2 = thecontent | parens
parens2 = pyparsing.nestedExpr('{', '}', content=thecontent2)
res = parens2.parseString("{sdf(a + (a + b)^5 + c)asdf}")
這給我帶來兩個問題:
a)當我匹配了我的^ 5時,解析器將其消耗掉。 如何提取^ 5? b)是否有快速/簡便的方法來用pyparsing進行替換?
解決幾乎所有解析問題的第一步都是為要解析的語法提供精確的定義。 如果該語法是上下文無關的,那么上下文無關的語法是一種很好的描述方式,通常比非正式描述或示例目錄要好得多。
在這個問題上,你的例子
a12 * c33 +-d34
不符合描述
var op var op var ... op var
因為它有兩個op
身旁由端。 在這個例子中
((a12 * c33)^5 + c3)^2
subexp (a12 * c33)^5 + c3
也不是var op var
; 而是( subexp ) op var
。 (我知道,您的文字中提到“術語可以用括號分組”,但是沒有提及“術語”實際上可以是子subexp
因為它可能是冪運算,如您的示例。)
(如果我猜對了)更精確的語法可能是用“ yacc”語法編寫的:
val : IDENTIFIER
| '(' expr ')'
term: val
| val '^' INTEGER
| '-' val
prod: term
| term '*' prod
expr: prod
| expr '+' prod
| expr '-' prod
以上不允許--var
, var^I1^I2
甚至-var^I
從您的描述中我不知道您是否希望它們起作用,但是修改起來很容易。 另外,我會認為數字文字不僅可以接受變量,而且可以接受,但問題描述中也沒有提及(它只需要添加到val
)。
實際上,您可能不需要如此精確地解析,因為您似乎計划只生成C代碼,因此不需要處理運算符優先級。 另一方面,您可能有一天可能希望進行代數轉換,在這種情況下,需要更精確的解析,並且在任何情況下都不會花費太多。
一旦有了語法,就可以使用ply
(例如)將其轉換為可執行的解析器。
(這是我放在一起的一個層板文件。但這不是很好的樣式:()
from ply import lex, yacc
class Lexer(object):
tokens = ('INTEGER', 'IDENTIFIER')
literals = '+ - * ( ) ^'.split()
t_ignore = ' \t\n'
t_INTEGER = r'[1-9][0-9]*'
t_IDENTIFIER = r'[a-z]+[0-9]+'
def t_error(self, t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
def build(self, **kwargs):
self.lexer = lex.lex(module=self)
# Parser starts here
tokens = Lexer.tokens
start = 'expr'
def p_unit(p):
'''val : IDENTIFIER
term : val
prod : term
expr : prod
'''
p[0] = p[1]
def p_paren(p):
'''val : '(' expr ')' '''
p[0] = p[2]
def p_binary(p):
'''expr : expr '+' prod
expr : expr '-' prod
prod : term '*' prod
'''
p[0] = '(%s%s%s)' %(p[1], p[2], p[3])
def p_pow(p):
'''term : val '^' INTEGER'''
p[0] = 'pow(%s, %s)' % (p[1], p[3])
def p_unary(p):
'''term : '-' val'''
p[0] = '(-%s)' % p[2]
parser = yacc.yacc()
lexer = Lexer().build()
def parse(text):
return parser.parse(text, lexer=lexer)
if __name__ == '__main__':
from sys import argv
for text in argv[1:]:
print(text + ' => ' + parse(text))
快速測試:
$ python exp.py '(a12 + c33 + d34)^2' '(a12 * c33 +-d34)^2'
(a12 + c33 + d34)^2 => pow(((a12+c33)+d34), 2)
(a12 * c33 +-d34)^2 => pow(((a12*c33)+(-d34)), 2)
$ python exp.py '((a12 * c33)^5 + c3)^2'
((a12 * c33)^5 + c3)^2 => pow((pow((a12*c33), 5)+c3), 2)
nestedExpr
並不是真正的解決之道,當嵌套標點內的項目定義不太明確時,便存在pyparsing方法。 在您的情況下,最好使用pyparsing Forward()
定義自己的嵌套。 見下文:
from pyparsing import *
# define arithmetic items
identifier = Word(alphas, alphanums+'_')
integer = Word(nums)
real = Regex(r'\d+\.\d*')
oper = oneOf("* + - /")
# define arithOperand as a Forward, since it could contain nested power expression
arithOperand = Forward()
arithExpr = arithOperand + ZeroOrMore(oper + arithOperand)
groupedArithExpr = '(' + arithExpr + ')'
# define expression for x^y, where x could be any kind of arithmetic term
powerOp = Literal('^')
powerExpr = (groupedArithExpr|real|integer|identifier) + powerOp + integer
powerExpr.setParseAction(lambda tokens: 'pow(%s,%s)' % (tokens[0], tokens[2]))
# now define the possible expressions for arithOperand, including a powerExpr
arithOperand <<= powerExpr | real | integer | identifier | groupedArithExpr
# convert parsed list of strings to a single string
groupedArithExpr.setParseAction(''.join)
# show how transform string will apply parse actions as transforms
print arithOperand.transformString("x = (4*(1 + 3^2) * a)^10")
print
打印x = pow((4*(1+pow(3,2))*a),10)
arithExpr.runTests("""\
(a12 + c33 + d34)^2
(a12 * c33 +-d34)^2
(a12 * (c33 + c3))^2
(a12 * (c33 + c3)^4)^2
((a12 * c33 + 12)^5 + c3)^2""")
版畫
(a12 + c33 + d34)^2
['pow((a12+c33+d34),2)']
(a12 * c33 +-d34)^2
^
Expected ")" (at char 11), (line:1, col:12)
(a12 * (c33 + c3))^2
['pow((a12*(c33+c3)),2)']
(a12 * (c33 + c3)^4)^2
['pow((a12*pow((c33+c3),4)),2)']
((a12 * c33 + 12)^5 + c3)^2
['pow((pow((a12*c33+12),5)+c3),2)']
注意上面的transformString的用法-這將在源代碼中搜索匹配項,並將修改后的代碼拼接回找到匹配項的位置。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.