javacc實現簡單SQL解析器

文章目錄

  • 前言
  • 本章節源碼
  • 需求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…