2015-10-13 56 views
4

我無法將這個EBNF表達式翻譯成Pyparsing,任何想法?Pyparsing中的遞歸

token:: [A-Z] 
P:: !|token;P|(P^P)|(P*P) 

問題是當使用遞歸時,解釋器失敗。 這樣的表達應該是有效的:

(ASD;!^FFF;!) 
A;B;C;! 
(((A;!^B;!)^C;D;!)*E;!) 

回答

2

建設有Pyparsing遞歸語法,你要想到一點由內向外,用pyparsing的正向類。使用轉發,您可以爲稍後定義的表達式定義一個空的佔位符。這是在pyparsing這個BNF開始:

EXCLAM,SEMI,HAT,STAR = map(Literal,"!;^*") 
LPAR,RPAR = map(Suppress,"()") 
token = oneOf(list(alphas.upper())) 

我用文字來定義你的運營商,但抑制分組()的,我們將使用pyparsing組物理組的成果轉化爲子列表。

現在我們定義佔位表達轉發:

expr = Forward() 

現在我們可以使用這個佔位符(我們不得不使用「< < =」爲賦值運算符,這樣EXPR保持爲構建表達式轉發,而不是反彈到表達本身)。這是我的第一關,使用您的BNF作爲-是:

expr <<= (EXCLAM | 
      token + SEMI + expr | 
      Group(LPAR + expr + HAT + expr + RPAR) | 
      Group(LPAR + expr + STAR + expr + RPAR)) 

這給了這些結果:

(ASD;!^FFF;!) 
^
Expected ";" (at char 2), (line:1, col:3) 

A;B;C;! 
['A', ';', 'B', ';', 'C', ';', '!'] 

(((A;!^B;!)^C;D;!)*E;!) 
[[[['A', ';', '!', '^', 'B', ';', '!'], '^', 'C', ';', 'D', ';', '!'], '*', 'E', ';', '!']] 

似乎有你的BNF一個不成文的規矩,即一個或多個令牌可以一起被本也容易固定爲:

expr <<= (EXCLAM | 
      OneOrMore(token) + SEMI + expr | 
      Group(LPAR + expr + HAT + expr + RPAR) | 
      Group(LPAR + expr + STAR + expr + RPAR)) 

現在給出:

(ASD;!^FFF;!) 
[['A', 'S', 'D', ';', '!', '^', 'F', 'F', 'F', ';', '!']] 

A;B;C;! 
['A', ';', 'B', ';', 'C', ';', '!'] 

(((A;!^B;!)^C;D;!)*E;!) 
[[[['A', ';', '!', '^', 'B', ';', '!'], '^', 'C', ';', 'D', ';', '!'], '*', 'E', ';', '!']] 

但是看起來我們可以從其他分組中受益,因此二進制「^」和「*」運算符的操作數更加清晰。所以我看中了:

expr <<= (EXCLAM | 
      Group(OneOrMore(token) + SEMI + ungroup(expr)) | 
      Group(LPAR + expr + HAT + expr + RPAR) | 
      Group(LPAR + expr + STAR + expr + RPAR)) 

而且我覺得這個版本輸出的現在可以更容易地處理:

(ASD;!^FFF;!) 
[[['A', 'S', 'D', ';', '!'], '^', ['F', 'F', 'F', ';', '!']]] 

A;B;C;! 
[['A', ';', 'B', ';', 'C', ';', '!']] 

(((A;!^B;!)^C;D;!)*E;!) 
[[[[['A', ';', '!'], '^', ['B', ';', '!']], '^', ['C', ';', 'D', ';', '!']], '*', ['E', ';', '!']]] 

下面是完整的腳本:

from pyparsing import * 

EXCLAM,SEMI,HAT,STAR = map(Literal,"!;^*") 
LPAR,RPAR = map(Suppress,"()") 
token = oneOf(list(alphas.upper())) 
expr = Forward() 
expr <<= (EXCLAM | 
      Group(OneOrMore(token) + SEMI + ungroup(expr)) | 
      Group(LPAR + expr + HAT + expr + RPAR) | 
      Group(LPAR + expr + STAR + expr + RPAR)) 

tests = """\ 
(ASD;!^FFF;!) 
A;B;C;! 
(((A;!^B;!)^C;D;!)*E;!)""".splitlines() 

for t in tests: 
    print t 
    try: 
     print expr.parseString(t).dump() 
    except ParseException as pe: 
     print ' '*pe.loc + '^' 
     print pe 
    print 

最後需要注意的:我認爲「AAA」是連續3個「A」代幣。如果你的意思是代幣是1個或多個alpha的詞組,那麼把表達式中的'OneOrMore(token)'改爲'Word(alphas.upper())' - 然後你會得到這個結果作爲你的第一個測試用例:

[[['ASD', ';', '!'], '^', ['FFF', ';', '!']]] 
+0

哇,你做到了! 只是,還有一個問題... 我想更新像*和^這樣的二元運算符來作爲前綴符號,比如Lisp。在文檔中有一個使用字符串的例子,但不知道如何將其轉換爲數組。 喜歡的東西: ' 集團(LPAR + EXPR +帽子+ EXPR + RPAR)到 集團(LPAR + EXPR + EXPR +帽子+ RPAR) ' – ccamacho

+0

'EXPR EXPR HAT'聽起來像*後綴*符號給我。這種方式會不會像你一樣工作?如果您希望STAR和HAT具有相同的優先級,請嘗試'Group(LPAR + expr + expr +(STAR | HAT)+ RPAR)'。 – PaulMcG

1

這使得Lisp符號工作xD!

from pyparsing import * 

def pushFirst(strg, loc, toks): 
    toks[0][2], toks[0][1] = toks[0][1], toks[0][2] 

def parseTerm(term): 
    """ 
    EBNF syntax elements 
    EXCLAM = ! 
    HAT =^
    STAR = * 
    SEMI = ; 
    LPAR = (
    RPAR = ) 
    """ 

    EXCLAM,HAT,STAR = map(Literal,"!^*") 
    LPAR,RPAR = map(Suppress,"()") 
    SEMI = Suppress(";") 

    token = oneOf(list(alphas.upper())) 
    expr = Forward() 
    expr <<= (
        EXCLAM | 
        Group(Word(alphas.upper()) + SEMI + ungroup(expr)) | 
        Group(LPAR + expr + HAT + expr + RPAR).setParseAction(pushFirst) | 
        Group(LPAR + expr + STAR + expr + RPAR).setParseAction(pushFirst) 
       ) 
    try: 
     result = expr.parseString(term) 
    except ParseException as pe: 
     print ' '*pe.loc + '^' 
     print pe 
    return result[0] 



def computeTerm(term): 
    print term 


term = (parseTerm("(((AXX;!^B;!)^C;D;!)*E;!)")) 

computeTerm(term) 
+1

好吧,這是蠻力,但看起來你已經超越了pyparsingness的第二級,使用解析操作。祝賀你,並樂於與pyparsing! – PaulMcG