MySQL 源码|21 - 词法解析:状态转移逻辑梳理

MySQL 源码|词法解析:状态转移逻辑梳理

源码位置:(版本 = MySQL 8.0.37)

前置文档:

自动机整体逻辑

lex_one_token() 中有启动了一个无限循环,在循环中根据 state 变量 switch 选择需要执行的逻辑。在这样的设计下:

  • 如果当前 token 已经结束,则直接 return 结束函数
  • 如果当前 token 没有结束,可以更新 state 变量,然后 break 出当前循环的 switch,并在下一次循环中,根据更新后的 state 变量重新选择需要执行的逻辑。
  for (;;) {
    switch (state) {

lex_one_token() 的返回值是当前 token 的类型。

初始状态

lex_one_token() 函数启动无限循环之前,从 lip->next_state 获取当前状态并存入 state 变量,并将 lip->next_state 重置为 MY_LEX_START。如果后续没有重新指定 lip->next_state 的话,那么解析下一个 token 时的初始状态就是 MY_LEX_START

这里需要区分 state 变量和 lip->next_state 的区别。state 变量是 lex_one_token() 中的内部变量,记录当前 token 内部的处理状态,用于在无限循环中当前 token;lip->next_state 中的 lip 变量是存储在当前线程对象的 &thd->m_parser_state->m_lip 中的 Lex_input_stream 类型变量,记录 token 之间的处理状态,用于在下次调用 lex_one_token() 函数时使用。

  state = lip->next_state;
  lip->next_state = MY_LEX_START;
状态转移规则

整理各个状态的转移规则如下:其中标注 “(※)” 表示在代码并不真实存在该状态转移,但等价于存在该状态转移规则。

当前状态转移后状态指针状态转移条件
MY_LEX_START(通用开始状态)MY_LEX_IDENT(token 中只有字母、多字节字符和 _向后移动 1 个字节当前字符为字母、多字节字符或 _
MY_LEX_NUMBER_IDENT(token 中只有数字,不确定 token 是否结束)向后移动 1 个字节当前字符为数字
MY_LEX_START(通用开始状态)向后移动 1 个字节当前字符为空格或换行符(※)
MY_LEX_CHAR(token 中包含其他字符)向后移动 1 个字节当前字符为字母、数字、_、空格和其他特殊处理字符以外的其他字符
MY_LEX_STRING'向后移动 1 个字节当前字符为 '
MY_LEX_REAL_OR_POINT.向后移动 1 个字节当前字符为 .
MY_LEX_CMP_OP>=!向后移动 1 个字节当前字符为 >=!
MY_LEX_LONG_CMP_OP<向后移动 1 个字节当前字符为 <
MY_LEX_BOOL& 或 ``)向后移动 1 个字节
MY_LEX_COMMENT#--向后移动 1 个字节当前字符为 #
MY_LEX_SEMICOLON;向后移动 1 个字节当前字符为 ;
MY_LEX_SET_VAR:向后移动 1 个字节当前字符为 :
MY_LEX_EOL\x00向后移动 1 个字节当前字符为 \x00(空字符)
MY_LEX_LONG_COMMENT/向后移动 1 个字节当前字符为 /
MY_LEX_END_LONG_COMMENT*向后移动 1 个字节当前字符为 *
MY_LEX_USER_END@向后移动 1 个字节当前字符为 @
MY_LEX_USER_VARIABLE_DELIMITER\x60向后移动 1 个字节当前字符为 \x60(标识符引号、开单引号)
MY_LEX_STRING_OR_DELIMITER"向后移动 1 个字节当前字符为 "
MY_LEX_IDENT_OR_HEXxX向后移动 1 个字节当前字符为 xX
MY_LEX_IDENT_OR_BINbB向后移动 1 个字节当前字符为 bB
MY_LEX_IDENT_OR_NCHARnN向后移动 1 个字节当前字符为 nN
MY_LEX_IDENT_OR_DOLLAR_QUOTED_TEXT$向后移动 1 个字节当前字符为 $
MY_LEX_CHAR(token 中包含其他字符)MY_LEX_COMMENT#--不移动指针(即当前字符为第 1 个 -当前字符为 --,且下一个字符是控制字符或空字符
【结束】JSON_UNQUOTED_SEPARATOR_SYM(状态 = MY_LEX_START向后移动 2 个字节当前字符为 ->>
【结束】JSON_SEPARATOR_SYM(状态 = MY_LEX_START向后移动 1 个字节当前字符为 ->,且下一个字符不是 >
【结束】PARAM_MARKER(状态 = MY_LEX_START不移动指针当前字符为 ?,且开启了 stmt_prepare_mode 模式,且下一个字符不是有效字符,即当前 token为占位符
【结束】当前字符 ASCII 码值(状态 = MY_LEX_START不移动指针当前字符为 -) 之外的字符,且当前 token 不是占位符
【结束】当前字符 ASCII 码值(状态不重置)不移动指针当前字符为 )
MY_LEX_IDENT_OR_NCHARnN【结束】NCHAR_STRING(状态不重置)向后移动指针到匹配的 '当前字符为 ',且能够找到匹配的 '
MY_LEX_CHAR(token 中包含其他字符)向后移动 1 个字节跳过 '当前字符为 ',但找不到匹配的 '
MY_LEX_IDENT(token 中只有字母、多字节字符和 _不移动指针当前字符不是 '
MY_LEX_IDENT_OR_DOLLAR_QUOTED_TEXT$MY_LEX_IDENT(token 中包含其他字符)不移动指针-
MY_LEX_IDENT_OR_HEXxXMY_LEX_HEX_NUMBERx'X'不移动指针当前字符为 '
MY_LEX_IDENT不移动指针当前字符不为 '(※)
MY_LEX_IDENT_OR_BINbBMY_LEX_BIN_NUMBERb'B'不移动指针当前字符为 '
MY_LEX_IDENT不移动指针当前字符不为 '(※)
MY_LEX_IDENT(token 中只有字母、多字节字符和 _【结束】关键字对应的编码(状态 = MY_LEX_START移动指针直到结束当前 token当前 token 为函数或关键字
【结束】UNDERSCORE_CHARSET(状态 = MY_LEX_IDENT_SEP移动指针直到结束当前 token当前 token 以 _ 开头且字符集满足条件;当前 token 之后为 .,且 . 的下一个字符是下一个 token 的一部分
【结束】UNDERSCORE_CHARSET(状态不重置)移动指针直到结束当前 token当前 token 以 _ 开头且字符集满足条件;当前 token 之后不为 .,或 . 的下一个字符不是下一个 token 的一部分
【结束】IDENT_QUOTEDIDENT(状态 = MY_LEX_IDENT_SEP移动指针直到结束当前 token当前 token 不以 _ 开头或字符集不满足条件,且当前 token 不是函数或关键字;当前 token 之后为 .,且 . 的下一个字符是下一个 token 的一部分
【结束】IDENT_QUOTEDIDENT(状态不重置)移动指针直到结束当前 token当前 token 不以 _ 开头或字符集不满足条件,且当前 token 不是函数或关键字;当前 token 之后不为 .,或 . 的下一个字符不是下一个 token 的一部分
MY_LEX_IDENT_SEP(上一个 token 之后是 .. 的下一个字符为下一个 token)【结束】. 的 ASCII 码值 46(状态 = MY_LEX_IDENT_START指针向后移动 1 个字节. 之后中的字符为 token 的一部分
【结束】. 的 ASCII 码值 46(状态 = MY_LEX_START指针向后移动 1 个字节. 之后中的字符不是 token 的一部分
MY_LEX_NUMBER_IDENT(token 中只有数字,不确定 token 是否结束)【结束】HEX_NUM(状态不重置)移动指针直到结束十六进制数0x 开头的十六进制数
【结束】BIN_NUM(状态不重置)移动指针直到结束二进制数0b 开头的二进制数
MY_LEX_IDENT_START(以数字开头但包含其他字符)不移动指针)0x0b 开头但不是十六进制数或二进制数
MY_LEX_INT_OR_REAL(token 中只有数字,且 token 已结束)移动指针直到结束数字数字之后不是 SQL 字符
【结束】FLOAT_NUM(状态不重置)移动指针直到结束浮点数满足类似 1e+101e-10 形式的浮点数
MY_LEX_IDENT_START(以数字开头但包含其他字符)移动指针直到结束数字以数字开头但包含其他字符,且不是十六进制数、二进制数或浮点数(※)
MY_LEX_IDENT_START(以数字开头但包含其他字符)【结束】IDENT_QUOTEDIDENT(状态 = MY_LEX_IDENT_SEP移动指针直到结束当前 token当前 token 之后为 .,且 . 的下一个字符是下一个 token 的一部分
【结束】IDENT_QUOTEDIDENT(状态不重置)移动指针直到结束当前 token当前 token 之后不为 .,或 . 的下一个字符不是下一个 token 的一部分
MY_LEX_USER_VARIABLE_DELIMITER\x60【结束】ABORT_SYM(状态不重置)移动指针直到 \x00(结束符)找不到配对的引号
【结束】IDENT_QUOTED(状态 = MY_LEX_START移动指针到越过配对的引号能够找到配对的引号
MY_LEX_INT_OR_REAL(token 中只有数字,且 token 已结束)【结束】NUMLONG_NUMDECIMAL_NUMULONGLONG_NUM(状态不重置)不移动指针当前字符不是 .
MY_LEX_REAL(数字 + ..不移动指针当前字符是 .(※)
MY_LEX_REAL(数字 + ..MY_LEX_CHAR(token 中包含其他字符)移动指针直到结束数字如果数字后为 eE,且 eE 后不是数字
【结束】FLOAT_NUM(状态不重置)移动指针直到 e 之后的数字结束如果数字后为 eE,且 eE 后是数字
【结束】DECIMAL_NUM(状态不重置)移动指针直到结束数字数字后不是 eE
MY_LEX_HEX_NUMBERx'X'【结束】ABORT_SYM(状态不重置)移动指针直到十六进制数结束遍历所有十六进制数后,找不到配对的单引号或十六进制数为奇数个
【结束】HEX_NUM(状态不重置)移动指针直到十六进制数结束遍历所有十六进制数后,能够找到配对的单引号且十六进制数为偶数个
MY_LEX_BIN_NUMBERb'B'【结束】ABORT_SYM(状态不重置)移动指针直到二进制数结束遍历所有二进制数后,找不到配对的单引号
【结束】BIN_NUM(状态不重置)移动指针直到二进制数结束遍历所有二进制数后,找得到配对的单引号
MY_LEX_CMP_OP>=!【结束】token 关键字的编码(状态 = MY_LEX_START如果当前字符是运算符则移动指针当前 token 是关键字
MY_LEX_CHAR(token 中包含其他字符)如果当前字符是运算符则移动指针当前 token 不是关键字
``MY_LEX_LONG_CMP_OP<`)【结束】当前 token 关键字的编码(状态 = MY_LEX_START如果当前字符和下一个字符为运算符,则将指针移动 1 个或 2 个字符当前 token 是关键字
MY_LEX_CHAR(token 中包含其他字符)如果当前字符和下一个字符为运算符,则将指针移动 1 个或 2 个字符当前 token 不是关键字
MY_LEX_BOOL& 或 ``)MY_LEX_CHAR(token 中包含其他字符)不移动指针
【结束】当前 token 关键字的编码(状态 = MY_LEX_START指针向后移动 1 个字符& 和 `
MY_LEX_STRING_OR_DELIMITER"MY_LEX_USER_VARIABLE_DELIMITER\x60不移动指针当前线程 sql_mode 开启 MODE_ANSI_QUOTES
MY_LEX_STRING'不移动指针当前线程 sql_mode 未开启 MODE_ANSI_QUOTES(※)
MY_LEX_STRING'【结束】TEXT_STRING(状态不重置)移动指针到越过配对的引号能够找到配对的引号
MY_LEX_CHAR(token 中包含其他字符)不移动指针找不到配对的引号
MY_LEX_COMMENT#--MY_LEX_START(通用开始状态)向后移动指针直到 \n\x00-
MY_LEX_LONG_COMMENT/MY_LEX_CHAR(token 中包含其他字符)不移动指针当前字符不是 *
MY_LEX_START(通用开始状态)向后移动指针越过 */当前字符是 *,且能够找到配对的 */
【结束】ABORT_SYM(状态不重置)-当前字符是 *,但找不到配对的 */
MY_LEX_END_LONG_COMMENT*MY_LEX_START(通用开始状态)向后移动指针越过 */当前在注释中且下一个字符是 /
MY_LEX_CHAR(token 中包含其他字符)不移动指针当前不在注释中或下一个字符不是 /
MY_LEX_SET_VAR:【结束】SET_VAR(状态不重置)指针向后移动 1 个字符当前字符是 =
MY_LEX_CHAR(token 中包含其他字符)不移动指针当前字符不是 =
MY_LEX_SEMICOLON;MY_LEX_CHAR(token 中包含其他字符)不移动指针-
MY_LEX_EOL\x00【结束】END_OF_INPUT(状态 = MY_LEX_END不移动指针原始数据流已遍历结束,且不在注释中
【结束】ABORT_SYM不移动指针原始数据流已遍历结束,且在注释中
MY_LEX_CHAR(token 中包含其他字符)不移动指针原始数据流没有遍历结束
MY_LEX_END(当前语句已经结束)【结束】0(状态 = MY_LEX_END不移动指针-
MY_LEX_REAL_OR_POINT.MY_LEX_REAL(数字 + ..不移动指针当前字符为数字
MY_LEX_IDENT_SEP(上一个 token 之后是 .. 的下一个字符为下一个 token)指针向前移动 1 个字符当前字符不是数字
MY_LEX_USER_END@【结束】@ 的 ASCII 码值 64(状态不重置)不移动指针当前字符是单引号、标识符引号或双引号
【结束】@ 的 ASCII 码值 64(状态 = MY_LEX_SYSTEM_VAR不移动指针当前字符是 @
【结束】@ 的 ASCII 码值 64(状态 = MY_LEX_HOSTNAME不移动指针当前字符不是单引号、标识符引号、双引号或 @
MY_LEX_HOSTNAME(上一个 token 是 @【结束】LEX_HOSTNAME(状态不重置)将指针移动到结束当前 token-
MY_LEX_SYSTEM_VAR(上一个 token 是 @,且当前 token 是 @【结束】@ 的 ASCII 码值 64(状态 = MY_LEX_START将指针向后移动 1 个字符越过 @当前字符为 \x60(标识符引号)
【结束】@ 的 ASCII 码值 64(状态 = MY_LEX_IDENT_OR_KEYWORD将指针向后移动 1 个字符越过 @当前字符不是 \x60(标识符引号)
MY_LEX_IDENT_OR_KEYWORD(之前的 token 是 @@,且当前 token 是不是 \x60【结束】当前关键字的码值(状态 = MY_LEX_IDENT_SEP将指针移动到结束当前 token当前 token 之后的字符为 .,且 token 中为关键字
【结束】IDENT_QUOTEDIDENT(状态 = MY_LEX_IDENT_SEP将指针移动到结束当前 token当前 token 之后的字符为 .,且 token 中不为关键字
【结束】当前关键字的码值(状态不重置)将指针移动到结束当前 token当前 token 之后的字符不为 .,且 token 中为关键字
【结束】IDENT_QUOTEDIDENT(状态不重置)将指针移动到结束当前 token当前 token 之后的字符不为 .,且 token 中不为关键字
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值