2015-11-07 63 views
1

我想创建一个解析器,它将某些数学转换为C.这归结为必须找到表单的嵌套表达式...^x,并用pow替换(..., x)(这里x是一个数字)。解析器失败 - pyparsing

一些假设:

  • ^只会出现在地方表明幂
  • 的“X”将永远是一个实数为代表用C
  • 之前的指数^,在.. 。将是某种变量,数字或者括号中的分组表达式。变量将是一个带有(可能)下划线的字母数字字符串。

我可以澄清更多的假设,如果我错过了什么(只是问)。

我的代码如下所示,以及一个失败的例子。为什么这是失败的?

代码:

from pyparsing include * 
def parse(s): 
    identifier = Regex(r'-?[a-zA-Z0-9_]+') 
    real = Regex(r'-?[0-9]+(\.?[0-9]+)?(e-?[0-9]+)?') 
    oper = Regex(r'[\*\+/-]-?') 
    #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|identifier) + powerOp + real #order matters? 
    powerExpr.setParseAction(lambda tokens: 'pow(%s,%s)' % (tokens[0], tokens[2])) 

    # now define the possible expressions for arithOperand, including a powerExpr 
    arithOperand <<= powerExpr | real | identifier | groupedArithExpr 

    # convert parsed list of strings to a single string 
    groupedArithExpr.setParseAction(''.join) 

    return arithExpr.transformString(s) 

这是造成失败的字符串:

s = ((s9*(s4*s6+c4*c6*s5)-c5*c6*c9)*(-(c4*s6-c6*s4*s5)*(x1*(1.0/2.0)+BASE_ORIGIN_Z*(s4*s6+c4*c6*s5)+(c4*s6-c6*s4*s5)*(-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y*(1.0/2.0)+LEG_LINK_EXTENTS_Y*(1.0/2.0))+BASE_ORIGIN_X*c5*c6)+(c4*c6+s4*s5*s6)*(x2*(1.0/2.0)-BASE_ORIGIN_Z*(c6*s4-c4*s5*s6)-(c4*c6+s4*s5*s6)*(-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y*(1.0/2.0)+LEG_LINK_EXTENTS_Y*(1.0/2.0))+BASE_ORIGIN_X*c5*s6)+c5*s4*(x3*(1.0/2.0)-BASE_ORIGIN_X*s5+BASE_ORIGIN_Z*c4*c5-c5*s4*(-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y*(1.0/2.0)+LEG_LINK_EXTENTS_Y*(1.0/2.0))))+(c4*s6-c6*s4*s5)*((c9*s5+c4*c5*s9)*(x3*(1.0/2.0)-BASE_ORIGIN_X*s5+BASE_ORIGIN_Z*c4*c5-c5*s4*(-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y*(1.0/2.0)+LEG_LINK_EXTENTS_Y*(1.0/2.0)))+(s9*(s4*s6+c4*c6*s5)-c5*c6*c9)*(x1*(1.0/2.0)+BASE_ORIGIN_Z*(s4*s6+c4*c6*s5)+(c4*s6-c6*s4*s5)*(-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y*(1.0/2.0)+LEG_LINK_EXTENTS_Y*(1.0/2.0))+BASE_ORIGIN_X*c5*c6)-(s9*(c6*s4-c4*s5*s6)+c5*c9*s6)*(x2*(1.0/2.0)-BASE_ORIGIN_Z*(c6*s4-c4*s5*s6)-(c4*c6+s4*s5*s6)*(-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y*(1.0/2.0)+LEG_LINK_EXTENTS_Y*(1.0/2.0))+BASE_ORIGIN_X*c5*s6)))^2 

这里的指数不转换为一个POW和整个输入表达式保持完好,没有变化。我的解析器出了什么问题?

回答

1

我认为你唯一缺少的是你没有处理领先的一元' - '操作符。这是很容易结合到您的arithOperand表达使用:

arithOperand <<= Optional('-') + (powerExpr | real | identifier | groupedArithExpr) 

进行此更改后,代码生成的输出(其不包含“^”运算符):

