解析器
代码地址 👉 GitHub地址:DagonParser
即Parser,和计算机科学中的编译原理这门课关联比较多。
网上常见的解析器有Cobar的解析器、Druid的解析器、还有一些选择采用一些工具生成的解析器,比如说 ANTLR,只需要描述好语法规则,这个工具就能生成对应的编译器。
下面介绍的解析器是手撸的,目前也实现了DML、DDL语法(持续更新中),语法参考MySQL8.0官方文档上的语法,项目组成部分参考Cobar的解析器,但是转为byte流来解析。
解析器的三个重要组成部分,它们分别是:
- Parser
- Lexer(词法分析)
- Syntax(语法分析)
- AST(Abstract Syntax Tree)抽象语法树
- Visitor
词法分析:
举例说明,SQL语句:select id from merchant
拿到这条SQL语句后,需要解析出每个独立的单词,select、id、from、merchant。Keywords就是以上在SQL中具有重要意义的单词,某些关键字,如SELECT, DELETE或 BIGINT,被保留,需要用作标识符,例如表和列名特殊待遇。这里将需要保留的keywords统一称为token、不需要保留的称为keywords。解析独立单词的过程,就称为词法分析。
语法分析:
对于解析器来说呢,它不仅需要知道每个单词,而且要知道这些单词组合在一起后,表达了什么含义。语法分析的职责就是明确一个语句的语义,表达的是什意思。
自然语言和形式语言的一个重要区别是,自然语言的一个语句,可能有多重含义,而形式语言的一个语句,只能有一个语义;形式语言的语法是人为规定的,有了一定的语法规则,语法解析器就能根据语法规则,解析出一个语句的一个唯一含义。
AST:
AST (抽象语法树)是 Parser 的产物,语句经过词法分析,语法分析后,它的结构需要以一种计算机能读懂的方式表达出来,最常用的就是抽象语法树。
一个语句,它由哪些部分组成?其中一个组成部分又有哪些部分组成?
例如一条 select 语句,它由 select 列表、where 子句、order by排序字段、group by分组字段等组成,而 select 列表则由一个或多个 select 项组成,where 子句又由一个或者多个 where条件组成。
这种组成结构就是一个总分的逻辑结构,用树来表达,最合适不过。对于计算机来说,处理树也是最方便的。
AST 仅仅是语义的表示,但如何对这个语义进行表达,便需要去访问这棵 AST,看它到底表达什么含义。
通常遍历语法树,使用 Visitor 模式去遍历,从根节点开始遍历,一直到最后一个叶子节点,在遍历的过程中,便不断地收集信息到一个上下文中,整个遍历过程完成后,对这棵树所表达的语法含义,已经被保存到上下文了。我这边解析器的遍历方式主要为深度优先遍历,进行byte流的拼接。
快速入门
MySQL8.0官方文档的SELECT语法
demo test
public class MySQLDemoParserTest extends AbstractSyntaxTest {
public void test() throws Exception {
//byte流
byte[] sql = "select id from merchant".getBytes();
//语法解析(包含词法解析)
Tuple2<List<SQLStatement>, Exception> tuple = Parser.parse(sql, MySqlCharset.UTF8_FOR_JAVA);
assertNotNull(tuple);
assertNotNull(tuple._1());
assertNull(tuple._2());
assertEquals(tuple._1().size(), 1);
//获取AST树
DMLSelectStatement select = (DMLSelectStatement)tuple._1().get(0);
//遍历输出AST树
String output = outputMySQL(select, sql);
assertEquals("SELECT id FROM merchant", output);
}
}
public class AbstractSyntaxTest extends TestCase {
private static final boolean debug =