将块解析为 Python
Parsing blocks as Python
我正在用 JFlex + CUP 编写一个词法分析器 + 解析器,我希望有关于块的 Python 类语法;也就是说,缩进标记块级别。
我不确定如何解决这个问题,以及是否应该在词法或语法级别上完成。
我目前的方法是在词法层面解决问题 - 换行符被解析为指令分隔符,当处理一个时,我将词法分析器移动到一个特殊状态,检查新行前面有多少字符并记住最后一行从哪一列开始,并相应地引入和打开块或关闭块字符。
然而,我 运行 遇到了各种各样的麻烦。例如:
- JFlex 无法匹配空字符串,所以我的指令需要在每个换行符后至少有一个空格。
- 我不能用这种方法同时关闭两个块。
我的做法对吗?我应该做些不同的事情吗?
您在词法分析器而不是解析器中处理缩进的方法是正确的。好吧,这两种方法都是可行的,但这通常是更简单的方法,而且 Python 本身(或至少 CPython 和 PyPy)就是这样做的。
我对 JFlex 了解不多,您也没有给我们任何代码,但我可以笼统地解释一下。
对于您的第一个问题,您已经在换行符之后将词法分析器置于特殊状态,因此 "grab 0 or more spaces" 应该可以通过逃避正常的流程来实现 运行对行使用正则表达式。
对于第二个问题,最简单的解决方案(Python 使用的解决方案)是保留一堆缩进。我将演示一些比 Python 更简单的东西。
第一个:
indents = [0]
在每个换行符之后,抓取一个 运行 的 0 个或更多的空格作为 spaces
。那么:
if len(spaces) == indents[-1]:
pass
elif len(spaces) > indents[-1]:
indents.append(len(spaces))
emit(INDENT_TOKEN)
else:
while len(spaces) != indents[-1]:
indents.pop()
emit(DEDENT_TOKEN)
现在您的解析器只看到 INDENT_TOKEN
和 DEDENT_TOKEN
,它们与类 C 语言中的 OPEN_BRACE_TOKEN
和 CLOSE_BRACE_TOKEN
没有区别。
如果你想要更好的错误处理——引发某种标记器错误而不是隐式 IndexError
,也许使用 <
而不是 !=
这样你就可以检测到你已经走得太远而不是耗尽堆栈(如果你想继续发出更多错误而不是在第一个错误中退出,为了更好的错误恢复),等等。
对于现实生活中的示例代码(错误处理、制表符和空格、反斜杠换行符转义以及处理括号表达式内的非句法缩进等),请参阅 tokenize
stdlib 中的文档和源代码。
我正在用 JFlex + CUP 编写一个词法分析器 + 解析器,我希望有关于块的 Python 类语法;也就是说,缩进标记块级别。
我不确定如何解决这个问题,以及是否应该在词法或语法级别上完成。
我目前的方法是在词法层面解决问题 - 换行符被解析为指令分隔符,当处理一个时,我将词法分析器移动到一个特殊状态,检查新行前面有多少字符并记住最后一行从哪一列开始,并相应地引入和打开块或关闭块字符。
然而,我 运行 遇到了各种各样的麻烦。例如:
- JFlex 无法匹配空字符串,所以我的指令需要在每个换行符后至少有一个空格。
- 我不能用这种方法同时关闭两个块。
我的做法对吗?我应该做些不同的事情吗?
您在词法分析器而不是解析器中处理缩进的方法是正确的。好吧,这两种方法都是可行的,但这通常是更简单的方法,而且 Python 本身(或至少 CPython 和 PyPy)就是这样做的。
我对 JFlex 了解不多,您也没有给我们任何代码,但我可以笼统地解释一下。
对于您的第一个问题,您已经在换行符之后将词法分析器置于特殊状态,因此 "grab 0 or more spaces" 应该可以通过逃避正常的流程来实现 运行对行使用正则表达式。
对于第二个问题,最简单的解决方案(Python 使用的解决方案)是保留一堆缩进。我将演示一些比 Python 更简单的东西。
第一个:
indents = [0]
在每个换行符之后,抓取一个 运行 的 0 个或更多的空格作为 spaces
。那么:
if len(spaces) == indents[-1]:
pass
elif len(spaces) > indents[-1]:
indents.append(len(spaces))
emit(INDENT_TOKEN)
else:
while len(spaces) != indents[-1]:
indents.pop()
emit(DEDENT_TOKEN)
现在您的解析器只看到 INDENT_TOKEN
和 DEDENT_TOKEN
,它们与类 C 语言中的 OPEN_BRACE_TOKEN
和 CLOSE_BRACE_TOKEN
没有区别。
如果你想要更好的错误处理——引发某种标记器错误而不是隐式 IndexError
,也许使用 <
而不是 !=
这样你就可以检测到你已经走得太远而不是耗尽堆栈(如果你想继续发出更多错误而不是在第一个错误中退出,为了更好的错误恢复),等等。
对于现实生活中的示例代码(错误处理、制表符和空格、反斜杠换行符转义以及处理括号表达式内的非句法缩进等),请参阅 tokenize
stdlib 中的文档和源代码。