数据库项目目前实现了一个简单的词法分析器,其中就是读取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.