如何在python中生成DEDENT令牌?

时间:2016-12-04 15:19:55

标签: python python-3.x token

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

  

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

     

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

我试图理解DEDENT部分,但未能,有人能提供比引用更好的解释吗?

2 个答案:

答案 0 :(得分: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之前的数量的位置。

答案 1 :(得分:2)

由于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