【问题标题】:PEG recursive grammarPEG递归文法
【发布时间】:2016-08-09 13:00:52
【问题描述】:

我正在尝试编写 Tiger 解析器。我最初使用的是 PyPEG,但由于一些困难,我选择了 Arpeggio。

我的语法真的很简单。

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 是有效的 Tiger。

但是 - 目前 - 琶音不能按原样识别 let-in-exp,而是识别为三个 simple-var。实际上,进入 ZeroOrMore(exp) 时,它消耗了 end,因此找不到 let-in-exp

如何解决这样的问题?

【问题讨论】:

  • 我认为问题在于'let'、'in'和'end'也匹配为id。我不确定 PyPEG 如何让您定义否定的前瞻,但如果您可以扩展 id 以在匹配正则表达式之前匹配关键字,那么我认为您的递归将起作用。跨度>
  • 我在其他项目中搜索lookaheads,终于找到Not()And()。那解决了它。所以非常感谢!无论如何,没有更惯用的方式来编写 PEG 语法吗?

标签: python parsing grammar peg


【解决方案1】:

不是琶音解决方案,但可能更符合您的口味?

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 以允许使用算术运算符组合表达式:

  • + -> 和
  • | -> 先匹配
  • ^ -> 或者(尝试所有,匹配最长)
  • ~ -> 不是
  • &amp; -> 每个(与 And 相同,但顺序不限)
  • * -> 多个(如 expr*3 而不是 expr+expr+expr

我相信这些运算符和像OneOrMore 这样的简单语言类名使这些解析器更容易理解,并且随着时间的推移更容易维护。

【讨论】:

  • 非常感谢!更惯用的说法是,我正在考虑与此 post 相关的事情。
【解决方案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)

这将产生以下解析树

【讨论】:

  • 这就是我最终使用的。我建议您在入门页面上添加一些关于前瞻的内容。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多