文章目录

  • 前言
  • 本章节源码
  • 需求1:实现一个最简单的select sql
    • 要求
    • 实现jj文件
    • 编译测试
  • 需求2:理解Token及其他属性
    • 说明
    • javajj文件
  • 需求3:实现解析得到SQL语法树 & 精确点位
  • 资料获取

 javacc实现简单SQL解析器

前言

博主介绍:✌目前全网粉丝4W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。

博主所有博客文件目录索引:博客目录索引(持续更新)

CSDN搜索:长路

视频平台:b站-Coder长路

本章节源码

当前文档配套相关源码地址:

  • gitee:https://gitee.com/changluJava/demo-exer/tree/master/java-sqlparser/demo-javacc/learn-javacc-demo
  • github:https://github.com/changluya/Java-Demos/tree/master/java-sqlparser/demo-javacc/learn-javacc-demo

需求1:实现一个最简单的select sql

要求

支持语法:
select * from table where a=1
select id from table where a=1
select id,name from table where a=2

实现jj文件

options {STATIC = false; // 动态生成解析器
}PARSER_BEGIN(SQLParser)
import java.io.*;
public class SQLParser {public static void main(String[] args) throws ParseException, IOException {for (String arg : args) {Reader reader = new StringReader(arg);SQLParser parser = new SQLParser(reader);parser.sqlQuery();System.out.println("Parsing completed successfully.");}}
}
PARSER_END(SQLParser)// 定义词法规则
SKIP : {" " | "\t" | "\n" | "\r"  // 跳过空白字符
}TOKEN : {< SELECT: "select" >| < FROM: "from" >| < WHERE: "where" >| < IDENTIFIER: (["a"-"z", "A"-"Z"])+ >| < NUMBER: (["0"-"9"])+ >| < ASTERISK: "*" >  // 添加对 * 的支持
}// 定义 SQL 查询的语法规则
void sqlQuery() :
{}
{<SELECT>( columnListOrAsterisk() )<FROM> tableName()[ whereClause() ] <EOF>{System.out.println("Parsed SQL Query");}
}// 列表规则或 *
void columnListOrAsterisk() :
{}
{<ASTERISK> { System.out.println("Column: *"); }|columnList()
}// 列表规则
void columnList() :
{}
{columnName() ( "," columnName() )*
}// 单个列名规则
void columnName() :
{}
{<IDENTIFIER>{// 没有定义变量的场景,默认匹配到的单词为tokenSystem.out.println("Column: " + token.image);}
}// 表名规则
void tableName() :
{}
{<IDENTIFIER>{System.out.println("Table: " + token.image);}
}// WHERE 子句规则
void whereClause() :
{}
{<WHERE> condition()
}// 条件规则
void condition() :
{// 如果某个方法中涉及到多个变量获取打印情况,则可以使用如下变量定义Token identifierToken, numberToken;
}
{// 读取值方式为: 变量名=<token> 匹配相应字符identifierToken=<IDENTIFIER> "=" numberToken=<NUMBER>{System.out.println("Condition: " + identifierToken.image + " = " + numberToken.image);}
}

编译测试

javacc demo03.jjtjavac SQLParser.javajava SQLParser "select id from changlu where a=1"

需求2:理解Token及其他属性

说明

JavaCC生成的SQL解析器中打印出Token的其他属性,你需要访问Token对象的额外信息。默认情况下,JavaCC为每个匹配到的词法单元(token)创建一个Token对象,这个对象不仅包含词法单元的文本内容(即image),还包含了其他有用的信息,如行号(beginLineendLine)、列号(beginColumnendColumn)等。

javajj文件

补充打印了token的image、beginLine、endLine、beginColumn、endColumn

options {STATIC = false; // 动态生成解析器
}PARSER_BEGIN(SQLParser)
import java.io.*;
public class SQLParser {public static void main(String[] args) throws ParseException, IOException {for (String arg : args) {Reader reader = new StringReader(arg);SQLParser parser = new SQLParser(reader);parser.sqlQuery();System.out.println("Parsing completed successfully.");}}
}
PARSER_END(SQLParser)// 定义词法规则
SKIP : {" " | "\t" | "\n" | "\r"  // 跳过空白字符
}TOKEN : {< SELECT: "select" >| < FROM: "from" >| < WHERE: "where" >| < IDENTIFIER: (["a"-"z", "A"-"Z"])+ >| < NUMBER: (["0"-"9"])+ >| < ASTERISK: "*" >  // 添加对 * 的支持
}// 定义 SQL 查询的语法规则
void sqlQuery() :
{}
{<SELECT>( columnListOrAsterisk() )<FROM> tableName()[ whereClause() ] <EOF>{System.out.println("Parsed SQL Query");}
}// 列表规则或 *
void columnListOrAsterisk() :
{}
{<ASTERISK> { System.out.println("Column: *"); }|columnList()
}// 列表规则
void columnList() :
{}
{columnName() ( "," columnName() )*
}// 单个列名规则
void columnName() :
{}
{<IDENTIFIER>{// 没有定义变量的场景,默认匹配到的单词为tokenToken t = token;System.out.println("Column: " + t.image+ ", Line: " + t.beginLine + ", end Line:" + t.endLine+ ", beginColumn: " + t.beginColumn + ", endColumn:" + t.endColumn);}
}// 表名规则
void tableName() :
{}
{<IDENTIFIER>{// 没有定义变量的场景,默认匹配到的单词为tokenToken t = token;System.out.println("Table: " + t.image+ ", Line: " + t.beginLine + ", end Line:" + t.endLine+ ", beginColumn: " + t.beginColumn + ", endColumn:" + t.endColumn);}
}// WHERE 子句规则
void whereClause() :
{}
{<WHERE> condition()
}// 条件规则
void condition() :
{// 如果某个方法中涉及到多个变量获取打印情况,则可以使用如下变量定义Token identifierToken, numberToken;
}
{// 读取值方式为: 变量名=<token> 匹配相应字符identifierToken=<IDENTIFIER> "=" numberToken=<NUMBER>{System.out.println("Condition: " + identifierToken.image + " = " + numberToken.image);System.out.println("identifierToken: " + identifierToken.image+ ", Line: " + identifierToken.beginLine + ", end Line:" + identifierToken.endLine+ ", beginColumn: " + identifierToken.beginColumn + ", endColumn:" + identifierToken.endColumn);System.out.println("numberToken: " + numberToken.image+ ", Line: " + numberToken.beginLine + ", end Line:" + numberToken.endLine+ ", beginColumn: " + numberToken.beginColumn + ", endColumn:" + numberToken.endColumn);}
}

需求3:实现解析得到SQL语法树 & 精确点位

img

能力:解析出语法树,每个字段节点都能够解析到位点。

SqlParser.jjt:

options {STATIC = false;
}PARSER_BEGIN(SQLParser)
import java.io.*;
import java.util.ArrayList;
import java.util.List;// 位置信息类
class TokenPosition {public final int startLine;public final int startColumn;public final int endLine;public final int endColumn;public TokenPosition(int startLine, int startColumn, int endLine, int endColumn) {this.startLine = startLine;this.startColumn = startColumn;this.endLine = endLine;this.endColumn = endColumn;}public TokenPosition(Token token) {this(token.beginLine, token.beginColumn, token.endLine, token.endColumn);}@Overridepublic String toString() {return "[" + startLine + ":" + startColumn + "-" + endLine + ":" + endColumn + "]";}
}// 语法树节点接口
interface ASTNode {void accept(ASTVisitor visitor);TokenPosition getPosition();
}// SELECT语句节点
class SelectStatement implements ASTNode {public final TokenPosition position;public final List<Column> columns;public final Table table;public final Condition whereCondition;public SelectStatement(TokenPosition position, List<Column> columns, Table table, Condition whereCondition) {this.position = position;this.columns = columns;this.table = table;this.whereCondition = whereCondition;}@Overridepublic void accept(ASTVisitor visitor) {visitor.visit(this);}@Overridepublic TokenPosition getPosition() {return position;}
}// 列节点
class Column implements ASTNode {public final TokenPosition position;public final String name;public Column(TokenPosition position, String name) {this.position = position;this.name = name;}@Overridepublic void accept(ASTVisitor visitor) {visitor.visit(this);}@Overridepublic TokenPosition getPosition() {return position;}
}// 表节点
class Table implements ASTNode {public final TokenPosition position;public final String name;public Table(TokenPosition position, String name) {this.position = position;this.name = name;}@Overridepublic void accept(ASTVisitor visitor) {visitor.visit(this);}@Overridepublic TokenPosition getPosition() {return position;}
}// WHERE条件节点
class Condition implements ASTNode {public final TokenPosition position;public final Column column;public final String value;public final TokenPosition valuePosition;public Condition(TokenPosition position, Column column, String value, TokenPosition valuePosition) {this.position = position;this.column = column;this.value = value;this.valuePosition = valuePosition;}@Overridepublic void accept(ASTVisitor visitor) {visitor.visit(this);}@Overridepublic TokenPosition getPosition() {return position;}
}// 访问者接口
interface ASTVisitor {void visit(SelectStatement select);void visit(Column column);void visit(Table table);void visit(Condition condition);
}// 示例访问者实现
class PrintVisitor implements ASTVisitor {private int indent = 0;private String getIndent() {StringBuilder sb = new StringBuilder();for (int i = 0; i < indent; i++) {sb.append("  ");}return sb.toString();}private String formatPosition(TokenPosition pos) {return " " + pos;}@Overridepublic void visit(SelectStatement select) {System.out.println(getIndent() + "SELECT" + formatPosition(select.position));indent++;for (Column col : select.columns) {col.accept(this);}select.table.accept(this);if (select.whereCondition != null) {select.whereCondition.accept(this);}indent--;}@Overridepublic void visit(Column column) {System.out.println(getIndent() + "COLUMN: " + column.name + formatPosition(column.position));}@Overridepublic void visit(Table table) {System.out.println(getIndent() + "FROM " + table.name + formatPosition(table.position));}@Overridepublic void visit(Condition condition) {System.out.println(getIndent() + "WHERE " +condition.column.name + " = " + condition.value +formatPosition(condition.position) +" (value at" + formatPosition(condition.valuePosition) + ")");}
}public class SQLParser {private SelectStatement selectStatement;public SelectStatement getAST() {return selectStatement;}public static void main(String[] args) throws ParseException, IOException {String sql = args.length > 0 ? args[0] : "select id, name from users where id = 1";Reader reader = new StringReader(sql);SQLParser parser = new SQLParser(reader);try {parser.sqlQuery();System.out.println("Parsing completed successfully.");SelectStatement ast = parser.getAST();System.out.println("\nAbstract Syntax Tree with Positions:");ast.accept(new PrintVisitor());} catch (ParseException e) {System.err.println("SQL parse error: " + e.getMessage());}}
}
PARSER_END(SQLParser)// 词法规则
SKIP : { " " | "\t" | "\n" | "\r" }
TOKEN : {< SELECT: "select" >| < FROM: "from" >| < WHERE: "where" >| < IDENTIFIER: (["a"-"z","A"-"Z"])+ >| < NUMBER: (["0"-"9"])+ >| < ASTERISK: "*" >
}// 语法规则
void sqlQuery() :
{Token selectToken;List<Column> columns = new ArrayList<>();Table table;Condition whereCondition = null;TokenPosition selectPosition;
}
{selectToken = <SELECT>columns = columnListOrAsterisk(selectToken)<FROM>  // Explicitly consume FROM tokentable = tableName()[ whereCondition = whereClause() ] <EOF>{selectPosition = new TokenPosition(selectToken.beginLine, selectToken.beginColumn,token.endLine, token.endColumn);selectStatement = new SelectStatement(selectPosition, columns, table, whereCondition);}
}List<Column> columnListOrAsterisk(Token selectToken) :
{List<Column> columns = new ArrayList<>();Token token;
}
{(token = <ASTERISK>{columns.add(new Column(new TokenPosition(token), "*"));}|columns = columnList()){ return columns; }
}List<Column> columnList() :
{List<Column> columns = new ArrayList<>();Column column;
}
{column = columnName() { columns.add(column); }( "," column = columnName() { columns.add(column); } )*{ return columns; }
}Column columnName() :
{Token t;
}
{t = <IDENTIFIER>{return new Column(new TokenPosition(t), t.image);}
}Table tableName() :
{Token t;
}
{t = <IDENTIFIER>  // Just parse the identifier, FROM is already handled{return new Table(new TokenPosition(t), t.image);}
}Condition whereClause() :
{Token whereToken;Condition condition;
}
{whereToken = <WHERE> condition = condition(){return condition;}
}Condition condition() :
{Column column;Token operator, valueToken;String value;
}
{column = columnName()operator = "="(valueToken = <NUMBER>{value = valueToken.image;}|valueToken = <IDENTIFIER>{value = valueToken.image;}){return new Condition(new TokenPosition(column.getPosition().startLine,column.getPosition().startColumn,valueToken.endLine,valueToken.endColumn),column,value,new TokenPosition(valueToken));}
}

执行命令解析:

javacc SqlParser.jjtjavac *.javajava SQLParser "select id,name from users where id=1"

资料获取

大家点赞、收藏、关注、评论啦~

精彩专栏推荐订阅:在下方专栏👇🏻

  • 长路-文章目录汇总(算法、后端Java、前端、运维技术导航):博主所有博客导航索引汇总
  • 开源项目Studio-Vue—校园工作室管理系统(含前后台,SpringBoot+Vue):博主个人独立项目,包含详细部署上线视频,已开源
  • 学习与生活-专栏:可以了解博主的学习历程
  • 算法专栏:算法收录

更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917839.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917839.shtml
英文地址,请注明出处:http://en.pswp.cn/news/917839.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Odoo OWL前端框架全面学习指南 (后端开发者视角)

核心理念&#xff1a; 将您熟悉的Odoo后端MVCORM架构思想&#xff0c;完整映射到前端OWL组件化开发中&#xff0c;让您在熟悉的概念体系下&#xff0c;快速掌握新的技术栈。第一部分&#xff1a;核心概念映射与环境搭建内容摘要&#xff1a; 本部分旨在建立后端与前端最核心的概…

Java开发工具包,jdk,idea,VMware,rocketmq,redis,CentOS7

Java开发工具包&#xff0c;jdk&#xff0c;idea&#xff0c;VMware&#xff0c;rocketmq&#xff0c;redis&#xff0c;CentOS7 下载地址 通过网盘分享的文件&#xff1a;Java开发环境工具包 链接: https://pan.baidu.com/s/1eJqvPx5DYqtmXgmEtOl8-A?pwdcj1f 提取码: cj1f –…

macOS Python 安装

目录 一、确认系统环境 二、安装 &#xff08;一&#xff09;下载安装包 &#xff08;二&#xff09;安装过程 三、配置环境变量 四、验证安装 一、确认系统环境 在安装 Python 之前&#xff0c;我们先简单了解一下自己的 MACOS 系统。可以点击屏幕左上角的苹果菜单&…

MySQL 全方位解析:从基础到高可用架构

1. 介绍 (Introduction) 1.1. 什么是 MySQL&#xff1f; MySQL 是全球最受欢迎的开源关系型数据库管理系统 (Relational Database Management System, RDBMS)。它由瑞典的 MySQL AB 公司开发&#xff0c;现隶属于 Oracle 公司。MySQL 将数据存储在不同的、预先定义好结构的表中…

力扣热题100——滑动窗口

无重复字符的最长子串步骤 1&#xff1a;初始状态 字符串 s “abcabcbb”&#xff0c;哈希表 charSet 初始为空&#xff0c;双指针 left 0&#xff0c;right 0。 哈希表&#xff08;charSet&#xff09;&#xff1a; {} 字符串&#xff1a; a b c a b c b b 指…

SOD-YOLO:增强基于YOLO的无人机影像小目标检测

摘要 https://www.arxiv.org/pdf/2507.12727 小目标检测仍是目标检测领域中的一个挑战性问题。为应对这一挑战&#xff0c;我们提出了一种基于YOLOv8的增强模型SOD-YOLO。该模型在颈部&#xff08;neck&#xff09;中集成了ASF&#xff08;注意力尺度序列融合&#xff09;机制以…

监督微调-指令微调-偏好微调

有监督微调 有监督微调是使用输入及其标签对的典型情况。例如&#xff0c;判断邮件是垃圾邮件还是非垃圾邮件&#xff0c;判断情感是积极还是消极。根据文档的主要主题对其进行分类也是一种常见应用。模型会将输入文本的相应表示&#xff08;隐藏状态或嵌入向量&#xff09;作为…

楼宇自控系统对建筑碳中和目标的实现具重要价值

随着全球气候变化问题日益严峻&#xff0c;建筑行业作为碳排放的重要来源之一&#xff0c;其节能减排工作备受关注。楼宇自控系统&#xff08;Building Automation System&#xff0c;BAS&#xff09;作为智能建筑的核心组成部分&#xff0c;通过集成控制、监测和管理建筑内的各…

【YOLO学习笔记】YOLOv5详解

一、数据增强 mosaic仿射变换与透视变换Mixup mosaic代码位置仿射变换 与 透视变换​代码片段位置 二、网络结构 1. 网络不同尺寸 nsmlx与网络深宽度 yolov5 官方提供了5个目标检测的网络版本&#xff1a;yolov5n、yolov5s、yolov5m、yolov5l、yolov5x &#xff0c;早年是…

WebRTC前处理模块技术详解:音频3A处理与视频优化实践

一、WebRTC前处理模块概述 WebRTC&#xff08;Web Real-Time Communication&#xff09;作为实时音视频通信的核心技术&#xff0c;其前处理模块是提升媒体质量的关键环节。该模块位于媒体采集与编码之间&#xff0c;通过对原始音频/视频数据进行优化处理&#xff0c;解决实时…

ssm复习

Spring Framework系统架构核心容器的学习IOC/DIIOC容器IOC使用对象时,由主动new产生的对象转换为由外部提供对象,此过程中对象的创建的控制权交由外部,此思想称为控制反转, (实现了自己new的解耦) 对象创建的控制权Spring提供一个容器,称为IOC容器 用来充当IOC思想的外部Bea…

ESP32:2.搭建UDP服务器

硬件&#xff1a;ESP32-Devkit-V4 MODEL:ESP32-32U 库&#xff1a;ESP-IDF v5.4.1 系统&#xff1a;windows中的虚拟机 ubuntu 22.04 实现STA&#xff0c;主动连接AP后&#xff0c;打印IP地址&#xff0c;获取IP后&#xff0c;创建socket&#xff0c;搭建UDP 服务器&#xff0…

【Linux】动静态库制作

&#x1f43c;故事背景假设今天你有一位舍友。你需要帮助他完成老师的作业。而他写的代码依赖两个文件&#xff08;mymath.h,mystdio.h&#xff09;。但是这两个文件的功能他不会写&#xff0c;他只会调用。他的调用代码:#include"mystdio.h" #include"mymath.h…

使用Database Navigator插件进行连接sqlite报错invalid or incomplete database

解决方案 &#xff0c;将这个db.sqlite3文件拷贝到盘的文件中 &#xff0c;修改文件夹名字&#xff0c;重新使用绝对路径访问 db.sqlite3&#xff0c;将路径名字的中文去掉 &#xff0c;不能有中文

【Linux】重生之从零开始学习运维之主从MGR高可用

MGR集群部署12、15、18主机环境准备ssh免密码登录\rm -rf .ssh/* ssh-keygen ssh-copy-id 127.1 scp -r .ssh 10.0.0.12:/root/ ssh root10.0.0.12还原基础环境systemctl stop mysqld \rm -rf /var/lib/mysql/* id mysqlvim /etc/my.cnf.d/mysql-server.cnf [mysqld] datadir/v…

如何在虚拟机(Linux)安装Qt5.15.2

1.进入到阿里的网站下载在线安装包 qt-official_releases-online_installers安装包下载_开源镜像站-阿里云 https://mirrors.aliyun.com/qt/official_releases/online_installers/?spma2c6h.13651104.d-5201.2.60ad4773ZZNPNm 2.下载完毕后&#xff0c;进入到下载地址&…

【运维进阶】DHCP服务配置和DNS域名解析

DHCP服务配置和DNS域名解析 DHCP 服务介绍 在大型网络中&#xff0c;系统静态分配IP地址面临问题&#xff1a; 确保不要同时在多个系统上使用同一个地址。部署新系统通常需要手动分配其IP地址。在云环境中&#xff0c;实例的网络是自动化配置的。 动态主机配置协议&#xff08;…

VisionPro MR环境下虚拟物体与现实的透明度混合

display.rgb (virtualcontent.rgb*1)(passthrough.rgb*(1 - vistualcontent.a) viirtualcontent预乘过a值了&#xff0c;跟透明度混合公式一致 人头检测挖孔不清晰问题&#xff0c;这个a值变成设备层动态检测人头的a值&#xff0c;当面前的渲染压力过大时&#xff0c;会导致…

css怪异模式(Quirks Mode)和标准模式(Standards Mode)最明显的区别

文章目录css怪异模式&#xff08;Quirks Mode&#xff09;和标准模式&#xff08;Standards Mode&#xff09;最明显的区别详细对比示例对比&#xff08;盒模型&#xff09;标准模式&#xff08;Standards Mode&#xff09;怪异模式&#xff08;Quirks Mode&#xff09;如何触发…

一种简单的3dnr去噪算法介绍

一段未经过插补的视频图像可以分解为若干帧&#xff0c;为了能正确地找到并去除图像帧中的噪声污染&#xff0c;由于视频图像各帧的连续性&#xff0c;在去噪的过程中就必须考虑帧图像的空间性和时间性&#xff0c;一个简单的例子&#xff0c;在去噪算法中就必须考虑&#xff0…