1

如何使用Lark正确匹配子字符串?

我的意图(可能/不建议使用 Lark 或任何 CFG)是仅匹配和解析字符串的重要部分,而忽略其余部分。例如:

  • 从“约翰”,我想将“约翰”解析为one_person.
  • 从“昨天约翰睡觉”开始,我想将“约翰”解析为one_person并忽略其余部分。
  • 从“约翰和玛丽”开始,我想将“约翰和玛丽”解析为two_people.
  • 从“昨天约翰和玛丽接吻。”,我想将“约翰和玛丽”解析为two_people并忽略其余部分。

这是我的代码:

from lark import Lark, tree

grammar = """
    input: not_important* important not_important*

    important: one_person
        | two_people

    one_person: PERSON
    two_people: one_person conj one_person
    not_important: RANDOM_WORD

    conj: CONJ

    PERSON: "John" | "Mary"
    CONJ: "and" | "or"
    RANDOM_WORD: /\\w+/

    %import common.WS
    %ignore WS
"""

if __name__ == '__main__':
    parser = Lark(grammar, start='input', ambiguity='explicit')
    tree = parser.parse('Yesterday John and Mary kissed')
    print(tree.pretty())

有用:

  • 当重要部分周围没有任何内容时,例如“John”或“John and Mary”。
  • 或者当重要部分只有一侧有不重要的东西时,例如“约翰睡了”或“约翰和玛丽亲吻了”。

但是当不重要的东西围绕着重要的东西时,它就不起作用了,例如“昨天约翰和玛丽接吻了”。在这个例子中,我希望得到:

input
    not_important   Yesterday
    important
      two_people
        one_person  John
        conj    and
        one_person  Mary
    not_important   kissed

但我得到:

_ambig
  input
    not_important   Yesterday
    important
      one_person    John
    not_important   and
    not_important   Mary
    not_important   kissed
  input
    not_important   Yesterday
    important
      two_people
        one_person  John
        conj    and
        one_person  Mary
    not_important   kissed
    not_important   John
    not_important   and

也就是说,Lark 不仅将输入视为模棱两可,而且第二次解析也失败了,因为两个终端(“John”和“and”)被消耗了两次。

4

1 回答 1

1

解决此问题的一种方法是为某些规则设置更高的优先级:

from lark import Lark, tree

grammar = """
    input: not_important* important not_important* 

    important.2: one_person
        | two_people

    one_person: PERSON
    two_people.2: one_person conj one_person
    not_important: RANDOM_WORD

    conj.2: CONJ

    PERSON: "John" | "Mary"
    CONJ: "and" | "or"
    RANDOM_WORD: /\\w+/

    %import common.WS
    %ignore WS
"""

if __name__ == '__main__':
    parser = Lark(grammar, start='input')
    tree = parser.parse('Yesterday John and Mary kissed')
    print(tree.pretty())

注意.2添加到important,two_peopleconj,这是您在 lak 中设置优先级的方式。默认情况下,所有规则的优先级为 1。设置了优先级后,您可以删除ambiguity='explicit',因为 Lark 将能够正确处理歧义。

第二种方法是lalr用作解析器。这允许您在终端上设置优先级。然后我们可以将PERSONandCONJ的优先级设置为 2。这样可以使语法明确,这是使用的要求lalr


from lark import Lark, tree

grammar = """
    input: not_important* important not_important* 

    important: one_person
        | two_people

    one_person: PERSON
    two_people: one_person conj one_person
    not_important: RANDOM_WORD

    conj: CONJ

    PERSON.2: "John" | "Mary"
    CONJ.2: "and" | "or"
    RANDOM_WORD: /\\w+/

    %import common.WS
    %ignore WS
"""

if __name__ == '__main__':
    parser = Lark(grammar, start='input', parser="lalr")
    tree = parser.parse('Yesterday John and Mary kissed')
    print(tree.pretty())

两种方法都输出

input
  not_important Yesterday
  important
    two_people
      one_person        John
      conj      and
      one_person        Mary
  not_important kissed
于 2020-05-11T12:15:19.427 回答