如何借助JavaCC处理注释,实现带上下文保留的SQL代码格式化?
基于JavaCC保留JSQLParser注释上下文实现SQL格式化的方案
JSQLParser默认将注释定义为SPECIAL_TOKEN,这类令牌会在解析前被从令牌流中移除,导致注释丢失所属的SQL语法上下文,无法精准实现格式化。要解决这个问题,核心是调整JavaCC的注释处理规则,让注释与对应的语法节点绑定,具体步骤如下:
1. 调整JavaCC语法,将注释从SPECIAL_TOKEN改为普通TOKEN
修改原有的语法定义,把注释从SPECIAL_TOKEN块移到TOKEN块中,这样注释会被保留在令牌流中,参与后续的语法解析:
TOKEN: { < LINE_COMMENT: ("--" | "//") (~["\r","\n"])*> | < MULTI_LINE_COMMENT: "/*" (~["*"])* "*" ("*" | (~["*","/"] (~["*"])* "*"))* "/" > }
2. 在语法规则中显式捕获注释的位置
在SQL核心语法规则(比如SELECT、FROM、JOIN、WHERE等子句的定义)中,添加可选的注释匹配逻辑,让解析器识别注释所属的上下文节点。例如,在SELECT语句的规则中,可以这样定义:
void selectStatement() : { Token comment; } { <SELECT> (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* selectExpression() (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* <FROM> (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* tableReference() (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* ( <JOIN> (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* tableReference() (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* <ON> expression() (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* )* ( <WHERE> (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* expression() (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* )? <SEMICOLON> (comment=<LINE_COMMENT> | comment=<MULTI_LINE_COMMENT>)* }
通过这种方式,解析器会在解析对应语法元素时,同步捕获附近的注释令牌。
3. 扩展JSQLParser节点类,存储关联的注释
给JSQLParser的核心语法节点类(比如SelectStatement、SelectExpressionItem、TableReference等)添加用于存储注释的字段,例如:
public class SelectExpressionItem implements SelectItem { // 原有字段 private Expression expression; // 新增注释存储字段 private List<Token> comments = new ArrayList<>(); // 对应的getter、setter方法 public List<Token> getComments() { return comments; } public void addComment(Token comment) { comments.add(comment); } }
在解析过程中,将捕获到的注释令牌存入对应节点的注释集合中。
4. 基于上下文实现格式化逻辑
在格式化SQL时,根据注释关联的节点位置,将注释输出到对应的位置:
- 前置注释(比如SELECT语句开头的多行--注释):在对应语法元素前保留原格式输出
- 行内注释(比如
* /* All cols */):紧跟在关联的表达式/元素后输出 - 行尾注释(比如
aap -- two):在对应元素的行末尾输出 - 子句末尾的注释(比如WHERE子句后的--注释):在子句结束后输出
以你的示例输入为例,格式化时会:
- 保留开头的两行--注释位置不变
- 将
/* All cols */跟在*后,随SELECT表达式换行缩进 - 将
-- two跟在表名aap后,随FROM子句的表项换行缩进 - WHERE子句的
/* inline comment */紧跟在WHERE后输出
内容的提问来源于stack exchange,提问作者Rob Audenaerde
相关产品推荐
相关产品推荐

