节点深度编码为星数

时间:2019-07-11 03:03:08

标签: lark-parser

这种语言的文档看起来像

* A top-level Headline 

  Some text about that headline.

** Sub-Topic 1

Text about the sub-topic 1.

*** Sub-sub-topic

 More text here about the sub-sub-topic

** Sub-Topic 2

   Extra text here about sub-topic 2

*** Other Sub-sub-topic

 More text here about the other sub-sub-topic

深度级别的数量是无限的。我想知道如何获取解析器,以适当地构建嵌套树。我一直在寻找indenter example的灵感,但我没有弄清楚。

1 个答案:

答案 0 :(得分:1)

该问题需要上下文相关的语法,因此我们使用您链接的压头示例中的变通方法:

我们编写了一个自定义postlex处理器,该处理器保留一堆观察到的缩进级别。读取星号(******,...)时,将弹出堆栈,直到堆栈上的缩进级别较小,然后将新级别压入在堆栈上。对于每次推送/弹出,都将相应的INDENT / DEDENT帮助者令牌注入令牌流中。然后可以在语法中使用这些辅助标记,以获得反映嵌套级别的解析树。

from lark import Lark, Token

tree_grammar = r"""
    start: NEWLINE* item*
    item: STARS nest
    nest: _INDENT (nest | LINE+ item*) _DEDENT
    STARS.2: /\*+/
    LINE.1: /.*/ NEWLINE
    %declare _INDENT _DEDENT
    %import common.NEWLINE
"""

class StarIndenter():
  STARS_type = 'STARS'
  INDENT_type = '_INDENT'
  DEDENT_type = '_DEDENT'

  def dedent(self, level, token):
    """ When the given level leaves the current nesting of the stack,
        inject corresponding number of DEDENT tokens into the stream.
    """
    while level <= self.indent[-1]:
      pop_level = self.indent.pop()
      pop_diff = pop_level - self.indent[-1]
      for _ in range(pop_diff):
        yield token

  def handle_stars(self, token):
    """ Handle tokens of the form '*', '**', '***', ...
    """

    level = len(token.value)

    dedent_token = Token.new_borrow_pos(self.DEDENT_type, '', token)
    yield from self.dedent(level, dedent_token)

    diff = level-self.indent[-1]
    self.indent.append(level)

    # Put star token into stream
    yield token

    indent_token = Token.new_borrow_pos(self.INDENT_type, '', token)
    for _ in range(diff):
      yield indent_token

  def process(self, stream):
    self.indent = [0]

    # Process token stream
    for token in stream:
      if token.type == self.STARS_type:
        yield from self.handle_stars(token)
      else:
        yield token

    # Inject closing dedent tokens
    yield from self.dedent(1, Token(self.DEDENT_type, ''))

  # No idea why this is needed
  @property
  def always_accept(self):
    return ()

parser = Lark(tree_grammar, parser='lalr', postlex=StarIndenter())

请注意,STARS终端被分配了比LINES更高的优先级(通过.2.1),以防止LINES+占用开始的线路带有星星。

使用示例的精简版本:

test_tree = """
* A
** AA
*** AAA
** AB
*** ABA
"""

print(parser.parse(test_tree).pretty())

结果:

start


  item
    *
    nest
       A

      item
        **
        nest
           AA

          item
            ***
            nest     AAA

      item
        **
        nest
           AB

          item
            ***
            nest     ABA