MySQL 源码|16 - 词法解析:状态及状态转移规则(4)

MySQL 源码|词法解析:状态及状态转移规则(4)

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

前置文档:

MY_LEX_IDENT_SEP(在 token 和 . 之后)
      case MY_LEX_IDENT_SEP:  // Found ident and now '.'
        yylval->lex_str.str = const_cast<char *>(lip->get_ptr());
        yylval->lex_str.length = 1;
        c = lip->yyGet();  // should be '.'
        if (uchar next_c = lip->yyPeek(); ident_map[next_c]) {
          lip->next_state =
              MY_LEX_IDENT_START;  // Next is an ident (not a keyword)
          if (next_c == '$')       // We got .$ident
            push_deprecated_warn_no_replacement(
                lip->m_thd,
                "$ as the first character of an unquoted identifier");
        } else  // Probably ` or "
          lip->next_state = MY_LEX_START;

        return ((int)c);

令当前 token 为 .。如果下一个字符是 SQL 字符,则将状态置为 MY_LEX_IDENT_START(下一个字符是标识符而非关键字),否则下一个字符可能标识符引号或双引号,将状态置为 MY_LEX_START(初始状态)。

涉及的状态转移规则如下:

当前状态字符类型变更状态
MY_LEX_IDENT_SEP下一个字符是 SQL 字符状态 = MY_LEX_IDENT_START;【返回】46(. 的 ASCII 码)
其他状态 = MY_LEX_START;【返回】46(. 的 ASCII 码)
MY_LEX_NUMBER_IDENT(在数字字符串中)

Step 1|如果第一个数字是 0,则判断下一个字符是否为 bx,处理 0b0x 的问题。如果确认为十六进制数(0x 开头)时,则返回 474(HEX_NUM);如果确认为二进制数(0b 开头),则返回 292BIN_NUM);如果是 0x0b 开头,但不是十六进制数或二进制数,则将指针向前移动回 0 的位置,并将状态置为 MY_LEX_IDENT_START(下一个字符是标识符而非关键字),并 break 出 switch 继续处理当前 token。

        if (lip->yyGetLast() == '0') {
          c = lip->yyGet();
          if (c == 'x') {
            while (my_isxdigit(cs, (c = lip->yyGet())))
              ;
            if ((lip->yyLength() >= 3) && !ident_map[c]) {
              /* skip '0x' */
              yylval->lex_str = get_token(lip, 2, lip->yyLength() - 2);
              return (HEX_NUM);
            }
            lip->yyUnget();
            state = MY_LEX_IDENT_START;
            break;
          } else if (c == 'b') {
            while ((c = lip->yyGet()) == '0' || c == '1')
              ;
            if ((lip->yyLength() >= 3) && !ident_map[c]) {
              /* Skip '0b' */
              yylval->lex_str = get_token(lip, 2, lip->yyLength() - 2);
              return (BIN_NUM);
            }
            lip->yyUnget();
            state = MY_LEX_IDENT_START;
            break;
          }
          lip->yyUnget();
        }

Step 2|遍历所有到所有数字之后

        while (my_isdigit(cs, (c = lip->yyGet())))
          ;

Step 3|如果数字之后不是 SQL 字符,则将状态改为 MY_LEX_INT_OR_REAL(数字之后,不是标识符),并 break 出 switch 继续处理当前 token。

        if (!ident_map[c]) {  // Can't be identifier
          state = MY_LEX_INT_OR_REAL;
          break;
        }

Step 4|如果数字之后的字符是 eE,则考虑是类似 1E+10 这类浮点数的情形。如果 eE 的下一个字符是数字,或 +- 符号后跟着数字,则继续解析剩余遍历到所有数字之后,返回 443(FLOAT_NUM)。如果不是,则将指针向前移动 1 个字符回到数字之后。

        if (c == 'e' || c == 'E') {
          // The following test is written this way to allow numbers of type 1e1
          if (my_isdigit(cs, lip->yyPeek()) || (c = (lip->yyGet())) == '+' ||
              c == '-') {  // Allow 1E+10
            if (my_isdigit(cs,
                           lip->yyPeek()))  // Number must have digit after sign
            {
              lip->yySkip();
              while (my_isdigit(cs, lip->yyGet()))
                ;
              yylval->lex_str = get_token(lip, 0, lip->yyLength());
              return (FLOAT_NUM);
            }
          }
          lip->yyUnget();
        }

Step 5|继续执行 MY_LEX_IDENT_START 的逻辑,相当于将状态置为 MY_LEX_IDENT_START,并 break 出 switch 继续处理当前 token。

        [[fallthrough]];

涉及的状态转移规则如下:

当前状态字符类型变更状态
MY_LEX_NUMBER_IDENT0x 开头的十六进制数【返回】474(HEX_NUM
0b 开头的二进制数【返回】292BIN_NUM
0x0b 开头但不是十六进制数或二进制数状态 = MY_LEX_IDENT_START
数字之后不是 SQL 字符状态 = MY_LEX_INT_OR_REAL
形如 1e+10 的浮点数【返回】443(FLOAT_NUM
其他状态 = MY_LEX_IDENT_START
MY_LEX_IDENT_START:下一个字符是标识符而非关键字

Step 1|根据多字符字符集或单字符字符集,遍历当前 token 的所有字符。如果是多字节字符集,则将 result_state 置为 IDENT_QUOTED;如果是单字节字符集,如果最后字符是否为 ASCII 字符为非 ASCII 字符则将 result_state 置为 IDENT_QUOTED,否则,置为 IDENT

switch 逻辑详见:MySQL 源码|词法解析:状态及状态转移规则(3)

        result_state = IDENT;
        if (use_mb(cs)) {
          result_state = IDENT_QUOTED;
          while (ident_map[c = lip->yyGet()]) {
            switch (my_mbcharlen(cs, c)) {
              case 1:
                break;
              case 0:
                if (my_mbmaxlenlen(cs) < 2) break;
                [[fallthrough]];
              default:
                int l;
                if ((l = my_ismbchar(cs, lip->get_ptr() - 1,
                                     lip->get_end_of_query())) == 0)
                  break;
                lip->skip_binary(l - 1);
            }
          }
        } else {
          for (result_state = 0; ident_map[c = lip->yyGet()]; result_state |= c)
            ;
          /* If there were non-ASCII characters, mark that we must convert */
          result_state = result_state & 0x80 ? IDENT_QUOTED : IDENT;
        }

Step 2|如果下一个字符是 . 且之后的字符为 SQL 字符,则将状态置为 MY_LEX_IDENT_SEP

        if (c == '.' && ident_map[lip->yyPeek()])
          lip->next_state = MY_LEX_IDENT_SEP;  // Next is '.'

Step 3|获取当前 token 并更新 UTF-8 格式数据流,返回 result_state 作为当前状态。

        yylval->lex_str = get_token(lip, 0, lip->yyLength());

        lip->body_utf8_append(lip->m_cpp_text_start);

        lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
                                      lip->m_cpp_text_end);

        return (result_state);

涉及的状态转移规则如下:

当前状态字符类型变更状态
MY_LEX_IDENT_START多字节字符的字符串【返回】484(IDENT_QUOTED
单字节字符的字符串【返回】482(IDENT
当前 token 是多字节字符的字符串,且之后是 “. + SQL 字符”状态 = MY_LEX_IDENT_SEP;【返回】484(IDENT_QUOTED
当前 token 是单字节字符的字符串,且之后是 “. + SQL 字符”状态 = MY_LEX_IDENT_SEP;【返回】482(IDENT
  • 25
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

长行

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

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

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

打赏作者

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

抵扣说明:

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

余额充值