数据库项目目前实现了一个简单的词法分析器,其中就是读取SQL语句中的字符,把他们转换成相应的Token。
项目发布在GitHub上,可以查看代码。https://github.com/DyingDown/Database/tree/main
Token
Token就是不可以再分割的最小语素,里面保存Token的类型,和Token的内容。
1 2 3 4 5 6 7 8 9 10 11
| class Token { public: int type; std::string value; Token(int type, std::string value); };
Token::Token(int type, std::string value) { this->type = type; this->value = std::move(value); }
|
Token的类型
目前针对SQL语句,我就设计了几种简单的类型:int,float,string,保留字,变量名(ID),符号,非法,结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class TokenType { public: enum TokenTypes{ INT, STRING, FLOAT, ID, ADD, ... WHERE,
L_BRACKET, R_BRACKET, ASTERISK, SEMICOLON, COMMA, PLUS, MINUS, DIVISION, GREATER_THAN, LESS_THAN, GREATER_EQUAL_TO, LESS_EQUAL_TO, EQUAL, NOT_EQUAL, ILLEGAL, END }; };
|
分割Token
一个一个读取字符,按照空格(‘\n’, ' ', '\t'
)来分割字符,或者有时候()
也可以分割。
这部分代码不难,就是比较繁琐,需要多个if 来判断,
也用一个类来保存这个功能。
由于这个词法分析,每次只读取一个Token,由外部(语法分析器)来多次驱动才能将所有的字符分割完全。所以,需要有一个全局变量来记录当前处理到哪个位置的字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class LexicalAnalyzer { private: Token getNumber(); Token getWords(); Token getString(); Token getPunct(); int currentPosition; bool isSpace(char a); public: std::string sql_str; int sql_len; Token scanNextTokens(); LexicalAnalyzer(std::string content); };
|
其中,最主要的函数就是 读取下一个Token函数,其他的函数都会通过这个来驱动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Token LexicalAnalyzer::scanNextTokens() { while(currentPosition < sql_len and isSpace(sql_str[currentPosition])) { currentPosition ++; } Token currentToken(TokenType::ILLEGAL, ""); if(currentPosition >= sql_len) { currentToken = Token(TokenType::END, ""); } else if(sql_str[currentPosition] == '\"') { currentToken = getString(); } else if(isdigit(sql_str[currentPosition])) { currentToken = getNumber(); } else if(isalpha(sql_str[currentPosition])) { currentToken = getWords(); } else { currentToken = getPunct(); } return currentToken; }
|
测试
目前还没有学习如何测试代码,所以暂时先写在了main里面
1 2 3 4 5 6 7 8 9 10 11 12
| int main() { InputBuffer a = InputBuffer(); a.read_input(); LexicalAnalyzer b = LexicalAnalyzer(a.input_buffer); Token t = b.scanNextTokens(); while(t.type != TokenType::END) { std::cout << t.value<< " " << t.type << std::endl; t = b.scanNextTokens(); } std::cout << t.value << std::endl; return 0; }
|
Author: o_oyao
License: All articles in this blog are licensed under
CC BY-NC-SA 4.0 unless stating additionally.