pow 
    (
    (
     (s9* 
     (s4*s6+c4*c6*s5) 
     -c5*c6*c9) 
    * 
     (- 
     (c4*s6-c6*s4*s5) 
     * 
     (x1*pow 
      (
      (1.0/2.0) 
      ,3) 
     +BASE_ORIGIN_Z* 
      (s4*s6+c4*c6*s5) 
     + 
      (c4*s6-c6*s4*s5) 
     * 
      (-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y* 
      (1.0/2.0) 
      +LEG_LINK_EXTENTS_Y* 
      (1.0/2.0) 
     ) 
     +BASE_ORIGIN_X*c5*c6) 
     + 
     (c4*c6+s4*s5*s6) 
     * 
     (x2* 
      (1.0/2.0) 
     -BASE_ORIGIN_Z* 
      (c6*s4-c4*s5*s6) 
     - 
      (c4*c6+s4*s5*s6) 
     * 
      (-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y* 
      (1.0/2.0) 
      +LEG_LINK_EXTENTS_Y* 
      (1.0/2.0) 
     ) 
     +BASE_ORIGIN_X*c5*s6) 
     +c5*s4* 
     (x3* 
      (1.0/2.0) 
     -BASE_ORIGIN_X*s5+BASE_ORIGIN_Z*c4*c5-c5*s4* 
      (-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y* 
      (1.0/2.0) 
      +LEG_LINK_EXTENTS_Y* 
      (1.0/2.0) 
     ) 
     ) 
    ) 
    + 
     (c4*s6-c6*s4*s5) 
    * 
     (
     (c9*s5+c4*c5*s9) 
     * 
     (x3* 
      (1.0/2.0) 
     -BASE_ORIGIN_X*s5+BASE_ORIGIN_Z*c4*c5-c5*s4* 
      (-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y* 
      (1.0/2.0) 
      +LEG_LINK_EXTENTS_Y* 
      (1.0/2.0) 
     ) 
     ) 
     + 
     (s9* 
      (s4*s6+c4*c6*s5) 
     -c5*c6*c9) 
     * 
     (x1* 
      (1.0/2.0) 
     +BASE_ORIGIN_Z* 
      (s4*s6+c4*c6*s5) 
     + 
      (c4*s6-c6*s4*s5) 
     * 
      (-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y* 
      (1.0/2.0) 
      +LEG_LINK_EXTENTS_Y* 
      (1.0/2.0) 
     ) 
     +BASE_ORIGIN_X*c5*c6) 
     - 
     (s9* 
      (c6*s4-c4*s5*s6) 
     +c5*c9*s6) 
     * 
     (x2* 
      (1.0/2.0) 
     -BASE_ORIGIN_Z* 
      (c6*s4-c4*s5*s6) 
     - 
      (c4*c6+s4*s5*s6) 
     * 
      (-BASE_ORIGIN_Y+BASE_LINK_EXTENTS_Y* 
      (1.0/2.0) 
      +LEG_LINK_EXTENTS_Y* 
      (1.0/2.0) 
     ) 
     +BASE_ORIGIN_X*c5*s6) 
    ) 
    ) 
    ,2) 

编辑:(一些化妆品清理)

为了让您的操作数为单可求组,你可能会做最好的使用来定义:

baseArithOperand = powerExpr | real | identifier | groupedArithExpr 
arithOperand <<= Group('-' + baseArithOperand) | baseArithOperand 

此外,添加一元减号将允许您删除添加到实数和标识符的前导' - '。

对于您关于“排列顺序问题”的问题 - 是的。幸运的是,你已经将powerExpr放在了groupedArithExpr之前,这是唯一可能导致问题的两种选择。如果这两者的顺序相反,那么我认为powerExprs将不会被正确评估,因为前导()分组表达式将使用groupedArithExpr表达式进行分析,而在powerExpr的后续分析错误中,您会留下'^ '字符。你可能从'|'更改运算符(“匹配第一”)到'^'运算符(“匹配最长”),这将迫使所有替代方案进行评估并选择最长匹配。但在递归语法中,“最长匹配”可以运行得非常缓慢,甚至可以永久递增,所以我鼓励人们设计“匹配第一”。

EDIT2:

没关系有关组,我忘了,你只是在做transformString这里 - 只要坚持自己的原创:

arithOperand <<= Optional('-') + (powerExpr | real | identifier | groupedArithExpr) 

但是仔细一看,我现在看到的是标识确实是过分宽泛,并将匹配整数以及标识符。不如在这里使用2个参数的字(而不必担心速度 - Word将在内部建立和使用进行匹配的正则表达式):

identifier = Word(alphas, alphanums+'_') # not Regex(r'[a-zA-Z0-9_]+') 
real = Regex(r'\d+(\.\d*)?([Ee][+-]?\d+)?') 
oper = oneOf("* + - /") 

EDIT3:为了您的方便,这里是我写的函数缩进你的怪物的一个例子:

def output_nested_parens(s): 
    out = '' 
    indent = '' 
    for c in s: 
     if c == '(': 
      indent += ' ' 
      out += '\n' + indent 
      out += c 
     elif c == ')': 
      indent = indent[2:] 
      out += c 
      out += '\n' + indent 
     else: 
      out += c 
    return out 
+0

你是对的,你发布这个就像我去发布,我得到它的工作。谢谢!唯一的问题是,这使得事情变得难以忍受 - 要花20个小时来分析我的20MB文件,每个2MB元素一个小时。我想知道如果你知道有什么方法可以加速packrat的速度,那就是使用大量的内存? – user650261

+0

如果在实际和标识符中删除可选的前导' - ',并将操作符还原为“oneOf(”+ - * /“)”,那么事情会更好吗? – PaulMcG

+0

我只是去检查一下,但看到你做了一些编辑 - 哪些建议应该在你看来测试? – user650261