2016-04-17 49 views
3

我想寫一個老虎分析器。我最初使用PyPEG,但由於一些困難,我去了琶音。PEG遞歸語法

我的語法很簡單。

def number(): return _(r'[0-9]+') 
def string(): return _(r"\".*?\"") 
def id(): return _(r'[a-zA-Z][a-zA-Z0-9_]*') 

def literal(): return [number, string] 

def simple_var(): return id 

def let_in_exp(): return 'let', 'in', Optional(ZeroOrMore(exp)), 'end' 

param = [number, string] 
params = Optional(param, ZeroOrMore(',', param)) 

def function_call(): return id, '(', params, ')' 

exp = [let_in_exp, simple_var, literal, function_call] 

def code(): return OneOrMore(exp), EOF 

該困難存在於let-in-exp表達式中。 let in let in let in end end end是有效的虎。

但是 - 現在 - 琶音不承認let-in-exp原樣,但三simple-var。事實上,進入ZeroOrMore(exp)時,它會消耗end,因此不會爲let-in-exp找到它。

如何解決此類問題?

+0

我認爲問題是,「讓」,「中」,和「結束」也匹配作爲'id'。我不確定PyPEG如何讓你定義一個負面預測,但是如果你可以在匹配正則表達式之前擴展'id'到* not *匹配關鍵字,那麼我認爲你的遞歸會起作用。 – PaulMcG

+0

我在其他項目中搜索了lookaheads,最後發現'Not()'和'And()'。這解決了它。非常感謝! 無論如何,是不是有更習慣寫PEG語法的方法? – kino

回答

3

不是琶音解決方案,但也許更符合你的口味慣用?

from pyparsing import (CaselessKeyword,Word,nums,QuotedString,alphas,alphanums, 
    Forward,Group,Optional,OneOrMore,ZeroOrMore,delimitedList) 

LET,IN,END = map(CaselessKeyword, "let in end".split()) 

number = Word(nums).setName("number") 
string = QuotedString('"') 
ident = ~(LET | IN | END) + Word(alphas, alphanums+'_') 
ident.setName("ident") 

literal = number | string 

simple_var = ident 

exp = Forward().setName("exp") 
let_in_exp = Group(LET + IN + ZeroOrMore(exp) + END).setName("let_in_exp") 

param = number | string 
params = delimitedList(param) 
function_call = ident() + '(' + Optional(params) + ')' 

exp <<= let_in_exp | simple_var | literal | function_call 

code = OneOrMore(exp) 

tests = """\ 
    let in let in let in end end end 
    let in let in let in "blah" end end end 
    let in let in let in "blah" end 1729 end end 
    """ 
code.runTests(tests) 

我設計pyparsing允許使用算術運算符表達式的組合:

  • + - >和
  • | - >符合
  • ^ - >或(嘗試所有,匹配最長)
  • ~ - >不
  • & - >每個(同和,但在任何順序)
  • * - >多個(如expr*3而不是expr+expr+expr

我相信這些運營商和通俗易懂的語言類的名稱,如OneOrMore使這些解析器更易於理解,並隨時間保持。

+0

非常感謝! 通過更習慣,我正在考慮與[post](http://stackoverflow.com/questions/13374121/non-left-recursive-peg-grammar-for-an-expression)有關的東西。 – kino

2

正如Paul已經指出的那樣,您應該使用Not句法謂詞來避免通過simple_var規則匹配關鍵字。此外,我建議不要將ZeroOrMore解析爲Optional中的表達式,因爲它已經暗示了。

解決方案琶音

from arpeggio import Not, OneOrMore, ZeroOrMore, Optional, EOF, ParserPython 
from arpeggio import RegExMatch as _ 

keyword = ['let', 'in', 'end'] 
def number(): return _(r'[0-9]+') 
def string(): return _(r"\".*?\"") 
def id(): return _(r'[a-zA-Z][a-zA-Z0-9_]*') 

def literal(): return [number, string] 
def simple_var(): return Not(keyword), id 
def let_in_exp(): return 'let', 'in', ZeroOrMore(exp), 'end' 

param = [number, string] 
params = Optional(param, ZeroOrMore(',', param)) 

def function_call(): return id, '(', params, ')' 

exp = [let_in_exp, simple_var, literal, function_call] 

def code(): return OneOrMore(exp), EOF 

parser = ParserPython(code, debug=True) 
test = 'let in 42 let in "foo" let in end end end' 
parse_tree = parser.parse(test) 

這將產生以下解析樹

enter image description here

+0

這就是我最終使用的。 我建議您在入門頁面上添加一些關於lookahead的內容。 謝謝! – kino