使用ANTLR4实现简易解析器:匹配内容至结束标记与函数解析
问题与解决方案
核心问题
需要实现支持文本块(PLAIN_TEXT_BLOB)、脚本块(SCRIPT_BLOB)、函数声明(FUNCTION)的解析器,保持三者出现顺序。当前瓶颈是SCRIPT模式下无法正确匹配函数体到end function标记——贪心规则导致end被识别为普通标识符,无法触发FunctionEnd token;同时无法捕获任意类型的通用脚本语句。
解决方案
1. 调整Lexer规则(解决end function识别问题)
Antlr Lexer遵循先定义优先+最长匹配原则,当前Ident规则会优先捕获end和function作为单独标识符,导致FunctionEnd无法被识别。需调整SCRIPT模式下的token顺序,并补充通用脚本内容的捕获:
lexer grammar ExprLexer; channels { Comments, SkipChannel } SeaWhitespace: [ \t\r\n\f]+ -> channel(HIDDEN); HtmlDtd: '<!' .*? '>'; ScriptStart: '<%' -> channel(SkipChannel), pushMode(SCRIPT); GenericText: . ; // 捕获所有未匹配的文本 mode SCRIPT; ScriptEnd: '%>' -> channel(SkipChannel), popMode; ScriptWhitespace: [ \t\r\n\f]+ -> channel(SkipChannel); ScriptSingleLineComment: '\'' -> channel(SkipChannel), pushMode(SingleLineCommentMode); // 优先识别关键字,避免被Ident捕获 FunctionStart: 'function'; FunctionEnd: 'end function'; // 脚本符号 COMMA: ','; SEMICOLON: ';'; L_PAREN: '('; R_PAREN: ')'; ASSIGNTO: '='; // 最后定义Ident,确保关键字优先匹配 Ident: [a-zA-Z0-9_\u0080-\ufffe]+; // 捕获任意未匹配的脚本内容(用于通用语句) ScriptContent: ~[ \t\r\n\f%();,=]+; mode SingleLineCommentMode; Comment: ~[\r\n]+ -> channel(Comments); CommentEnd: [\r\n] -> channel(SkipChannel), popMode;
2. 重构Parser规则(实现三类结构的正确捕获)
重新设计规则,明确区分文本块、脚本块(含函数和通用语句),并确保函数体正确匹配到end function:
parser grammar ExprParser; options { tokenVocab=ExprLexer; } file : contentNode* EOF ; // 统一节点类型,严格保留原顺序 contentNode : plainTextBlob | scriptBlob | functionDeclaration ; // 文本块:连续的非脚本文本内容 plainTextBlob : (HtmlDtd | GenericText)+ ; // 脚本块:除函数声明外的任意脚本内容 scriptBlob : scriptStatement+ ; // 通用脚本语句:匹配任意脚本元素,无需区分类型 scriptStatement : (Ident | ScriptContent | COMMA | SEMICOLON | L_PAREN | R_PAREN | ASSIGNTO)+ ; // 函数声明:匹配完整函数定义到end function,支持可选参数和嵌套函数 functionDeclaration : FunctionStart Ident L_PAREN (Ident (COMMA Ident)*)? R_PAREN (scriptStatement | functionDeclaration)* FunctionEnd ;
关键说明
- Lexer优先级调整:将
FunctionStart、FunctionEnd等关键字放在Ident之前,确保这些完整标记被优先识别,避免被拆分为普通标识符。 - 通用脚本内容捕获:新增
ScriptContent和scriptStatement规则,直接捕获所有未被关键字/符号匹配的脚本内容,无需区分语句类型。 - 结构顺序保留:通过
contentNode统一管理三类结构,解析结果严格遵循原文件中的出现顺序。 - 嵌套支持(可选):函数体规则允许嵌套函数,若不需要可移除
functionDeclaration选项。
内容的提问来源于stack exchange,提问作者Nicholas
相关产品推荐
相关产品推荐

