如何使用ANTLR4编写处理Python缩进的词法分析器?
处理缩进/取消缩进的Lexer实现方案
要解析类似示例的代码结构,核心是识别**缩进(INDENT)和取消缩进(DEDENT)**令牌,示例代码如下:
print(x) for x in x: print(x) print(x)
可行实现思路(避开你试过的无效方案)
核心逻辑:维护缩进栈
Lexer需要逐行跟踪当前行的缩进级别,和栈顶的缩进值对比:- 当前缩进 > 栈顶:生成INDENT令牌,把当前缩进压入栈
- 当前缩进 < 栈顶:循环弹出栈顶,每次生成一个DEDENT令牌,直到栈顶≤当前缩进;如果最终栈顶和当前缩进不相等,直接抛出缩进不匹配的语法错误
- 当前缩进 = 栈顶:不生成任何令牌
具体实现要点
- 空行直接忽略,但要保留换行符来判断行结构
- 计算缩进时,把制表符(
\t)统一转成固定数量的空格(比如4个),避免混合缩进出问题 - Lexer初始化时,先把0压入缩进栈,作为最外层的基准级别
- 第一行不能有缩进,否则直接报错
针对你之前失效方案的调整建议
你试过的两种方案没效果,大概率是这两个原因:- 第一种方案的规则没处理换行后的缩进对比逻辑,或者缩进栈的维护逻辑有问题
- 第二种Java实现的Python Lexer基类,你可能没正确继承或重写关键方法(比如
nextToken()里没调用缩进处理的核心逻辑)
调整方向:
- 别直接套用现成的Lexer文件,参考核心思路自己写缩进栈逻辑
- 对每一行开头,先算出转换后的有效缩进长度,再和栈顶值对比生成对应令牌
核心逻辑伪代码示例
// 初始化缩进栈,先压入0作为基准 private Deque<Integer> indentStack = new ArrayDeque<>(); private int currentIndent = 0; @Override public Token nextToken() { Token token = super.nextToken(); // 只在遇到换行符后处理缩进 if (token.getType() == NEWLINE) { // 计算当前行的有效缩进长度(把制表符转成空格) currentIndent = calculateCurrentIndent(); Integer top = indentStack.peek(); if (currentIndent > top) { indentStack.push(currentIndent); return createToken(INDENT, ""); } else if (currentIndent < top) { indentStack.pop(); return createToken(DEDENT, ""); } // 缩进相等时,直接返回下一个令牌 } return token; } private int calculateCurrentIndent() { // 读取行首的空格/制表符,转换成统一的空格数 int indent = 0; while (input.LA(1) == ' ' || input.LA(1) == '\t') { if (input.LA(1) == '\t') { indent += 4; // 这里设定1个制表符等价于4个空格 } else { indent += 1; } input.consume(); } return indent; }
注意:实际实现时要处理连续取消缩进的情况(比如多级缩进后连续退格),还要确保文件末尾时,把栈里所有非0的缩进值都弹出,生成对应的DEDENT令牌。
内容的提问来源于stack exchange,提问作者Mohee99s
相关产品推荐
相关产品推荐

