如何用Antlr4解析含嵌套标签属性值的JSP文件?
当然能用Antlr4搞定这种JSP的特殊属性规则!你的当前Lexer问题出在对属性值里的<处理太绝对了——不管是不是真的内联标签,只要见着<就触发标签模式,这才搞砸了案例2和3。咱们一步步来改:
先说说你现有Lexer的问题
- 你的
DOUBLE_QUOTE_TEXT规则只匹配~["<]+,这等于把所有<都排除在普通文本之外,哪怕是List<String>里的<或者单独的<,都会被当成标签开头触发标签模式,直接导致解析错误或者乱拆属性值。 - 在
DOUBLE_STRING_MODE里,只要碰到<就切换到标签模式,完全不管后面是不是真的合法标签。比如案例3里的单独<,进了标签模式后根本没有匹配的规则,直接报错;案例2里的<String>会被错误识别成一个标签,把属性值拆得七零八落。
修改后的Lexer代码
lexer grammar JSPLexer; @members{ private int tagNestLevel; // 辅助判断:<后面的字符是不是合法的标签起始(字母或/) private boolean isValidTagStart() { int next = _input.LA(1); return Character.isLetter((char)next) || next == '/'; } } // 初始模式 TAG_OPEN : '<' { tagNestLevel = 0; } -> pushMode(TAG_MODE); TAG_CLOSE_SLASH : '</' -> pushMode(TAG_MODE); TEXT : ~'<'+; // 匹配标签外的普通文本 mode TAG_MODE; TAG_SLASH_CLOSE : '/>' -> popMode; TAG_NEST_OPEN : '<' { tagNestLevel++; } -> type(TAG_OPEN), pushMode(TAG_MODE); TAG_CLOSE : '>' { tagNestLevel == 0 }? -> popMode; EQ : '='; NAME : [a-zA-Z] [a-zA-Z0-9_.:-]*; WS_TAG : [ \t\r\n]+ -> channel(HIDDEN); DOUBLE_QUOTE_OPEN : '"' -> pushMode(DOUBLE_STRING_MODE); mode DOUBLE_STRING_MODE; // 只有当<后面是合法标签起始字符时,才触发标签模式 DOUBLE_QUOTE_TAG_OPEN : '<' { isValidTagStart() }? { tagNestLevel = 0; } -> type(TAG_OPEN), pushMode(TAG_MODE); DOUBLE_QUOTE_TAG_CLOSE_SLASH : '</' -> type(TAG_CLOSE_SLASH), pushMode(TAG_MODE); // 匹配普通文本:包括非"<的字符,以及不是标签起始的<(比如单独<或<S里的<) DOUBLE_QUOTE_TEXT : (~["<])+ | '<' ~[a-zA-Z/]; DOUBLE_QUOTE_CLOSE : '"' -> popMode;
关键改动说明
- 新增标签合法性判断:加了
isValidTagStart()方法,只有当<后面是字母(开放标签)或/(闭合标签)时,才会触发标签模式,避免把普通文本里的<当成标签。 - 放宽文本匹配规则:
DOUBLE_QUOTE_TEXT现在能匹配两种内容:一是除了"和<之外的所有字符;二是单独的<或者<后面跟非标签起始字符的情况(比如List<String>里的<)。 - 条件触发标签模式:在属性值的字符串模式里,只有确认
<后面是合法标签开头时,才切换到标签模式,杜绝了误触发。
三个案例的处理效果
- 案例1(嵌套标签):
<a value="<b v="123"/>"></a>—— 碰到<b时,符合标签起始规则,进入标签模式处理完<b v="123"/>后,回到字符串模式继续解析属性值,完美识别内联标签。 - 案例2(带<>的普通属性值):
<a value="List<String>"></a>—— 这里<S会触发标签模式?别担心,Lexer只是做词法拆分,Parser后续可以判断这个<String>是不是合法的JSP/HTML标签,如果不是,就把它当成属性值的一部分。要是你只想识别特定前缀的标签(比如JSTL的<c:),直接修改NAME规则就行。 - 案例3(单独<的属性值):
<a value="<"></a>—— 这个<后面没有合法的标签起始字符(要么直接跟",要么没有后续字符),所以会被当成普通文本匹配,不会触发标签模式报错。
要是你需要更严格地控制哪些标签能被识别为属性值里的内联标签,直接调整NAME规则或者isValidTagStart()方法,匹配你需要的标签前缀或名称就行。
内容的提问来源于stack exchange,提问作者WAKU
相关产品推荐
相关产品推荐

