如何在ANTLR4中实现嵌套表达式(海岛)与普通文本的正确分离
修复ANTLR4类StringSubstitutor语言的语法解析问题
问题背景
我基于ANTLR4实现一款类StringSubstitutor的简单语言语法(因使用C#无法直接用原Java库),支持在普通文本中做变量替换,示例输入:
text ${aaa}${/asd/${yyy}} goes on ${fff} and on
语言支持${id}、${/some/path}格式,且路径允许嵌套表达式。
当前已实现无外围普通文本时的解析,但添加普通文本识别后,TEXT规则会在IN_LOCAL_EXPR模式下错误匹配ID,导致本该解析为localVariable或path的内容被识别为text,出现重复解析问题。需要修复语法,同时支持嵌套表达式和外围普通文本识别。
当前有问题的代码
Lexer代码
lexer grammar LocalExpressionLexer; BEGIN : '${'; END : '}'; SPACE : [ \t\r\n]-> skip; ID : [0-9a-zA-Z_]+; PATH_SEPARATOR : '/'; LOCAL_EXPRESSION : BEGIN -> more, pushMode(IN_LOCAL_EXPR) ; TEXT : ~[$]+ ; mode IN_LOCAL_EXPR; LOCAL_EXPR_END : END -> more, popMode;
Parser代码
parser grammar LocalExpressionParser; options { tokenVocab=LocalExpressionLexer; } parse : (atom | text)+ EOF ; atom : BEGIN (localVariable | path) END ; localVariable : ID; path : (PATH_SEPARATOR (atom | ID))+ ; text : TEXT ;
修复方案
修复后的Lexer代码
lexer grammar LocalExpressionLexer; // 全局模式规则 BEGIN : '${' -> pushMode(IN_LOCAL_EXPR); END : '}'; SPACE : [ \t\r\n] -> skip; // 匹配普通文本:除$外的字符,或单独的$(非${开头) TEXT : (~[$] | '$' ~'{')+; mode IN_LOCAL_EXPR; // 表达式内的结束符,退出当前模式 IN_END : '}' -> popMode; // 表达式内的标识符 IN_ID : [0-9a-zA-Z_]+; // 表达式内的路径分隔符 IN_PATH_SEPARATOR : '/'; // 表达式内嵌套的开始符,推入新的表达式模式 IN_BEGIN : '${' -> pushMode(IN_LOCAL_EXPR); // 跳过表达式内的空白(如需保留可删除此规则) IN_SPACE : [ \t\r\n] -> skip;
修复后的Parser代码
parser grammar LocalExpressionParser; options { tokenVocab=LocalExpressionLexer; } parse : (atom | text)+ EOF ; text : TEXT ; atom : BEGIN (localVariable | path) END ; localVariable : IN_ID; path : (IN_PATH_SEPARATOR (atom | IN_ID))+ ;
修复说明
- 核心问题解决:原Lexer在IN_LOCAL_EXPR模式下未定义ID、路径分隔符等规则,导致Lexer回退到全局规则匹配TEXT,错误识别表达式内的内容。现在在IN_LOCAL_EXPR模式下单独定义对应规则,确保表达式内的标识符、路径分隔符和嵌套表达式被正确识别。
- TEXT规则优化:将全局TEXT规则改为
(~[$] | '$' ~'{')+,既匹配非$的普通文本,也兼容单独的$字符(避免误判为表达式开头)。 - 移除冗余配置:删除原
LOCAL_EXPR规则的more命令,该命令会追加令牌内容,不符合当前解析需求;同时全局模式下不再需要ID和PATH_SEPARATOR规则,所有相关令牌都在表达式模式内定义。 - Parser规则适配:将Parser中引用的ID、PATH_SEPARATOR改为IN_LOCAL_EXPR模式下的IN_ID、IN_PATH_SEPARATOR,确保令牌匹配正确。
验证效果
修复后的语法可正确解析示例输入,识别出所有普通文本段、变量表达式和嵌套路径表达式,无重复解析或错误匹配问题。
内容的提问来源于stack exchange,提问作者Marcin Tarsier
相关产品推荐
相关产品推荐

