这几周一直在研究怎么写编译器,算是这一年学花指令的最后学习了吧。这个写完之后,就专心学Win内核QAQ
目前还没有把编译器写完,所以现在只是先总结一下流程。
目前我遵循的流程是:
- 输入文本
- token流:将输入的文本解析为token字,它是最小的基本词汇,比如把+=视为1个符号,把abcd视为标识符。此处会进行词法检查。
- 抽象语法树(AST):将token流解析为抽象语法树,以语句块(节点)为基本单位,装载在不同函数块(节点)中。生成结束后,再进行语法检查。
- 三地址码:将抽象语法树按自顶向下的(从块到句)解析为三地址码,三地址码类似汇编,比如:a += b + c这个AST,会变为tmp = b+c,a = a + tmp,这种方式可以很方便地在下一步映射中间变量为寄存器。
- 汇编代码:三地址码转化为汇编基本只用实现寄存器初始化,使用和销毁。这一步可以和三地址码一起使用,也就是说可以省略三地址码,直接从抽象语法树转换为汇编。
生成token流
这一步需要通过读取文本,解析文本结构来实现输出token流,可以先对输入的内容分类:
1 2 3 4 5 6
| 字符:'' 字符串:"" 数字:数字开头纯数字,0x开头且字母小于F的hex值 关键字:控制流关键字(main,if,continue,break,return,等),类型关键字(void,int,dword,byte,proc,struct,等) 符号:运算符:(+,-,*,%,/,等),间隔符:((),{},[],;) 标识符:字母开头的不含运算符和间隔符的任意内容。且不与关键字重复。
|
那么就可以对以上内容进行顺序判断实现生成token流,因为是第一次写,所以类型写的比较混乱,QAQ之后有机会改一改
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class Token { public: std::string originaldata; ttoken tokenData; TYPE tokenType; Token(std::string data, ttoken type, TYPE type_) : originaldata(data), tokenData(type), tokenType(type_) { } ttoken type() const { return tokenData; } bool isType(ttoken type) const { return tokenData == type; } bool operator==(Token a) const { return tokenData == a.tokenData; } }; class Parser { private: std::vector<char> origalCode; std::vector<Token> TokenList;
public: Parser(std::string soureCodePath) { std::ifstream file(soureCodePath, std::ios::binary); if (!file.is_open()) throw std::runtime_error("文件打开失败"); file.seekg(0, std::ios::end); std::streamsize dataSize = file.tellg(); file.seekg(0, std::ios::beg); origalCode.resize(dataSize); if (!file.read(origalCode.data(), dataSize)) throw std::runtime_error("拷贝文件至缓冲区失败"); }
void lexicalAnalysis();
};
|
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
| namespace pomnet { namespace inner { char getNext(char*& pdata) { return pdata[1]; }
bool isDigit(char data) { if (data >= '0' && data <= '9') return true; return false; } bool isHex(char data) { if (isDigit(data)) return true; if (data >= 'a' && data <= 'f' || data >= 'A' && data <= 'F') return true; return false; }
bool isAlpha(char data) { if (data >= 'a' && data <= 'z' || data >= 'A' && data <= 'Z') return true; return false; }
bool notDigitAlpha(char data) { if (isAlpha(data) || isDigit(data)) return false; return true; } bool notDigitAlpha_(char data) { if (data == '_' || isAlpha(data) || isDigit(data)) return false; return true; }
bool isEndSentence(char data) { return data == ';'; } bool isBlockStart(char data) { return data == '{'; } bool isBlockEnd(char data) { return data == '}'; }
bool isNumberEndHex(char data) { if (!isHex(data) && (isAlpha(data) || data == '_')) { return false; } return true; } bool isNumberEnd(char data) { if (isAlpha(data) || data == '_') { return false; } return true; } }
std::string getMostData(char*& pdata) { using namespace inner; if (!isAlpha(*pdata)) return std::string(); std::string result; while (!notDigitAlpha_(*pdata)) { result += *pdata; pdata++; } return result; } std::string getMostNumber(char*& pdata) { using namespace inner;
if (!isDigit(*pdata)) return std::string(); std::string result;
if (pdata[0] == '0' && getNext(pdata) == 'x') { pdata += 2; result += "0x"; while (isHex(*pdata)) { result += *pdata; pdata++; } if (!isNumberEndHex(*pdata)) { throw std::runtime_error("数字未闭合"); } return result; } if (pdata[0] != '0') { while (isDigit(*pdata)) { result += *pdata; pdata++; } if (!isNumberEnd(*pdata)) { throw std::runtime_error("数字未闭合"); } return result; } pdata++; return result = "0"; }
std::string getMostString(char*& pdata) { using namespace inner; std::string result; if (*pdata == '"') { pdata++; result += '"'; while (*pdata != '"') { if (*pdata == '\\' && getNext(pdata) == '"') { pdata++; result += *pdata; pdata++; continue; } if (*pdata == '\\' && getNext(pdata) == '\\') { pdata++; result += *pdata; pdata++; continue; } result += *pdata; pdata++; } if (*pdata == '"') { result += '"'; pdata++; return result; } else { return std::string(); } } }
std::string getChar(char*& pdata) { using namespace inner; std::string result; if (*pdata == '\'') { pdata++; result += '\''; if (*pdata == '\\') { pdata++; result += *pdata; pdata++; } else { result += *pdata; pdata++; } if (*pdata != '\'') { return std::string(); } result += *pdata; pdata++; return result; } }
std::string getMostSymbol(char*& pdata) { using namespace inner; std::string result; auto targetKey = symbolMap.find(result + *pdata); if (targetKey == symbolMap.end()) return result; auto targetKey2 = symbolMap.find(result + *pdata + getNext(pdata)); if (targetKey2 == symbolMap.end()) { result += *pdata; pdata++; return result; } result += *pdata + getNext(pdata); pdata += 2; return result; }
Token generateToken(std::string data, TYPE type_) { switch (type_) { case identifier: { return Token(data, ID_TOKEN, identifier); } case keyword: { auto targetKey = keyWordMap.find(data); if (targetKey != keyWordMap.end()) { return Token(data, targetKey->second, keyword);
} return Token(data, BAD_TOKEN, keyword); } case type: { auto targetKey = typeMap.find(data); if (targetKey != typeMap.end()) { return Token(data, targetKey->second, type); } return Token(data, BAD_TOKEN, type); } case number: { return Token(data, NUM_TOKEN, number); } case string: { return Token(data, STR_TOKEN, string); } case character: { return Token(data, CHR_TOKEN, character); } case symbol: { auto targetKey = symbolMap.find(data); if (targetKey != symbolMap.end()) { return Token(data, targetKey->second, symbol); } return Token(data, BAD_TOKEN, symbol);
} case separator: { if (data == ";") return Token(data, SY_SEMI, separator); if (data == "{") return Token(data, SY_LBRACE, separator); if (data == "}") return Token(data, SY_RBRACE, separator); } }
} }
void Parser::lexicalAnalysis() { char* pdata = origalCode.data(); auto dataSize = origalCode.size(); if (dataSize == 0) { throw std::runtime_error("源文件为空"); } using namespace pomnet; while (pdata < origalCode.data() + dataSize) { if (inner::isDigit(*pdata)) { std::string number = getMostNumber(pdata); if (number.empty()) { throw std::runtime_error("数字未闭合"); } TokenList.push_back(generateToken(number, TYPE::number)); continue; } if (inner::isAlpha(*pdata)) {
std::string str = getMostData(pdata); if (keyWordMap.find(str) != keyWordMap.end()) { TokenList.push_back(generateToken(str, TYPE::keyword)); continue; } if (typeMap.find(str) != typeMap.end()) { TokenList.push_back(generateToken(str, TYPE::type)); continue; } TokenList.push_back(generateToken(str, TYPE::identifier)); continue; } if (inner::notDigitAlpha(*pdata)) { if (*pdata == ' ' || *pdata == '\t' || *pdata == '\n' || *pdata == '\r') { pdata++; continue; }
if (*pdata == '\'') { std::string chr = getChar(pdata); if (!chr.empty()) { TokenList.push_back(generateToken(chr, TYPE::character)); continue; } } if (*pdata == '"') { std::string str = getMostString(pdata); if (!str.empty()) { TokenList.push_back(generateToken(str, TYPE::string)); continue; } } if (inner::isEndSentence(*pdata)) { TokenList.push_back(generateToken(";", TYPE::separator)); pdata++; continue; } if (inner::isBlockEnd(*pdata)) { TokenList.push_back(generateToken("}", TYPE::separator)); pdata++; continue; } if (inner::isBlockStart(*pdata)) { TokenList.push_back(generateToken("{", TYPE::separator)); pdata++; continue; }
std::string symbol = getMostSymbol(pdata); if (symbol.empty()) { throw std::runtime_error("非符号"); } TokenList.push_back(generateToken(symbol, TYPE::symbol)); continue; } } }
|
通过以上内容,就可以生成token list
生成AST
这一步主要进行语法解析,所以是要对每个函数块的内容实现抽象描述。
从最基本的抽象开始:表达式
这里使用partt表达式解析方法。
在一次解析中,先解析符号是否是前缀运算符,再解析符号是否是中缀运算符,最后判断是否是后缀运算符。
解析符号生成AST时,使用固定的getGenerateFunc解析函数传入不同的token类型返回生成对应节点的函数
因此只需要递归调用主函数并在getGenerateFunc返回的函数中实现生成AST即可。
[未完待续]