MySQL 源码|词法解析:词法解析器调用方法
源码位置:(版本 = MySQL 8.0.37)
- router/src/routing/src/sql_lexer.cc
next_token()
lex_one_token()
函数在 SqlLexer::iterator::Token SqlLexer::iterator::next_token()
函数中被调用:
SqlLexer::iterator::Token SqlLexer::iterator::next_token() {
const auto token_id = lex_one_token(&st, session_);
return {get_token_text(token_id), token_id};
}
其中的 token_id 即 lex_one_token
的 int
类型返回值。接着查看 get_token_text(token_id)
的逻辑:
std::string_view SqlLexer::iterator::get_token_text(TokenId token_id) const {
auto &lip = session_->m_parser_state->m_lip;
auto raw_token = std::string_view{
lip.get_tok_start(),
static_cast<size_t>(lip.get_ptr() - lip.get_tok_start())};
if (token_id == END_OF_INPUT) {
return {"\0", 1};
} else if (token_id == 0) { // YYEOF
return {};
} else if (token_id < 256) { // 0-255 are plain ASCII characters
return raw_token;
} else if (token_id == IDENT) {
// in 'SET @@SESSION.timestamp' 'timestamp' is a IDENT
// in 'SET SESSION timestamp' 'timestamp' is a keyword
return to_string_view(st.lex_str);
} else if (is_keyword_or_func(raw_token.data(), raw_token.size())) {
return {st.keyword.str, st.keyword.length};
} else {
return to_string_view(st.lex_str);
}
}
get_token_text()
函数返回 std::string_view
类型,该类型包含两个成员,一个是指向字符串数据的指针和一个表示长度的整数。根据不同的 token_id
,get_token_text(TokenId token_id)
的返回结果如下:
-
token_id
为 411(END_OF_INPUT
)时,则返回长度为 0 字符串; -
token_id
为 0 时,返回空结果; -
token_id
小于 256,即为 ASCII 字符时,返回当前 token 的字符串 -
token_id
为 482(IDENT
)时,返回使用Lexer_yystype
类型SqlLexer -> st
的lex_str
构造的字符串 - 否则,如果 token 的字符串是关键字或函数,则返回该关键字和函数的字符串
- 否则,返回使用
Lexer_yystype
类型SqlLexer -> st
的lex_str
构造的字符串
next_token()
函数的调用位置
下面来看 next_token()
被调用的位置。
-
SqlLexer::iterator
类的构造函数,在初始化时将成员变量token_
初始化为next_token
函数的string_view
类型返回值。
SqlLexer::iterator::iterator(THD *session) : session_(session) {
if (session_) {
// init the first token
token_ = next_token();
}
}
-
SqlLexer::iterator
类重载实现后置递增运算符的函数,将next_token
函数的string_view
类型返回值返回。
SqlLexer::iterator SqlLexer::iterator::operator++(int) {
// the last token is END_OF_INPUT, +1 is past the "end()"
if (is_final_token(token_)) {
return {nullptr};
}
return {session_, next_token()};
}
-
SqlLexer::iterator
类重载实现前置递增运算符的函数,将成员变量token_
置为next_token
函数的string_view
类型返回值。
SqlLexer::iterator &SqlLexer::iterator::operator++() {
// the last token is END_OF_INPUT, +1 is past the "end()"
if (is_final_token(token_)) {
token_ = {};
} else {
token_ = next_token();
}
return *this;
}
SqlLexer
类
可以看到,next_token()
函数被用来实现 SqlLexer::iterator
类的初始化和递增运算,下面我们来看 SqlLexer
的实现逻辑:
class SqlLexer {
public:
using TokenId = int;
SqlLexer(THD *session);
class iterator {
public:
struct Token {
std::string_view text;
TokenId id;
};
using lexer_state = Lexer_yystype;
using value_type = Token;
using pointer = value_type *;
using const_pointer = const value_type *;
iterator(THD *session);
iterator(THD *session, Token token)
: session_(session), token_{std::move(token)} {}
value_type operator*() const { return token_; }
pointer operator->() { return &token_; }
const_pointer operator->() const { return &token_; }
iterator operator++(int);
iterator &operator++();
friend bool operator==(const iterator &a, const iterator &b);
friend bool operator!=(const iterator &a, const iterator &b);
private:
Token next_token();
std::string_view get_token_text(TokenId token_id) const;
THD *session_;
lexer_state st;
Token token_;
};
iterator begin() { return iterator(session_); }
iterator end() { return iterator(nullptr); }
private:
THD *session_;
};
到这里,我们基本已经了解了词法解析器的使用方法,下面开始梳理词法解析器的具体逻辑。