You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何借助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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 17:24:47