下划线被识别为空格是否正常?词法分析器需设置选项吗?
这确实是个容易踩的坑!咱们一步步理清楚:
问题本质:这不正常
你明明定义了空格规则,但解析器把下划线当空格处理且不报错,这肯定不符合预期。问题根源出在词法分析器(Lexer)阶段——因为空白符的识别、跳过逻辑是Lexer负责的,语法分析器(Parser)只会处理Lexer输出的Token流,如果Lexer把下划线当成空白符跳过了,Parser根本看不到这个字符,自然不会报错。
必须在Lexer里调整配置
答案是肯定的,你需要修改Lexer的规则,明确区分「合法空白符」和「下划线」:
- 首先检查你的Lexer规则,是不是不小心把下划线包含在了空白符的匹配正则里(比如写成了
[\s_]+这种错误格式) - 如果下划线不是你的语法里的合法字符,要给Lexer添加规则:匹配下划线时直接抛出词法错误,而不是跳过
- 如果下划线是合法标识符的一部分(比如变量名里允许下划线),那也要单独定义标识符的规则,避免和空白混淆
附你的简化示例与修正思路
原简化语法(可能存在的问题)
grammar MyLang; program : statement+; statement : ID ';' ; ID : [a-zA-Z]+ ; WS : [ \t\n\r]+ -> skip; // 这里如果写成[\s_]+就会把下划线当空白跳过
本应被拒绝的错误示例
my_var; // 下划线被当成空白,解析成my var; 反而通过了
修正后的Lexer规则(参考Bart的解答)
grammar MyLang; program : statement+; statement : ID ';' ; ID : [a-zA-Z]+ ; WS : [ \t\n\r]+ -> skip; // 只匹配合法空白符 ILLEGAL_UNDERSCORE : '_' -> channel(HIDDEN), throw; // 捕获下划线并抛出错误
关于JUnit测试的补充
你后来发现的问题很关键:之前的JUnit测试只检查Parser的语法错误,但下划线被Lexer提前当成空白跳过了,根本到不了Parser阶段,所以测试没检测出问题。你需要调整测试逻辑:
- 不仅验证合法输入的解析结果,还要验证包含下划线的非法输入是否会抛出词法错误
- 可以通过监听Lexer的错误信息,或者直接捕获词法分析阶段的异常来断言
内容的提问来源于stack exchange,提问作者YaFred




