MySQL 源码|9 - 词法解析:自动机状态转移矩阵

词法解析:自动机状态转移矩阵

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

lex_one_token 函数中,初始化了状态转移矩阵 my_lkex_states,该状态转移矩阵主要用于在开始匹配 token 时,根据当前字符获取状态。

const my_lex_states *state_map = cs->state_maps->main_map;

该矩阵初始化于 sql/sql_chars.cc 文件的 bool init_state_maps(MY_CHARSET_LOADER *loader, CHARSET_INFO *cs) 函数中实现初始化。

Step 1|初始化结构体 lex_state_maps_st,并令 state_map 指针指向结构体中的 main_map

  enum my_lex_states *state_map = nullptr;

  // This character set has already been initialized.
  if (cs->state_maps != nullptr && cs->ident_map != nullptr) return false;

  lex_state_maps_st *lex_state_maps = static_cast<lex_state_maps_st *>(
      loader->once_alloc(sizeof(lex_state_maps_st)));

  if (lex_state_maps == nullptr) return true;  // OOM

  cs->state_maps = lex_state_maps;
  state_map = lex_state_maps->main_map;

结构体定义如下:

struct lex_state_maps_st {
  enum my_lex_states main_map[256];
  enum hint_lex_char_classes hint_map[256];
};

Step 2|将结构体中的每个枚举值赋对应的状态值

  for (unsigned i = 0; i < 256; i++) {
    if (my_isalpha(cs, i))
      state_map[i] = MY_LEX_IDENT;
    else if (my_isdigit(cs, i))
      state_map[i] = MY_LEX_NUMBER_IDENT;
    else if (my_ismb1st(cs, i))
      /* To get whether it's a possible leading byte for a charset. */
      state_map[i] = MY_LEX_IDENT;
    else if (my_isspace(cs, i))
      state_map[i] = MY_LEX_SKIP;
    else
      state_map[i] = MY_LEX_CHAR;
  }
  • my_isalpha:判断是否为大写字母或小写字母
  • my_isdigit:判断是否为数字
  • my_ismb1st:判断是否为组多字节字符集中字符的开头字节
  • my_isspace:判断是否为空格

通过上述逻辑,可知以下状态含义:

枚举值枚举值含义
MY_LEX_IDENT在普通语法元素中(之后已有一个大写字母、小写字母或多字节字符集字符)
MY_LEX_NUMBER_IDENT在数字元素中(之前已经有了一个整数)
MY_LEX_SKIP在空格之后(之前已经有了一个空格)

Step 3|为一些特殊的字符赋状态值

  state_map[u'_'] = state_map[u'$'] = MY_LEX_IDENT;
  state_map[u'\''] = MY_LEX_STRING;
  state_map[u'.'] = MY_LEX_REAL_OR_POINT;
  state_map[u'>'] = state_map[u'='] = state_map[u'!'] = MY_LEX_CMP_OP;
  state_map[u'<'] = MY_LEX_LONG_CMP_OP;
  state_map[u'&'] = state_map[u'|'] = MY_LEX_BOOL;
  state_map[u'#'] = MY_LEX_COMMENT;
  state_map[u';'] = MY_LEX_SEMICOLON;
  state_map[u':'] = MY_LEX_SET_VAR;
  state_map[0] = MY_LEX_EOL;
  state_map[u'/'] = MY_LEX_LONG_COMMENT;
  state_map[u'*'] = MY_LEX_END_LONG_COMMENT;
  state_map[u'@'] = MY_LEX_USER_END;
  state_map[u'`'] = MY_LEX_USER_VARIABLE_DELIMITER;
  state_map[u'"'] = MY_LEX_STRING_OR_DELIMITER;

通过上述逻辑,可知以下状态含义:

枚举值枚举值含义
MY_LEX_STRING在单引号之后
MY_LEX_REAL_OR_POINT. 符号之后
MY_LEX_CMP_OP在比较运算符之后(在 >=! 之后)
MY_LEX_LONG_CMP_OP在比较运算符之后(在 < 之后)
MY_LEX_BOOL在布尔运算符之后(在 & 或 `
MY_LEX_COMMENT在单行注释中(在 # 之后)
MY_LEX_SEMICOLON在分号之后
MY_LEX_SET_VAR在冒号之后
MY_LEX_EOL在 ASCII 值为 0 的字节之后
MY_LEX_LONG_COMMENT在长注释符号的开始位置的第一个字符(在 / 之后)
MY_LEX_END_LONG_COMMENT在长注释符号的结束位置的第一个字符(在 * 之后)
MY_LEX_USER_END@ 符号之后
MY_LEX_USER_VARIABLE_DELIMITER在用户自定义变量引号之后(在 ` 引号之后)
MY_LEX_STRING_OR_DELIMITER在双引号之后

Step 4|为十六进制值、二进制值和 unicode 字符串赋状态值

  /* Special handling of hex and binary strings */
  state_map[u'x'] = state_map[u'X'] = MY_LEX_IDENT_OR_HEX;
  state_map[u'b'] = state_map[u'B'] = MY_LEX_IDENT_OR_BIN;
  state_map[u'n'] = state_map[u'N'] = MY_LEX_IDENT_OR_NCHAR;

通过上述逻辑,可知以下状态含义:

枚举值枚举值含义
MY_LEX_IDENT_OR_HEXxX 之后(如果后续是 ' 则为十六进制值)
MY_LEX_IDENT_OR_BINbB 之后(如果后续是 ' 则为二进制值)
MY_LEX_IDENT_OR_NCHARnN 之后(如果后续是 ' 则为 Unicode 字符串)

Step 5|为 $ 符赋状态值

  /* Special handling of '$' for dollar quoted strings */
  state_map[u'$'] = MY_LEX_IDENT_OR_DOLLAR_QUOTED_TEXT;
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

长行

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

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

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

打赏作者

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

抵扣说明:

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

余额充值