【问题标题】:How exactly a DEDENT token is generated in python?在 python 中究竟是如何生成 DEDENT 令牌的?
【发布时间】:2016-12-04 15:19:55
【问题描述】:

我正在阅读有关 lexical analysis of python 的文档,其中描述了如何生成 INDENT 和 DEDENT 令牌的课程。我在此处发布了描述。

连续行的缩进级别用于生成 INDENT 和 DEDENT 标记,使用堆栈,如下所示。

在读取文件的第一行之前,将单个零压入堆栈;这将永远不会再次弹出。压入堆栈的数字将始终严格从下到上递增。在每个逻辑行的开头,将行的缩进级别与堆栈顶部进行比较。如果相等,则什么都不会发生。如果较大,则将其压入堆栈,并生成一个 INDENT 令牌。如果它更小,它必须是堆栈上出现的数字之一;堆栈上所有较大的数字都被弹出,并且为每个弹出的数字生成一个 DEDENT 令牌。在文件的末尾,为堆栈中剩余的大于零的每个数字生成一个 DEDENT 令牌。

我试图理解 DEDENT 部分,但没有成功,有人能给出比所引用的更好的解释吗?

【问题讨论】:

    标签: python python-3.x token


    【解决方案1】:

    由于 Python 有时比英语更容易,因此这里是这个描述对 Python 的粗略翻译。您可以看到像 here 这样工作的真实世界解析器(由我自己编写)。

    import re
    code = """
    for i in range(10):
       if i % 2 == 0:
         print(i)
       print("Next number")
    print("That's all")
    
    for i in range(10):
       if i % 2 == 0:
           print(i)
    print("That's all again)
    
    for i in range(10):
       if i % 2 == 0:
          print(i)
      print("That's all")
    """
    def get_indent(s) -> int:
        m = re.match(r' *', s)
        return len(m.group(0))
    def add_token(token):
        print(token)
    INDENT="indent"
    DEDENT="dedent"
    indent_stack = [0]
    # Before the first line of the file is read, a single zero is pushed on the stack
    for line in code.splitlines():
        print("processing line:", line)
        indent = get_indent(line)
        # At the beginning of each logical line, the line’s 
        # indentation level is compared to the top of the stack. 
        if indent > indent_stack[-1]:
            # If it is larger, it is pushed on the stack, 
            # and one INDENT token is generated.
            add_token(INDENT)
            indent_stack.append(indent)
        elif indent < indent_stack[-1]:
            while indent < indent_stack[-1]:
                #  If it is smaller, ...
                # all numbers on the stack that are larger are popped off,
                # and for each number popped off a DEDENT token is generated.
                add_token(DEDENT)
                indent_stack.pop()
            if indent != indent_stack[-1]:
                # it must be one of the numbers occurring on the stack; 
                raise IndentationError
    while indent_stack[-1]>0:
         # At the end of the file, a DEDENT token is generated for each number 
         # remaining on the stack that is larger than zero.
         add_token(DEDENT)
         indent_stack.pop()
    

    这是输出:

    processing line: 
    processing line: for i in range(10):
    processing line:    if i % 2 == 0:
    indent
    processing line:      print(i)
    indent
    processing line:    print("Next number")
    dedent
    processing line: print("That's all")
    dedent
    processing line: 
    processing line: for i in range(10):
    processing line:    if i % 2 == 0:
    indent
    processing line:        print(i)
    indent
    processing line: print("That's all again)
    dedent
    dedent
    processing line: 
    processing line: for i in range(10):
    processing line:    if i % 2 == 0:
    indent
    processing line:       print(i)
    indent
    processing line:   print("That's all")
    dedent
    dedent
      File "<string>", line unknown
    IndentationError
    

    【讨论】:

      【解决方案2】:

      假设我们有一个每个缩进级别使用 4 个空格的源文件,并且我们目前处于第三级缩进。缩进堆栈的内容将是[0, 4, 8, 12] - 初始零,加上第一次遇到的每个新缩进级别。现在,考虑下一行代码中前导空格的数量...

      • 如果是 12(匹配当前栈顶),则没有缩进变化,没有什么特别的事情发生。
      • 如果它大于 12,则会生成一个 INDENT 标记,并将新值添加到堆栈中。
      • 如果是 8,则生成一个 DEDENT 令牌,然后将 12 从堆栈中弹出。
      • 如果是 4,您会得到两个 DEDENT,并且 12 和 8 都会弹出。
      • 如果为 0,或者源文件在此时结束,则会得到三个 DEDENT,然后弹出 12、8、4。
      • 如果它小于 12,则会生成“不一致缩进”错误,因为无法判断您已缩进到哪个先前级别的代码。

      请注意,仅考虑包含实际代码的行 - 如果一行仅包含空格或注释,则其前导空格的数量无关紧要。

      这个过程的重点是,恰好生成一个 DEDENT 以对应于每个 INDENT,发生在缩进级别返回(或低于)相应 INDENT 之前存在的数量的点。

      【讨论】:

        猜你喜欢
        • 2010-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-22
        • 1970-01-01
        • 1970-01-01
        • 2018-04-03
        相关资源
        最近更新 更多