感谢vczh轮子叔的坑了的教程,我的编译原理第一次入了个门,词法分析写完了,今后可以看看书继续往下学了。
http://www.cppblog.com/vczh/archive/2014/03/02/206014.html
词法分析,就是对于一段代码,把他们分割成一个个的token,同时记录他们的行列号,丢掉不必要的信息,这个词法分析器很简单,简单的状态机就能胜任,用正则就没有自己造轮子的快感了,所以要自己手撸状态机拆token出来。
模仿vczh的语言,我的语言包括了以下要素
标识符:大小写字母和下划线构成的连续串[a-zA-Z_]
数字:自然书写的整数和浮点数,整数部分为0的浮点数必须要用0.开头
运算符:单个符号
注释:#开头,#或\n结尾的任意内容,也就是说注释最多是一整行
字符串:双引号"开头结尾的任意内容,支持\\,\n两种转义字符。
这样简单的语言,用正则表达式处理也很简单,但是为了贯彻自己造轮子的原则,我们手写一波状态机
首先把状态机的图画出来,就像这样(因为没有钱买visio,所以只能用一个叫lucidchart的在线应用画图了)
虚线表示这个状态可以直接跳到End状态,而所谓End状态就是Start状态,所以到了有虚线的状态,可以看情况直接跳到Start,并且有些状态也可以直接优化掉,比如InStringEnd, InOperator等,遇到这样的状态,直接输出一个token,然后把state设为Start就好了。
这样的状态机是不是很简单,更加复杂的一些状态,也可以手动构造正则表达式,然后用vczh的另一篇教程手写出状态机来。
忘了说Token的结构了,这个很简单
struct Token { int line; int column; TokenType type; std::wstring value; Token(): Token(0, 0, TokenType::Unknown, L"", 0) { } Token(int _line, int _column, TokenType _type, const wchar_t* str, int len) : line(_line) , column(_column) , type(_type) , value(str, len) { } void Reset() { line = 0; column = 0; type = TokenType::Unknown; value.clear(); } void Push(const wchar_t ch) { value.push_back(ch); } };
那个Reset和Push纯粹是因为我偷懒,把这个第一个版本的代码写得很烂,只能用这种不科学的封装来减少一下代码的重复了。
有了状态机和具体的状态:
enum class TokenType { Unknown, Number, Comment, Operator, Identifier, String }; enum class ParseState { Begin, InComment, //InCommentEnd, InInteger, InFloat, InOperator, InIdentifier, InString, InStringEscaping, //InStringEnd };
可以构造状态机了,模板是这样的:
while (*reading) { switch (state) { case 某个状态: { switch (*reading) { case 某个或某几个字符: #根据状态机里当前状态能接受和不能接受的字符,修改新的状态,读入字符,reading++,或者直接抛异常 break; } } }
第一个版本的代码写得很烂,纯粹是能用的程序,放在这里纯粹是显摆一下,之后我会考虑重构什么的,然后再来更新这篇文章,而且没有写单元测试,正确性纯粹是肉眼检查,报错信息也很不友善,这一点下一个版本的代码再改。
下面贴代码:
TokenStream.h
1 #pragma once 2 #ifndef TOKEN_STREAM 3 #define TOKEN_STREAM 4 5 #include <string> 6 #include <vector> 7 enum class TokenType 8 { 9 Unknown, 10 Number, 11 Comment, 12 Operator, 13 Identifier, 14 String 15 }; 16 17 enum class ParseState 18 { 19 Begin, 20 InComment, 21 //InCommentEnd, 22 InInteger, 23 InFloat, 24 InOperator, 25 InIdentifier, 26 InString, 27 InStringEscaping, 28 //InStringEnd 29 }; 30 struct Token 31 { 32 int line; 33 int column; 34 TokenType type; 35 std::wstring value; 36 Token(): Token(0, 0, TokenType::Unknown, L"", 0) 37 { 38 } 39 Token(int _line, int _column, TokenType _type, const wchar_t* str, int len) 40 : line(_line) 41 , column(_column) 42 , type(_type) 43 , value(str, len) 44 { 45 } 46 void Reset() 47 { 48 line = 0; 49 column = 0; 50 type = TokenType::Unknown; 51 value.clear(); 52 } 53 void Push(const wchar_t ch) 54 { 55 value.push_back(ch); 56 } 57 }; 58 59 class TokenStream 60 { 61 public: 62 static std::vector<Token> Parse(std::wstring& source) 63 { 64 const wchar_t* reading = source.c_str(); 65 ParseState state = ParseState::Begin; 66 std::vector<Token> tokenList; 67 int lineNumber = 1; 68 int columnNumber = 1; 69 Token token; 70 while (*reading) 71 { 72 switch (state) 73 { 74 case ParseState::Begin: 75 { 76 switch (*reading) 77 { 78 case‘+‘:case‘-‘:case‘*‘:case‘/‘:case‘(‘:case‘)‘:case‘<‘:case‘>‘:case‘=‘:case‘;‘: 79 case‘^‘:case‘&‘:case‘.‘: 80 { 81 tokenList.push_back(Token(lineNumber, columnNumber, TokenType::Operator, reading, 1)); 82 token.Reset(); 83 reading++; 84 columnNumber++; 85 break; 86 } 87 case‘0‘:case‘1‘:case‘2‘:case‘3‘:case‘4‘:case‘5‘:case‘6‘:case‘7‘:case‘8‘:case‘9‘: 88 { 89 state = ParseState::InInteger; 90 token.type = TokenType::Number; 91 token.column = columnNumber; 92 token.line = lineNumber; 93 token.Push(*reading); 94 reading++; 95 columnNumber++; 96 break; 97 } 98 case‘a‘:case‘b‘:case‘c‘:case‘d‘:case‘e‘:case‘f‘:case‘g‘:case‘h‘:case‘i‘:case‘j‘:case‘k‘:case‘l‘:case‘m‘:case‘n‘:case‘o‘:case‘p‘:case‘q‘:case‘r‘:case‘s‘: 99 case‘t‘:case‘u‘:case‘v‘:case‘w‘:case‘x‘:case‘y‘:case‘z‘:case‘A‘:case‘B‘:case‘C‘:case‘D‘:case‘E‘:case‘F‘:case‘G‘:case‘H‘:case‘I‘:case‘J‘:case‘K‘:case‘L‘: 100 case‘M‘:case‘N‘:case‘O‘:case‘P‘:case‘Q‘:case‘R‘:case‘S‘:case‘T‘:case‘U‘:case‘V‘:case‘W‘:case‘X‘:case‘Y‘:case‘Z‘:case‘_‘: 101 { 102 state = ParseState::InIdentifier; 103 token.type = TokenType::Identifier; 104 token.column = columnNumber; 105 token.line = lineNumber; 106 token.Push(*reading); 107 reading++; 108 columnNumber++; 109 break; 110 } 111 case‘#‘: 112 { 113 state = ParseState::InComment; 114 token.type = TokenType::Comment; 115 token.column = columnNumber; 116 token.line = lineNumber; 117 reading++; 118 columnNumber++; 119 break; 120 } 121 case‘"‘: 122 { 123 state = ParseState::InString; 124 token.type = TokenType::String; 125 token.column = columnNumber; 126 token.line = lineNumber; 127 reading++; 128 columnNumber++; 129 break; 130 } 131 case‘\n‘: 132 { 133 reading++; 134 lineNumber++; 135 columnNumber = 1; 136 break; 137 } 138 case‘ ‘: 139 { 140 reading++; 141 columnNumber++; 142 break; 143 } 144 default: 145 { 146 throw std::exception("parse error"); 147 break; 148 } 149 } 150 break; 151 } //case ParseState::Begin 152 case ParseState::InInteger: 153 { 154 switch (*reading) 155 { 156 case‘0‘:case‘1‘:case‘2‘:case‘3‘:case‘4‘:case‘5‘:case‘6‘:case‘7‘:case‘8‘:case‘9‘: 157 { 158 token.Push(*reading); 159 reading++; 160 columnNumber++; 161 break; 162 } 163 case‘.‘: 164 { 165 state = ParseState::InFloat; 166 token.type = TokenType::Number; 167 token.Push(*reading); 168 reading++; 169 columnNumber++; 170 break; 171 } 172 default: 173 { 174 tokenList.push_back(token); 175 token.Reset(); 176 state = ParseState::Begin; 177 break; 178 } 179 } 180 break; 181 } //case ParseState::InInteger 182 case ParseState::InComment: 183 { 184 if (*reading == ‘\n‘ || *reading == ‘#‘) 185 { 186 //state = ParseState::InCommentEnd; 187 if (*reading == ‘\n‘) 188 { 189 lineNumber++; 190 } 191 reading++; 192 columnNumber = 1; 193 tokenList.push_back(token); 194 token.Reset(); 195 state = ParseState::Begin; 196 } 197 else 198 { 199 token.Push(*reading); 200 reading++; 201 columnNumber++; 202 } 203 break; 204 } //ParseState::InComment 205 case ParseState::InIdentifier: 206 { 207 switch (*reading) 208 { 209 case‘a‘:case‘b‘:case‘c‘:case‘d‘:case‘e‘:case‘f‘:case‘g‘:case‘h‘:case‘i‘:case‘j‘:case‘k‘:case‘l‘:case‘m‘:case‘n‘:case‘o‘:case‘p‘:case‘q‘:case‘r‘:case‘s‘: 210 case‘t‘:case‘u‘:case‘v‘:case‘w‘:case‘x‘:case‘y‘:case‘z‘:case‘A‘:case‘B‘:case‘C‘:case‘D‘:case‘E‘:case‘F‘:case‘G‘:case‘H‘:case‘I‘:case‘J‘:case‘K‘:case‘L‘: 211 case‘M‘:case‘N‘:case‘O‘:case‘P‘:case‘Q‘:case‘R‘:case‘S‘:case‘T‘:case‘U‘:case‘V‘:case‘W‘:case‘X‘:case‘Y‘:case‘Z‘:case‘_‘: 212 { 213 token.Push(*reading); 214 reading++; 215 columnNumber++; 216 break; 217 } 218 default: 219 { 220 tokenList.push_back(token); 221 token.Reset(); 222 state = ParseState::Begin; 223 break; 224 } 225 } 226 break; 227 } //ParseState::InIdentifier 228 case ParseState::InString: 229 { 230 switch (*reading) 231 { 232 case ‘\\‘: 233 { 234 state = ParseState::InStringEscaping; 235 reading++; 236 columnNumber++; 237 break; 238 } 239 case ‘"‘: 240 { 241 //state = ParseState::InStringEnd; 242 reading++; 243 columnNumber++; 244 tokenList.push_back(token); 245 token.Reset(); 246 state = ParseState::Begin; 247 break; 248 } 249 case ‘\n‘: 250 { 251 throw std::exception("parse error in string"); 252 break; 253 } 254 default: 255 { 256 token.Push(*reading); 257 reading++; 258 columnNumber++; 259 break; 260 } 261 } 262 break; 263 } // ParseState::InString 264 case ParseState::InFloat: 265 { 266 switch (*reading) 267 { 268 case‘0‘:case‘1‘:case‘2‘:case‘3‘:case‘4‘:case‘5‘:case‘6‘:case‘7‘:case‘8‘:case‘9‘: 269 { 270 token.type = TokenType::Number; 271 token.Push(*reading); 272 reading++; 273 columnNumber++; 274 break; 275 } 276 default: 277 { 278 tokenList.push_back(token); 279 token.Reset(); 280 state = ParseState::Begin; 281 break; 282 } 283 } 284 break; 285 } //ParseState::InFloat 286 case ParseState::InStringEscaping: 287 { 288 switch (*reading) 289 { 290 case ‘n‘: 291 { 292 state = ParseState::InString; 293 token.Push(‘\n‘); 294 reading++; 295 lineNumber++; 296 break; 297 } 298 case ‘\\‘: 299 { 300 state = ParseState::InString; 301 token.Push(‘\\‘); 302 reading++; 303 lineNumber++; 304 break; 305 } 306 case ‘"‘: 307 { 308 state = ParseState::InString; 309 token.Push(‘"‘); 310 reading++; 311 lineNumber++; 312 break; 313 } 314 default: 315 { 316 throw std::exception("parse error in escaping"); 317 break; 318 } 319 } 320 break; 321 } //ParseState::InStringEscaping 322 } //switch state 323 } 324 return tokenList; 325 } 326 }; 327 328 #endif // !TOKEN_STREAM
300行的大函数,我也是醉了