2015-11-07 61 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