词法解析:自动机状态转移矩阵
源码位置:(版本 = 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_HEX | 在 x 或 X 之后(如果后续是 ' 则为十六进制值) |
MY_LEX_IDENT_OR_BIN | 在 b 或 B 之后(如果后续是 ' 则为二进制值) |
MY_LEX_IDENT_OR_NCHAR | 在 n 或 N 之后(如果后续是 ' 则为 Unicode 字符串) |
Step 5|为 $
符赋状态值
/* Special handling of '$' for dollar quoted strings */
state_map[u'$'] = MY_LEX_IDENT_OR_DOLLAR_QUOTED_TEXT;