前言:关于词法分析的基础知识的介绍可以看一下这篇博客,我再累述估计也不会有这篇讲的清楚QAQ。 https://www.cnblogs.com/yanlingyin/archive/2012/04/17/2451717.html 默认大家已经对词法分析有了基本的了解了。
一:下面讨论PL/0语言的词法分析器的单词结构
1、关键字
关键字(共11个):空格分隔列表如下
begin end if then while do const var call procedure odd
2、运算符和界符
算符和界符(14个):空格分隔列表如下
+ - * / = # < > := ( ) , . ;
3、标示符
PL/0语言中标示符的定义为:开头可以是下划线或字母,后面可以是下划线、字母或数字。
4、常数
整形数和浮点数
5、空白符
PL/0语言中的空白符有:空格符、制表符、换行符,这些空白符在词法分析阶段可以被忽略。
6、注释
PL/0语言中的注释形式为//,可以多行注释(*…*)。
二:PL/0的语言的词法分析器将要完成以下工作
(1) 跳过分隔符(如空格,回车,制表符);
(2) 识别诸如begin,end,if,while等关键字;
(3) 识别非关键字的一般标识符。
(4) 识别常数数字序列。
(5) 识别前面列出的单字符操作符和:=双字符特殊符号。
(6)词法分析器的输出形式(种别,属性值)其中:种别在“2、单词的种别”中进行了定义;
属性值:若单词种别只代表唯一单词,属性值为空;
若单词种别是标识符,为该单词在标识符表中的位置;
若单词种别是常数,属性值为对应常数值。
三:代码实现
测试程序如下(我放置的路径为D:/b.txt,根据自己情况自行修改)
1 // PL/0 语法示例程序 2 3 (* 4 计算1~10的阶乘 5 多行注释 6 *) 7 8 var n, f; 9 begin 10 n := 0; 11 f := 1; 12 while n # 10 do 13 begin 14 n := n + 1; 15 f := f * n; 16 end; 17 call print;// 用于输出结果,假设预先声明 18 end.
种别码如下(我放置的路径为D:/a.txt,根据自己情况自行修改)
1 begin 1 2 end 2 3 if 3 4 then 4 5 while 5 6 do 6 7 const 7 8 var 8 9 call 9 10 procedure 10 11 odd 11 12 + 12 13 - 13 14 * 14 15 / 15 16 = 16 17 # 17 18 < 18 19 > 19 20 := 20 21 ( 21 22 ) 22 23 , 23 24 . 24 25 ; 25 26 (*多行注释*) 26 27 //单行注释 27 28 常数 28 29 标识符 29
实现代码如下
1 // pL/0语言词法分析器 2 #include<bits/stdc++.h> 3 using namespace std; 4 struct _2tup 5 { 6 string token; 7 int id; 8 }; 9 bool is_blank(char ch) 10 { 11 return ch == ' ' || ch == ' ';//空格或控制字符 12 } 13 bool gofor(char& ch, string::size_type& pos, const string& prog)//返回指定位置的字符 14 { 15 ++pos; 16 if (pos >= prog.size()) 17 { 18 return false; 19 } 20 else 21 { 22 ch = prog[pos]; 23 return true; 24 } 25 } 26 27 _2tup scanner(const string& prog, string::size_type& pos, const map<string, int>& keys, int& row) 28 { 29 /* 30 if 31 标示符 32 else if 33 数字 34 else 35 符号 36 */ 37 _2tup ret; 38 string token; 39 int id = 0; 40 41 char ch; 42 ch = prog[pos]; 43 44 while(is_blank(ch)) 45 { 46 ++pos; 47 ch = prog[pos]; 48 } 49 // 判断标示符、关键字 50 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_') 51 { 52 //保证读取一个单词 53 while((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_') 54 { 55 token += ch;//追加标示符、关键字 56 if (!gofor(ch, pos, prog)) 57 { 58 break; 59 } 60 } 61 // 这里先看做都是其他标示符 62 id = keys.size(); 63 64 // 验证是否是关键字 65 map<string, int>::const_iterator cit = keys.find(token);//根据string类型的token返回int类型的id赋值给cit 66 if (cit != keys.end()) 67 { 68 id = cit->second;//此时是关键字,记录他的id 69 } 70 } 71 // 识别常数 72 else if ((ch >= '0' && ch <= '9') || ch == '.') 73 { 74 while (ch >= '0' && ch <= '9' || ch == '.') 75 { 76 token += ch; 77 if (!gofor(ch, pos, prog)) 78 { 79 break; 80 } 81 } 82 id = keys.size() - 1; 83 int dot_num = 0; 84 for (string::size_type i = 0; i != token.size(); ++i) 85 { 86 if (token[i] == '.') 87 { 88 ++dot_num; 89 } 90 } 91 if (dot_num > 1) 92 { 93 id = -1; 94 } 95 } 96 else 97 { 98 map<string, int>::const_iterator cit; 99 switch (ch) 100 { 101 case '-': // - 操作符 102 token += ch; 103 if (gofor(ch, pos, prog)) 104 { 105 if (ch == '-' || ch == '=' || ch == '>') // -- 操作符 106 { 107 token += ch; 108 gofor(ch, pos, prog); 109 } 110 } 111 cit = keys.find(token); 112 if (cit != keys.end()) 113 { 114 id = cit->second; 115 } 116 break; 117 case ':': 118 token += ch; 119 if (gofor(ch, pos, prog)) 120 { 121 if (ch == '=') // -- 操作符 122 { 123 token += ch; 124 gofor(ch, pos, prog); 125 } 126 } 127 cit = keys.find(token); 128 if (cit != keys.end()) 129 { 130 id = cit->second; 131 } 132 break; 133 134 case '=': 135 token += ch; 136 if (gofor(ch, pos, prog)) 137 { 138 if (ch == '=') // !% %= 操作符 139 { 140 token += ch; 141 gofor(ch, pos, prog); 142 } 143 } 144 cit = keys.find(token); 145 if (cit != keys.end()) 146 { 147 id = cit->second; 148 } 149 break; 150 151 case '/': // / 操作符 152 token += ch; 153 if (gofor(ch, pos, prog)) 154 { 155 if (ch == '=') // /= 操作符 156 { 157 token += ch; 158 gofor(ch, pos, prog); 159 } 160 else if (ch == '/') // 单行注释 161 { 162 token += ch; 163 ++pos; 164 while (pos < prog.size()) 165 { 166 ch = prog[pos]; 167 if (ch == '\n') 168 { 169 break; 170 } 171 token += ch; 172 ++pos; 173 } 174 if (pos >= prog.size()) 175 { 176 ; 177 } 178 else 179 { 180 ; 181 } 182 id = keys.size() - 2; 183 break; 184 } 185 else if (ch == '*') // 注释 186 { 187 token += ch; 188 if (!gofor(ch, pos, prog)) 189 { 190 token += "\n!!!注释错误!!!"; 191 id = -10; 192 break; 193 } 194 if (pos + 1 >= prog.size()) 195 { 196 token += ch; 197 token += "\n!!!注释错误!!!"; 198 id = -10; 199 break; 200 } 201 char xh = prog[pos + 1]; 202 while (ch != '*' || xh != '/') 203 { 204 token += ch; 205 if (ch == '\n') 206 { 207 ++row; 208 } 209 //++pos; 210 if (!gofor(ch, pos, prog)) 211 { 212 token += "\n!!!注释错误!!!"; 213 id = -10; 214 ret.token = token; 215 ret.id = id; 216 return ret; 217 } 218 //ch = prog[pos]; 219 if (pos + 1 >= prog.size()) 220 { 221 token += ch; 222 token += "\n!!!注释错误!!!"; 223 id = -10; 224 ret.token = token; 225 ret.id = id; 226 return ret; 227 } 228 xh = prog[pos + 1]; 229 } 230 token += ch; 231 token += xh; 232 pos += 2; 233 ch = prog[pos]; 234 id = keys.size() - 2; 235 break; 236 } 237 } 238 cit = keys.find(token); 239 if (cit != keys.end()) 240 { 241 id = cit->second; 242 } 243 break; 244 case '+': 245 token += ch; 246 cit = keys.find(token); 247 if (cit != keys.end()) 248 { 249 id = cit->second; 250 } 251 gofor(ch, pos, prog); 252 break; 253 254 case '<': 255 token += ch; 256 if (gofor(ch, pos, prog)) 257 { 258 if (ch == '<') 259 { 260 token += ch; 261 if (gofor(ch, pos, prog)) 262 { 263 if (ch == '=') 264 { 265 token += ch; 266 gofor(ch, pos, prog); 267 } 268 } 269 } 270 else if (ch == '=') 271 { 272 token += ch; 273 gofor(ch, pos, prog); 274 } 275 } 276 cit = keys.find(token); 277 if (cit != keys.end()) 278 { 279 id = cit->second; 280 } 281 break; 282 283 case '>': 284 token += ch; 285 if (gofor(ch, pos, prog)) 286 { 287 if (ch == '>') 288 { 289 token += ch; 290 if (gofor(ch, pos, prog)) 291 { 292 if (ch == '=') 293 { 294 token += ch; 295 gofor(ch, pos, prog); 296 } 297 } 298 } 299 else if (ch == '=') 300 { 301 token += ch; 302 gofor(ch, pos, prog); 303 } 304 } 305 cit = keys.find(token); 306 if (cit != keys.end()) 307 { 308 id = cit->second; 309 } 310 break; 311 case '(': // / 操作符 312 token += ch; 313 if (gofor(ch, pos, prog)) 314 315 { 316 if (ch == '*') // 注释 317 { 318 token += ch; 319 if (!gofor(ch, pos, prog)) 320 { 321 token += "\n!!!注释错误!!!"; 322 id = -10; 323 break; 324 } 325 if (pos + 1 >= prog.size()) 326 { 327 token += ch; 328 token += "\n!!!注释错误!!!"; 329 id = -10; 330 break; 331 } 332 char xh = prog[pos + 1]; 333 while (ch != '*' || xh != ')') 334 { 335 token += ch; 336 if (ch == '\n') 337 { 338 ++row; 339 } 340 //++pos; 341 if (!gofor(ch, pos, prog)) 342 { 343 token += "\n!!!注释错误!!!"; 344 id = -10; 345 ret.token = token; 346 ret.id = id; 347 return ret; 348 } 349 //ch = prog[pos]; 350 if (pos + 1 >= prog.size()) 351 { 352 token += ch; 353 token += "\n!!!注释错误!!!"; 354 id = -10; 355 ret.token = token; 356 ret.id = id; 357 return ret; 358 } 359 xh = prog[pos + 1]; 360 } 361 token += ch; 362 token += xh; 363 pos += 2; 364 ch = prog[pos]; 365 id = keys.size() - 2; 366 break; 367 } 368 } 369 cit = keys.find(token); 370 if (cit != keys.end()) 371 { 372 id = cit->second; 373 } 374 break; 375 376 case '*': 377 token += ch; 378 cit = keys.find(token); 379 if (cit != keys.end()) 380 { 381 id = cit->second; 382 } 383 gofor(ch, pos, prog); 384 break; 385 386 case ',': 387 case '#': 388 case '.': 389 case ';': 390 token += ch; 391 gofor(ch, pos, prog); 392 //++pos; 393 //ch = prog[pos]; 394 cit = keys.find(token); 395 if (cit != keys.end()) 396 { 397 id = cit->second; 398 } 399 break; 400 401 case '\n': 402 token += "换行"; 403 ++pos; 404 ch = prog[pos]; 405 id = -2; 406 break; 407 default: 408 token += "错误"; 409 ++pos; 410 ch = prog[pos]; 411 id = -1; 412 break; 413 } 414 } 415 ret.token = token; 416 ret.id = id; 417 418 return ret; 419 } 420 421 void init_keys(const string& file, map<string, int>& keys)//读取单词符号和种别码 422 { 423 ifstream fin(file.c_str());//.c_str返回的是当前字符串的首地址 424 if (!fin) 425 { 426 cerr << file << " doesn't exist!" << endl;//cerr不经过缓冲而直接输出,一般用于迅速输出出错信息 427 // exit(1); 428 } 429 keys.clear();//清空map对象里面的内容 430 string line; 431 string key; 432 int id; 433 while (getline(fin, line))//这个函数接收两个参数:一个输入流对象和一个string对象,getline函数从输入流的下一行读取,并保存读取的内容到string中 434 { 435 istringstream sin(line);//istringstream sin(s);定义一个字符串输入流的对象sin,并调用sin的复制构造函数,将line中所包含的字符串放入sin 对象中! 436 sin >> key >> id;//读取里面的字符串每一行一个key id 437 keys[key] = id; 438 } 439 } 440 441 void read_prog(const string& file, string& prog){//读取代码,并追加到prog上 442 ifstream fin(file.c_str()); 443 if (!fin) 444 { 445 cerr << file << " error!" << endl; 446 // exit(2); 447 } 448 prog.clear(); 449 string line; 450 while (getline(fin, line)) 451 { 452 prog += line + '\n'; 453 } 454 } 455 int main() 456 { 457 map<string, int> keys; 458 init_keys("D:/Test/a.txt", keys); 459 460 string prog; 461 read_prog("D:/Test/b.txt", prog); 462 463 vector< _2tup > tups; 464 string token, id; 465 466 string::size_type pos = 0;//size_type属于string标准库,作用可看做相当于unsigned·int 467 int row = 1; 468 469 _2tup tu; 470 cout << "------------------" << "要分析的代码如下"<< "---------------" << endl; 471 cout << prog << endl << endl; 472 473 // prog += "#"; // 标识终止,其实可以检测 pos 来判别是否终止 474 475 int no = 0; 476 cout << "------------------" << "分析的结果如下如下"<< "---------------" << endl; 477 do 478 { 479 tu = scanner(prog, pos, keys, row); 480 481 switch (tu.id) 482 { 483 case -1://返回的是错误 484 ++no; 485 cout << no << ": "; 486 cout << "Error in row" << row << "!" << '<' << tu.token<< "," << tu.id << '>' << endl; 487 tups.push_back(tu); 488 break; 489 case -2: 490 ++row; 491 // cout << '<' << tu.token<< "," << tu.id << '>' << endl; 492 break; 493 default: 494 ++no; 495 cout << no << ": "; 496 if(tu.id==28 || tu.id==29){ 497 cout << '<' << tu.id<< "," << tu.token << '>' << endl; 498 } 499 else{ 500 cout << '<' << tu.id<< "," << "-" << '>' << endl; 501 } 502 tups.push_back(tu); 503 break; 504 } 505 } while (pos < prog.size()); 506 507 cout << endl << "--------------------------------结果总行数------------"<<tups.size() << endl; 508 return 0; 509 }
四:运行截图
看懂代码之后,便可以很容易扩展写出其他语言的词法分析器。