在循环语法中自动构建AST

时间:2015-02-06 09:26:07

标签: python parsing python-3.x pyparsing

我正在为过程演算开发一个解析器。我正在使用this pattern自动创建带解析对象的AST树。

我的问题是,由于我的语法在结果树中的递归性质,我得到了解析文本中不存在的对象(AST节点)。

即。

A = a.b我得到了 [Procdef:{'rest': ([Choice:{'lhs': ([Prefix:{'lhs': (['a'], {}), 'rhs': ([Prefix:{'lhs': (['b'], {}), 'rhs': ([], {})}], {})}], {}), 'rhs': ([], {})}], {}), 'proc': 'A'}]

但我希望如此:

[Procdef:{'rest': ([Prefix:{'lhs': (['a'], {}), 'rhs': ([Prefix:{'lhs': (['b'], {}), 'rhs': ([], {})}], {})}], {}), 'proc': 'A'}]

这里的区别是缺少Choice节点。在我过去的项目中,我使用自己的AST结构和函数传递给setParseAction,然后我只检查rhs是否为null并传递令牌。对象通过后我不知道该怎么做。

简化代码如下:

#!/usr/bin/env python

from pyparsing import * 

class ASTNode(object):
    def __init__(self, tokens):
        self.tokens = tokens
        self.assignFields()
    def __str__(self):
        return self.__class__.__name__ + ":" + str(self.__dict__)
    __repr__ = __str__

class Procdef(ASTNode):
    def assignFields(self):
        self.proc, self.rest = self.tokens
        del self.tokens

class Choice(ASTNode):
    def assignFields(self):
        self.lhs, self.rhs = self.tokens
        del self.tokens

class Prefix(ASTNode):
    def assignFields(self):
        self.lhs, self.rhs = self.tokens
        del self.tokens

class RasParser(object):

    def grammar(self):
        prefix_op = Literal('.').suppress()
        choice_op = Literal('+').suppress()
        lpar = Literal('(').suppress()
        rpar = Literal(')').suppress()
        define = Literal('=').suppress()
        Ident = Word(alphas.upper(), alphanums + "_")
        ident = Word(alphas.lower(), alphanums + "_")
        choice = Forward()
        prefix = Forward()

        process = ident | lpar + choice + rpar
        prefix << Group(process) + Group(ZeroOrMore(prefix_op + prefix))
        choice << Group(prefix) + Group(ZeroOrMore(choice_op + prefix))
        rmdef = Ident + define + Group(choice)

        rmdef.setParseAction(Procdef)
        prefix.setParseAction(Prefix)
        choice.setParseAction(Choice)
        ras = ZeroOrMore(rmdef)
        return ras

    def parse(self, string):
        oo = self.grammar().parseString(string, parseAll=True).asList()
        print(oo)

if __name__ == "__main__":
    p = RasParser()
    model = "A = a.b" 
    print(model)
    p.parse(model)

1 个答案:

答案 0 :(得分:0)

实际上,表达式中的那些元素,它们只是简并版本(没有运算符的单个操作数)。递归定义意味着单个进程将被解析为简并前缀(没有运算符的前缀),然后被解析为简并选择(没有运算符的选择)。

您已经以正确关联的方式定义了前缀。如果你的意思是左联想,那么它应该是:prefix << Group(process) + Group(ZeroOrMore(prefix_op + process))

&#39;。&#39; s&#34;运营商&#34;这里?您是否正在尝试支持类似&#34; A = b。(c + d)&#34; ?如果没有,那么最好将基本操作数定义为Combine(ident + ZeroOrMore('.' + ident)),然后添加为operand + ZeroOrMore( '+' + operand)。或者使用操作数作为infixNotation中的基本元素(参见下文)。

如果这个表示法符合您的意图,请考虑使用infixNotation助手来定义此语法:

choice = infixNotation(process, 
    [
    ('.', 2, opAssoc.LEFT, Prefix),
    ('+', 2, opAssoc.LEFT, Choice),
    ]
rmdef = ident("destination") + define + choice("rhs")

infixNotation将处理任何()覆盖已定义的操作优先级。