├── .gitignore ├── CGA-LR ├── .gitignore ├── CGA-LR.pro ├── gramma.cpp ├── gramma.h ├── main.cpp ├── myutility.h └── symbol.h ├── LICENSE ├── README.md └── img ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png └── 9.png /.gitignore: -------------------------------------------------------------------------------- 1 | build-CGA-LR-Desktop_Qt_5_8_0_MinGW_32bit-Debug 2 | build-CGA-LR-Desktop_Qt_5_8_0_MinGW_32bit-Release 3 | -------------------------------------------------------------------------------- /CGA-LR/.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | *.qm 18 | .#* 19 | *.*# 20 | core 21 | !core/ 22 | tags 23 | .DS_Store 24 | .directory 25 | *.debug 26 | Makefile* 27 | *.prl 28 | *.app 29 | moc_*.cpp 30 | ui_*.h 31 | qrc_*.cpp 32 | Thumbs.db 33 | *.res 34 | *.rc 35 | /.qmake.cache 36 | /.qmake.stash 37 | 38 | # qtcreator generated files 39 | *.pro.user* 40 | 41 | # xemacs temporary files 42 | *.flc 43 | 44 | # Vim temporary files 45 | .*.swp 46 | 47 | # Visual Studio generated files 48 | *.ib_pdb_index 49 | *.idb 50 | *.ilk 51 | *.pdb 52 | *.sln 53 | *.suo 54 | *.vcproj 55 | *vcproj.*.*.user 56 | *.ncb 57 | *.sdf 58 | *.opensdf 59 | *.vcxproj 60 | *vcxproj.* 61 | 62 | # MinGW generated files 63 | *.Debug 64 | *.Release 65 | 66 | # Python byte code 67 | *.pyc 68 | 69 | # Binaries 70 | # -------- 71 | *.dll 72 | *.exe 73 | 74 | -------------------------------------------------------------------------------- /CGA-LR/CGA-LR.pro: -------------------------------------------------------------------------------- 1 | QT += core 2 | QT -= gui 3 | 4 | CONFIG += c++11 5 | 6 | TARGET = CGA-LR 7 | CONFIG += console 8 | CONFIG -= app_bundle 9 | 10 | TEMPLATE = app 11 | 12 | SOURCES += main.cpp \ 13 | gramma.cpp 14 | 15 | # The following define makes your compiler emit warnings if you use 16 | # any feature of Qt which as been marked deprecated (the exact warnings 17 | # depend on your compiler). Please consult the documentation of the 18 | # deprecated API in order to know how to port your code away from it. 19 | DEFINES += QT_DEPRECATED_WARNINGS 20 | 21 | # You can also make your code fail to compile if you use deprecated APIs. 22 | # In order to do so, uncomment the following line. 23 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 24 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 25 | 26 | HEADERS += \ 27 | gramma.h \ 28 | symbol.h \ 29 | myutility.h 30 | -------------------------------------------------------------------------------- /CGA-LR/gramma.cpp: -------------------------------------------------------------------------------- 1 | #include "gramma.h" 2 | #include 3 | #include "myutility.h" 4 | #include 5 | 6 | using namespace std; 7 | 8 | void GrammaTable::killBlank(QString &str) const 9 | { 10 | QString result; 11 | for (auto c : str) 12 | { 13 | if (c != ' ' && c != '\t' && c != '\n') 14 | result += c; 15 | } 16 | str = result; 17 | } 18 | 19 | bool GrammaTable::format(QString &str) const 20 | { 21 | killBlank(str); 22 | //check format 23 | int i = str.indexOf('-'); 24 | if (str[i + 1] != '>') 25 | return false; 26 | return true; 27 | } 28 | 29 | void GrammaTable::killDuplicated(int index) 30 | { 31 | if (index == -1) 32 | { 33 | // eliminate same candidate for all gramma 34 | for (int i = 0; i < grammas.size(); ++i) 35 | { 36 | killDuplicated(i); 37 | } 38 | } 39 | else 40 | { 41 | // eliminate same candidate for grammas[index] 42 | for (int i = 0; i < grammas[index].size(); ++i) 43 | { 44 | for (int j = i + 1; j < grammas[index].size(); ++j) 45 | { 46 | if (grammas[index][i] == grammas[index][j]) 47 | { 48 | // eliminate grammas[index][j] 49 | grammas[index].erase(grammas[index].begin() + j); 50 | --j; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | void GrammaTable::killEpsilon() 58 | { 59 | for (auto &gramma : grammas) 60 | { 61 | for (auto &candidate : gramma) 62 | { 63 | for (int i = 0; i < candidate.size(); ++i) 64 | { 65 | if (candidate[i] == EPSILON && candidate.size() > 1) 66 | { 67 | candidate.erase(candidate.begin() + i); 68 | --i; 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | void GrammaTable::getFirsts() 76 | { 77 | firsts.clear(); 78 | // add empty set 79 | for (int i = 0; i < grammas.size(); ++i) 80 | { 81 | firsts.push_back(First()); 82 | } 83 | 84 | bool changed = true; 85 | while (changed) 86 | { 87 | changed = false; 88 | for (int i = 0; i < grammas.size(); ++i) // for each gramma 89 | { 90 | 91 | for (int j = 0; j < grammas[i].size(); ++j) // for each candidate 92 | { 93 | for (auto symbol : grammas[i][j]) 94 | { 95 | if (symbol.type == Symbol::SymbolType::T) 96 | { 97 | // add this terminator, even EPSILON 98 | if (firsts[i].find(symbol) == firsts[i].end()) 99 | { 100 | changed = true; 101 | firsts[i].insert(symbol); 102 | } 103 | break; // to next candidate 104 | } 105 | else // symbol.type == NT 106 | { 107 | for (auto first : firsts[symbol.index]) 108 | { 109 | if (firsts[i].find(first) == firsts[i].end()) 110 | { 111 | changed = true; 112 | firsts[i].insert(first); 113 | } 114 | } 115 | if (firsts[symbol.index].find(EPSILON) == firsts[symbol.index].end()) 116 | { 117 | break; // to next candidate 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | void GrammaTable::getFollows() 127 | { 128 | follows.clear(); 129 | // add empty set 130 | for (int i = 0; i < grammas.size(); ++i) 131 | { 132 | follows.push_back(Follow()); 133 | } 134 | 135 | // add END 136 | follows[0].insert(END); 137 | 138 | bool changed = true; 139 | while (changed) 140 | { 141 | changed = false; 142 | for (int i = 0; i < grammas.size(); ++i) // for each gramma 143 | { 144 | for (int j = 0; j < grammas[i].size(); ++j) // for each candidate 145 | { 146 | for (int k = 0; k < grammas[i][j].size(); ++k) // for each symbol 147 | { 148 | if (grammas[i][j][k].type == Symbol::SymbolType::NT) // this is an NT 149 | { 150 | if (k + 1 < grammas[i][j].size()) // next symbol exist 151 | { 152 | if (grammas[i][j][k + 1].type == Symbol::SymbolType::T) // next symbol is a terminator(it will not be END or EPSILON) 153 | { 154 | if (follows[grammas[i][j][k].index].find(grammas[i][j][k + 1]) == follows[grammas[i][j][k].index].end()) 155 | { 156 | changed = true; 157 | follows[grammas[i][j][k].index].insert(grammas[i][j][k + 1]); 158 | } 159 | break; // to next candidate 160 | } 161 | else // next symbol is a NT 162 | { 163 | bool hasEPSILON = false; 164 | for (int x = k + 1; x < grammas[i][j].size(); ++x) 165 | { 166 | for (auto first : firsts[grammas[i][j][k + 1].index]) 167 | { 168 | if (first != EPSILON && follows[grammas[i][j][k].index].find(first) == follows[grammas[i][j][k].index].end()) 169 | { 170 | changed = true; 171 | follows[grammas[i][j][k].index].insert(first); 172 | } 173 | else if (first == EPSILON) 174 | { 175 | hasEPSILON = true; 176 | } 177 | } 178 | if (!hasEPSILON) 179 | break; 180 | } 181 | if (hasEPSILON) // the last NT in this candidate has EPSILON in its FIRST 182 | { 183 | for (auto follow : follows[i]) 184 | { 185 | if (follows[grammas[i][j][k].index].find(follow) == follows[grammas[i][j][k].index].end()) 186 | { 187 | changed = true; 188 | follows[grammas[i][j][k].index].insert(follow); 189 | } 190 | } 191 | } 192 | } 193 | } 194 | else //next symbol not exist 195 | { 196 | for (auto follow : follows[i]) 197 | { 198 | if (follows[grammas[i][j][k].index].find(follow) == follows[grammas[i][j][k].index].end()) 199 | { 200 | changed = true; 201 | follows[grammas[i][j][k].index].insert(follow); 202 | } 203 | } 204 | } 205 | } 206 | } 207 | } 208 | } 209 | } 210 | } 211 | 212 | void GrammaTable::getDFA() 213 | { 214 | states.clear(); 215 | dfa.clear(); 216 | 217 | // get the first state 218 | State firstState; 219 | firstState.push_back({0, 0, 0}); 220 | getState(firstState); // construct the whole state 221 | states.push_back(firstState); 222 | 223 | // now try to expand dfa 224 | for (int i = 0; i < states.size(); ++i) 225 | { 226 | State state = states[i]; 227 | QVector tStates; 228 | DFA tDfa; 229 | // expand 230 | for (int j = 0; j < state.size(); ++j) 231 | { 232 | // for each project 233 | auto project = state[j]; 234 | if (project.index < grammas[project.ntIndex][project.candidateIndex].size()) 235 | { 236 | // pointer can move right 237 | auto sym = grammas[project.ntIndex][project.candidateIndex][project.index]; // store symbol 238 | ++project.index; 239 | if (!tDfa.contains({state, sym})) 240 | { 241 | // construct a new state 242 | State t; 243 | t.push_back(project); 244 | tStates.push_back(t); 245 | tDfa.insert({state, sym}, t); 246 | } 247 | else 248 | { 249 | // add this project to that state 250 | DFA_Key key = {state, sym}; 251 | int index = tStates.indexOf(tDfa[key]); 252 | tStates[index].push_back(project); 253 | tDfa[key] = tStates[index]; 254 | } 255 | } 256 | } 257 | // every project in state has been added to tStates 258 | // construct tStates and tDfa 259 | for (auto &s : tStates) 260 | { 261 | auto key = tDfa.key(s); 262 | getState(s); 263 | tDfa[key] = s; 264 | } 265 | 266 | // check duplicated state from tDfa and dfa, merge dfa and tDfa 267 | auto keys = tDfa.keys(); 268 | for (auto key : keys) 269 | { 270 | int index = states.indexOf(tDfa[key]); 271 | if (index == -1) 272 | { 273 | states.push_back(tDfa[key]); 274 | dfa.insert(key, tDfa[key]); 275 | } 276 | else 277 | { 278 | dfa.insert(key, states[index]); 279 | } 280 | } 281 | } 282 | } 283 | 284 | void GrammaTable::getState(State &state) 285 | { 286 | // make sure that state has at least one project 287 | bool flag = true; // flag of loop, should be false if there is no change after a loop 288 | while (flag) 289 | { 290 | flag = false; 291 | for (int i = 0; i < state.size(); ++i) 292 | { 293 | auto p = state.begin() + i; 294 | // for each project 295 | if (p->index >= grammas[p->ntIndex][p->candidateIndex].size()) 296 | continue; 297 | if (grammas[p->ntIndex][p->candidateIndex][p->index].type == Symbol::SymbolType::NT) 298 | { 299 | // this is an NT, should add all its candidates 300 | int ntIndex = grammas[p->ntIndex][p->candidateIndex][p->index].index; 301 | for (int j = 0; j < grammas[ntIndex].size(); ++j) 302 | { 303 | Project t = {ntIndex, j, 0}; 304 | if (!state.contains(t)) 305 | { 306 | flag = true; 307 | i = -1; // restart loop 308 | state.push_back(t); 309 | } 310 | } 311 | } 312 | } 313 | } 314 | } 315 | 316 | int GrammaTable::getIndex(int ntIndex, int candidateIndex) const 317 | { 318 | int result = 0; 319 | for (int i = 0; i < ntIndex; ++i) 320 | { 321 | result += grammas[i].size(); 322 | } 323 | return result + candidateIndex; 324 | } 325 | 326 | void GrammaTable::getSLR_Table() 327 | { 328 | // get reduce and accept 329 | for (auto state : states) 330 | { 331 | int ntIndex; 332 | int candidateIndex; 333 | int reduceIndex = getReduceIndex(state, ntIndex, candidateIndex); 334 | if (ntIndex != -1 && candidateIndex != -1) 335 | { 336 | //reduce 337 | for (auto s : follows[ntIndex]) 338 | { 339 | Action a; 340 | a.index = reduceIndex; 341 | if (reduceIndex == 0) 342 | { 343 | a.type = Action::ActionType::Accept; 344 | } 345 | else 346 | { 347 | a.type = Action::ActionType::Reduce; 348 | } 349 | slrTable.insert({state, s}, a); 350 | } 351 | } 352 | } 353 | auto keys = dfa.keys(); 354 | for (auto key : keys) 355 | { 356 | if (key.s.type == Symbol::SymbolType::T) 357 | { 358 | // shift 359 | slrTable.insert(key, {Action::ActionType::Shift, states.indexOf(dfa[key])}); 360 | } 361 | else 362 | { 363 | // this is a non-terminator, action.type = goto 364 | slrTable.insert(key, {Action::ActionType::Goto, states.indexOf(dfa[key])}); 365 | } 366 | } 367 | } 368 | 369 | int GrammaTable::getReduceIndex(const State &s, int &ntIndex, int &candidateIndex) const 370 | { 371 | int result = 0; 372 | ntIndex = candidateIndex = -1; 373 | for (auto p : s) 374 | { 375 | if (p.index == grammas[p.ntIndex][p.candidateIndex].size()) 376 | { 377 | ntIndex = p.ntIndex; 378 | candidateIndex = p.candidateIndex; 379 | break; 380 | } 381 | } 382 | if (ntIndex != -1 && candidateIndex != -1) 383 | { 384 | for (int i = 0; i < ntIndex; ++i) 385 | { 386 | result += grammas[i].size(); 387 | } 388 | result += candidateIndex; 389 | } 390 | return result; 391 | } 392 | 393 | void GrammaTable::getCandidateIndex(int index, int &ntIndex, int &candidateIndex) const 394 | { 395 | ntIndex = candidateIndex = -1; 396 | for (int i = 0; i < grammas.size(); ++i) 397 | { 398 | if (index >= grammas[i].size()) 399 | { 400 | index -= grammas[i].size(); 401 | continue; 402 | } 403 | else 404 | { 405 | ntIndex = i; 406 | candidateIndex = index; 407 | return; 408 | } 409 | } 410 | } 411 | 412 | int GrammaTable::candidateCount() const 413 | { 414 | int result = 0; 415 | for (const auto &gramma : grammas) 416 | { 417 | result += gramma.size(); 418 | } 419 | return result; 420 | } 421 | 422 | First GrammaTable::getFirst(const Candidate &candidate) const 423 | { 424 | First result; 425 | for (auto symbol : candidate) 426 | { 427 | if (symbol.type == Symbol::SymbolType::T) 428 | { 429 | // add this terminator, even EPSILON 430 | if (result.find(symbol) == result.end()) 431 | { 432 | result.insert(symbol); 433 | } 434 | return result; 435 | } 436 | else // symbol.type == NT 437 | { 438 | for (auto first : firsts[symbol.index]) 439 | { 440 | if (result.find(first) == result.end()) 441 | { 442 | result.insert(first); 443 | } 444 | } 445 | if (firsts[symbol.index].find(EPSILON) == firsts[symbol.index].end()) 446 | { 447 | return result; 448 | } 449 | } 450 | } 451 | return result; 452 | } 453 | 454 | Candidate GrammaTable::parseInputToCandidate(const QString &str, QVector *values) const 455 | { 456 | int i = 0; 457 | Candidate result; 458 | while (i < str.length()) 459 | { 460 | QString sym; 461 | int value = 0; 462 | if (str[i] == '$') 463 | { 464 | while (i + 1 < str.length() && str[i + 1] != '$') 465 | { 466 | ++i; 467 | if (values) 468 | { 469 | value *= 10; 470 | value += str[i].toLatin1() - '0'; 471 | } 472 | else 473 | sym += str[i]; 474 | } 475 | if (i == str.length() || (sym.length() < 2 && !values)) 476 | { 477 | // no matched $ 478 | result.clear(); 479 | return result; 480 | } 481 | else // str[i + 1] == $ 482 | { 483 | ++i; 484 | } 485 | if (values) 486 | sym = "num"; 487 | int index = tTable.getIndex(sym, false); 488 | if (index == -1) 489 | { 490 | result.clear(); 491 | return result; 492 | } 493 | result.push_back(Symbol({Symbol::SymbolType::T, index})); 494 | if (values) 495 | values->push_back(value); 496 | sym = ""; 497 | value = 0; 498 | } 499 | else 500 | { 501 | // see other chars as terminator 502 | sym += str[i]; 503 | while (i + 1 < str.length() && str[i] == '\'') 504 | { 505 | ++i; 506 | sym += str[i]; 507 | } 508 | int index = tTable.getIndex(sym, false); 509 | if (index == -1) 510 | { 511 | result.clear(); 512 | return result; 513 | } 514 | result.push_back(Symbol({Symbol::SymbolType::T, index})); 515 | if (values) 516 | values->push_back(0); 517 | } 518 | ++i; 519 | } 520 | return result; 521 | } 522 | 523 | int GrammaTable::insert(const QString &grammaLine) 524 | { 525 | if (error) 526 | return lineCount; 527 | ++lineCount; 528 | QString str = grammaLine; 529 | if (!format(str)) 530 | { 531 | error = true; 532 | return lineCount; 533 | } 534 | if (!str.length()) 535 | { 536 | error = true; 537 | return lineCount; // ERROR 538 | } 539 | 540 | // get left Symbol QString 541 | QString left; 542 | left += str[0]; 543 | int i = 1; // index of str 544 | while (i < str.length() && str[i] == '\'') 545 | { 546 | left += str[i]; 547 | ++i; 548 | } 549 | 550 | // check left Symbol 551 | int grammaIndex = ntTable.getIndex(left); 552 | if (grammaIndex == grammas.size()) //new symbol 553 | { 554 | grammas.push_back(Gramma()); 555 | } 556 | 557 | // get right 558 | i += 2; // read "->" 559 | Candidate candidate; 560 | QString sym; // current symbol QString 561 | while (i < str.length()) 562 | { 563 | if (str[i] >= 'A' && str[i] <= 'Z') 564 | { 565 | sym += str[i]; 566 | while (i + 1 < str.length() && str[i + 1] == '\'') 567 | { 568 | ++i; 569 | sym += str[i]; 570 | } 571 | //find this NT 572 | int index = ntTable.getIndex(sym); 573 | if (index == grammas.size()) // new NT 574 | { 575 | grammas.push_back(Gramma()); 576 | } 577 | candidate.push_back({Symbol::SymbolType::NT, index}); 578 | sym = ""; 579 | } 580 | else if (str[i] == '$') 581 | { 582 | ++i; 583 | while (i < str.length() && str[i] != '$') 584 | { 585 | sym += str[i]; 586 | ++i; 587 | } 588 | if (str[i] != '$') 589 | { 590 | // no matched $ 591 | error = true; 592 | return lineCount; 593 | } 594 | else 595 | { 596 | // $ matched 597 | if (sym.length() < 2) 598 | { 599 | error = true; 600 | return lineCount; 601 | } 602 | else 603 | { 604 | // got a terminator 605 | int index = tTable.getIndex(sym); 606 | candidate.push_back({Symbol::SymbolType::T, index}); 607 | sym = ""; 608 | } 609 | } 610 | } 611 | else if (str[i] == '|') 612 | { 613 | if (candidate.size()) 614 | { 615 | grammas[grammaIndex].push_back(candidate); 616 | candidate.clear(); 617 | } 618 | } 619 | else 620 | { 621 | //other characters, inlcude '~', see them as terminator 622 | sym = str[i]; 623 | while (i + 1 < str.length() && str[i + 1] == '\'') 624 | { 625 | sym += str[i + 1]; 626 | ++i; 627 | } 628 | int index = tTable.getIndex(sym); 629 | candidate.push_back({Symbol::SymbolType::T, index}); 630 | sym = ""; 631 | } 632 | ++i; 633 | } 634 | if (candidate.size()) 635 | { 636 | grammas[grammaIndex].push_back(candidate); 637 | } 638 | killDuplicated(grammaIndex); 639 | return 0; 640 | } 641 | 642 | bool GrammaTable::generate() 643 | { 644 | if (error) 645 | return false; 646 | getFirsts(); 647 | getFollows(); 648 | getDFA(); 649 | getSLR_Table(); 650 | return true; 651 | } 652 | 653 | void GrammaTable::outputSingleCandidate(int ntIndex, int candidateIndex) const 654 | { 655 | cout << ntTable.getStr(ntIndex) << " -> "; 656 | for (auto symbol : grammas[ntIndex][candidateIndex]) 657 | { 658 | if (symbol.type == Symbol::SymbolType::NT) 659 | { 660 | cout << ntTable.getStr(symbol.index); 661 | } 662 | else 663 | { 664 | cout << tTable.getStr(symbol.index); 665 | } 666 | } 667 | } 668 | 669 | void GrammaTable::outputProject(const Project &p) const 670 | { 671 | cout << ntTable.getStr(p.ntIndex) << " -> "; 672 | for (int i = 0; i < grammas[p.ntIndex][p.candidateIndex].size(); ++i) 673 | { 674 | if (i == p.index) 675 | cout << "."; 676 | if (grammas[p.ntIndex][p.candidateIndex][i].type == Symbol::SymbolType::NT) 677 | { 678 | cout << ntTable.getStr(grammas[p.ntIndex][p.candidateIndex][i].index); 679 | } 680 | else 681 | { 682 | cout << tTable.getStr(grammas[p.ntIndex][p.candidateIndex][i].index); 683 | } 684 | } 685 | if (p.index == grammas[p.ntIndex][p.candidateIndex].size()) 686 | cout << "."; 687 | } 688 | 689 | void GrammaTable::outputSymbol(const Symbol &s) const 690 | { 691 | if (s.type == Symbol::SymbolType::T) 692 | cout << tTable.getStr(s.index); 693 | else 694 | cout << ntTable.getStr(s.index); 695 | } 696 | 697 | void GrammaTable::outputSLR_Key(const SLR_Key &key) const 698 | { 699 | cout << states.indexOf(key.state) << " + '"; 700 | outputSymbol(key.s); 701 | cout << "'"; 702 | } 703 | 704 | void GrammaTable::outputAction(const Action &a) const 705 | { 706 | switch (a.type) 707 | { 708 | case Action::ActionType::Accept: 709 | cout << "ACC"; 710 | break; 711 | case Action::ActionType::Goto: 712 | cout << "GOTO " << a.index; 713 | break; 714 | case Action::ActionType::Reduce: 715 | cout << "R" << a.index; 716 | break; 717 | case Action::ActionType::Shift: 718 | cout << "S" << a.index; 719 | break; 720 | default: 721 | break; 722 | } 723 | } 724 | 725 | void GrammaTable::output() const 726 | { 727 | if (error) 728 | { 729 | cout << "Can NOT parse gramma to LR gramma\n"; 730 | return; 731 | } 732 | 733 | cout << "Format gramma:\n"; 734 | for (int i = 0; i < grammas.size(); ++i) 735 | { 736 | if (grammas[i].size()) 737 | cout << ntTable.getStr(i) << " -> "; 738 | for (int j = 0; j < grammas[i].size(); ++j) 739 | { 740 | // each candidate 741 | for (int k = 0; k < grammas[i][j].size(); ++k) 742 | { 743 | // each symbol 744 | if (grammas[i][j][k].type == Symbol::SymbolType::NT) 745 | { 746 | cout << ntTable.getStr(grammas[i][j][k].index); 747 | } 748 | else // type == T 749 | { 750 | cout << tTable.getStr(grammas[i][j][k].index); 751 | } 752 | } 753 | if (j != grammas[i].size() - 1) 754 | cout << " | "; 755 | } 756 | if (grammas[i].size()) 757 | cout << endl; 758 | } 759 | cout << endl; 760 | 761 | cout << "Candidates with index:\n"; 762 | int index = 0; 763 | for (int i = 0; i < grammas.size(); ++i) 764 | { 765 | for (int j = 0; j < grammas[i].size(); ++j) 766 | { 767 | cout << index << "\t"; 768 | outputSingleCandidate(i, j); 769 | ++index; 770 | cout << endl; 771 | } 772 | } 773 | cout << endl; 774 | 775 | cout << "First sets:\n"; 776 | for (int i = 0; i < firsts.size(); ++i) 777 | { 778 | cout << "First(" << ntTable.getStr(i) << "): "; 779 | for (auto first : firsts[i]) 780 | { 781 | cout << tTable.getStr(first.index) << " "; 782 | } 783 | cout << "\n"; 784 | } 785 | cout << endl; 786 | 787 | cout << "Follow sets:\n"; 788 | for (int i = 0; i < firsts.size(); ++i) 789 | { 790 | cout << "Follow(" << ntTable.getStr(i) << "): "; 791 | for (auto follow : follows[i]) 792 | { 793 | cout << tTable.getStr(follow.index) << " "; 794 | } 795 | cout << "\n"; 796 | } 797 | cout << endl; 798 | 799 | cout << "LR(0) DFA states:\n"; 800 | for (int i = 0; i < states.size(); ++i) 801 | { 802 | cout << "State[" << i << "]:\n"; 803 | for (auto project : states[i]) 804 | { 805 | cout << '\t'; 806 | outputProject(project); 807 | cout << endl; 808 | } 809 | } 810 | cout << endl; 811 | 812 | cout << "LR(0) DFA transition functions:\n"; 813 | auto keys = dfa.keys(); 814 | for (auto key : keys) 815 | { 816 | cout << states.indexOf(key.state) << " + '"; 817 | outputSymbol(key.s); 818 | cout << "' -> " << states.indexOf(dfa[key]); 819 | cout << endl; 820 | } 821 | cout << endl; 822 | 823 | cout << "SLR_1 table:\n"; 824 | auto slrKeys = slrTable.keys(); 825 | for (auto key : slrKeys) 826 | { 827 | outputSLR_Key(key); 828 | cout << " -> "; 829 | outputAction(slrTable[key]); 830 | cout << endl; 831 | } 832 | } 833 | 834 | bool GrammaTable::parse(const QString &str, bool calculateResult) const 835 | { 836 | QVector values; 837 | Candidate candidate; 838 | if (calculateResult) 839 | candidate = parseInputToCandidate(str, &values); 840 | else 841 | candidate = parseInputToCandidate(str); 842 | if (candidate.size() == 0) 843 | { 844 | cout << "Error input.\n"; 845 | return false; 846 | } 847 | candidate.push_back(END); 848 | 849 | QStack stateStack; 850 | QStack symbolStack; 851 | QStack valueStack; 852 | stateStack.push(0); 853 | symbolStack.push(END); 854 | 855 | int index = 0; // index of candidate 856 | while (index < candidate.size()) 857 | { 858 | SLR_Key key = {states[stateStack.top()], candidate[index]}; 859 | if (!slrTable.contains(key)) 860 | break; 861 | auto action = slrTable[key]; 862 | outputAction(action); 863 | cout << endl; 864 | switch (action.type) 865 | { 866 | case Action::ActionType::Accept: 867 | cout << "Accepted.\n"; 868 | if (calculateResult) 869 | cout << "Result is " << valueStack.top() << endl; 870 | return true; 871 | break; 872 | case Action::ActionType::Reduce: 873 | { 874 | // calculate value 875 | if (calculateResult) 876 | { 877 | double t = 0; 878 | switch (action.index) 879 | { 880 | case 1: // E -> E1 + T { E.v = E1.v + T.v } 881 | t = valueStack.top(); 882 | valueStack.pop(); 883 | valueStack.pop(); 884 | t += valueStack.top(); 885 | valueStack.pop(); 886 | valueStack.push(t); 887 | break; 888 | case 2: // E -> E1 - T { E.v = E1.v - T.v } 889 | t = valueStack.top(); 890 | valueStack.pop(); 891 | valueStack.pop(); 892 | t = valueStack.top() - t; 893 | valueStack.pop(); 894 | valueStack.push(t); 895 | break; 896 | case 3: // E -> T { E.v = T.v } 897 | break; 898 | case 4: // T -> T1 * F { T.v = T1.v * F.v} 899 | t = valueStack.top(); 900 | valueStack.pop(); 901 | valueStack.pop(); 902 | t *= valueStack.top(); 903 | valueStack.pop(); 904 | valueStack.push(t); 905 | break; 906 | case 5: // T -> T1 / F { T.v = T1.v / F.v} 907 | t = valueStack.top(); 908 | valueStack.pop(); 909 | valueStack.pop(); 910 | t = valueStack.top() / t; 911 | valueStack.pop(); 912 | valueStack.push(t); 913 | break; 914 | case 6: // T -> F { T.v = F.v } 915 | break; 916 | case 7: // F -> (E) { F.v = E.v } 917 | valueStack.pop(); 918 | t = valueStack.top(); 919 | valueStack.pop(); 920 | valueStack.top() = t; 921 | break; 922 | case 8: // F -> num { F.v = num.v } 923 | break; 924 | default: 925 | break; 926 | } 927 | } 928 | int ntIndex; 929 | int candidateIndex; 930 | getCandidateIndex(action.index, ntIndex, candidateIndex); 931 | for (int i = 0; i < grammas[ntIndex][candidateIndex].size(); ++i) 932 | { 933 | stateStack.pop(); 934 | symbolStack.pop(); 935 | } 936 | SLR_Key gotoKey = {states[stateStack.top()], {Symbol::SymbolType::NT, ntIndex}}; 937 | auto gotoAction = slrTable[gotoKey]; 938 | outputAction(gotoAction); // output 939 | cout << endl; 940 | stateStack.push(gotoAction.index); 941 | symbolStack.push(ntTable[ntIndex]); 942 | --index; // do not increase index 943 | break; 944 | } 945 | case Action::ActionType::Shift: 946 | stateStack.push(action.index); 947 | symbolStack.push(candidate[index]); 948 | if (calculateResult) 949 | valueStack.push(values[index]); 950 | break; 951 | default: 952 | break; 953 | } 954 | ++index; 955 | } 956 | cout << "This line not belongs to this gramma.\n"; 957 | return false; 958 | } 959 | -------------------------------------------------------------------------------- /CGA-LR/gramma.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "symbol.h" 4 | #include 5 | #include 6 | #include 7 | 8 | using Candidate = QVector; 9 | using Gramma = QVector; 10 | using First = QSet; 11 | using Follow = QSet; 12 | 13 | struct Project 14 | { 15 | int ntIndex; 16 | int candidateIndex; 17 | int index; // position of Point in a project 18 | bool operator==(const Project &ano) const { return ntIndex == ano.ntIndex && candidateIndex == ano.candidateIndex && index == ano.index; } 19 | bool operator<(const Project &ano) const 20 | { 21 | if (ntIndex != ano.ntIndex) 22 | return ntIndex < ano.ntIndex; 23 | else if (candidateIndex != ano.candidateIndex) 24 | return candidateIndex < ano.candidateIndex; 25 | else 26 | return index < ano.index; 27 | } 28 | bool operator!=(const Project &ano) const { return !(*this == ano); } 29 | }; 30 | 31 | // generate a hash value for Project 32 | inline uint qHash(const Project &key, uint seed) 33 | { 34 | return qHash(1000 * key.ntIndex + 100 * key.candidateIndex + key.index, seed); 35 | } 36 | 37 | using State = QVector; 38 | 39 | struct DFA_Key 40 | { 41 | State state; 42 | Symbol s; 43 | bool operator<(const DFA_Key &ano) const 44 | { 45 | if (state.size() != ano.state.size()) 46 | return state.size() < ano.state.size(); 47 | else 48 | { 49 | for (int i = 0; i < state.size(); ++i) 50 | { 51 | if (state[i] != ano.state[i]) 52 | return state[i] < ano.state[i]; 53 | } 54 | } 55 | return s < ano.s; 56 | } 57 | bool operator==(const DFA_Key &ano) { return state == ano.state && s == ano.s; } 58 | }; 59 | 60 | using DFA = QMap; 61 | 62 | struct Action 63 | { 64 | enum ActionType 65 | { 66 | Shift, 67 | Reduce, 68 | Goto, 69 | Accept 70 | }; 71 | ActionType type; 72 | int index; 73 | }; 74 | 75 | using SLR_Key = DFA_Key; 76 | using SLR_Table = QMap; 77 | 78 | class GrammaTable 79 | { 80 | private: 81 | // input 82 | QVector grammas; 83 | T_Table tTable; 84 | NT_Table ntTable; 85 | 86 | // error handling 87 | int lineCount; 88 | bool error; 89 | 90 | // process 91 | QVector firsts; 92 | QVector follows; 93 | QVector states; 94 | DFA dfa; 95 | SLR_Table slrTable; 96 | 97 | void killBlank(QString &str) const; // discard blank chars 98 | bool format(QString &str) const; // return false if format is wrong 99 | /** 100 | * killDuplicated: 101 | * eliminate same Candidate in grammas[index] if index != -1 102 | * eliminate same Candidate in each Gramma when index == -1 103 | */ 104 | void killDuplicated(int index = -1); 105 | void killEpsilon(); 106 | void getFirsts(); 107 | First getFirst(const Candidate &candidate) const; 108 | void getFollows(); 109 | void getDFA(); // construct DFA 110 | void getState(State &state); // construct a state 111 | int getIndex(int ntIndex, int candidateIndex) const; 112 | void getSLR_Table(); 113 | int getReduceIndex(const State &s, int &ntIndex, int &candidateIndex) const; 114 | void getCandidateIndex(int index, int &ntIndex, int &candidateIndex) const; 115 | int candidateCount() const; 116 | Candidate parseInputToCandidate(const QString &str, QVector *values = nullptr) const; // return empty candidate if error 117 | void outputSingleCandidate(int ntIndex, int candidateIndex) const; 118 | void outputProject(const Project &p) const; 119 | void outputSymbol(const Symbol &s) const; 120 | void outputSLR_Key(const SLR_Key &key) const; 121 | void outputAction(const Action &a) const; 122 | 123 | public: 124 | GrammaTable() : lineCount(0), error(false) {} 125 | 126 | int insert(const QString &grammaLine); // return 0 if ok, otherwise return lineCount 127 | /** 128 | * generate first set, follow set, index of candidates and predict table 129 | * return false if error 130 | */ 131 | bool generate(); 132 | void output() const; 133 | 134 | bool ok() const { return !error; } 135 | int currentLineCount() const { return lineCount; } 136 | bool parse(const QString &str, bool calculateResult = false) const; 137 | }; 138 | -------------------------------------------------------------------------------- /CGA-LR/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "gramma.h" 3 | #include "myutility.h" 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | void showDeveloper() 11 | { 12 | cout << "******************************************************************\n" 13 | << " Compiler-Grammatical-Analyzer-LR(CGA-LR)\n" 14 | << " Written By DiscreteTom\n" 15 | << " See source code and report BUG at\n" 16 | << " https://github.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR\n" 17 | << "******************************************************************\n\n"; 18 | } 19 | 20 | int main(/*int argc, char *argv[]*/) 21 | { 22 | showDeveloper(); 23 | 24 | GrammaTable gt; 25 | QString t; 26 | 27 | gt.insert("E' -> E"); 28 | gt.insert("E -> E+T | E-T | T"); 29 | gt.insert("T -> T*F | T/F | F"); 30 | gt.insert("F -> (E) | $num$"); 31 | 32 | gt.generate(); 33 | cout << "Output:\n"; 34 | gt.output(); 35 | 36 | while (1) 37 | { 38 | cout << "\nPress 1: Just parse input.\nPress 2: Calculate result.\nOtherwise: Exit\n"; 39 | int mode = getch() - '0'; 40 | if (mode != 1 && mode != 2) // error input 41 | mode = 3; 42 | if (mode == 3) 43 | return 0; 44 | 45 | bool flag = true; 46 | while (flag) 47 | { 48 | cout << "\nInput a line to parse, input blank line to stop.\n"; 49 | getline(cin, t); 50 | if (t.length()) 51 | { 52 | gt.parse(t, mode == 2); 53 | } 54 | else 55 | { 56 | flag = false; 57 | } 58 | } 59 | } 60 | system("pause"); 61 | } 62 | -------------------------------------------------------------------------------- /CGA-LR/myutility.h: -------------------------------------------------------------------------------- 1 | #ifndef MYUTILITY_H 2 | #define MYUTILITY_H 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | inline ostream & operator<<(ostream & out, const QString & str){return out << str.toStdString();} 10 | inline void getline(istream & in, QString &str){ 11 | string t; 12 | getline(in, t); 13 | str = QString::fromStdString(t); 14 | } 15 | 16 | #endif // MYUTILITY_H 17 | -------------------------------------------------------------------------------- /CGA-LR/symbol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct Symbol 8 | { 9 | enum SymbolType 10 | { 11 | T, // terminator 12 | NT // non-terminator 13 | }; 14 | SymbolType type; 15 | int index; // index in SymbolTable 16 | 17 | bool operator==(const Symbol &ano) const { return ano.type == type && ano.index == index; } 18 | bool operator!=(const Symbol &ano) const { return !(*this == ano); } 19 | bool operator<(const Symbol &ano) const 20 | { 21 | if (ano.type == type) 22 | return index < ano.index; 23 | else if (type == T) 24 | return false; 25 | else 26 | return true; 27 | } 28 | }; 29 | 30 | inline uint qHash(const Symbol &key, uint seed) 31 | { 32 | return qHash(key.index * (key.type == Symbol::SymbolType::T ? 1 : -1), seed); 33 | } 34 | 35 | template 36 | class SymbolTable 37 | { 38 | private: 39 | Symbol::SymbolType type; 40 | QVector symbols; 41 | 42 | public: 43 | SymbolTable(); 44 | int getIndex(const QString &str); // if str not exist, push it into symbols 45 | int getIndex(const QString &str, bool) const; // return -1 if str not exist 46 | QString getStr(int i) const; // return blank QString if i is invalid 47 | int size() const { return symbols.size(); } 48 | Symbol operator[](int n) const 49 | { 50 | Symbol result = {type, n}; 51 | return result; 52 | } 53 | }; 54 | 55 | const Symbol EPSILON = {Symbol::SymbolType::T, 0}; 56 | const Symbol END = {Symbol::SymbolType::T, 1}; 57 | 58 | using T_Table = SymbolTable; 59 | using NT_Table = SymbolTable; 60 | 61 | template <> 62 | inline SymbolTable::SymbolTable() 63 | { 64 | getIndex("~"); // insert EPSILON to terminatorTable 65 | getIndex("$"); // insert END to terminatorTable 66 | } 67 | 68 | template 69 | SymbolTable::SymbolTable() {} 70 | 71 | template 72 | int SymbolTable::getIndex(const QString &str) 73 | { 74 | for (int i = 0; i < symbols.size(); ++i) 75 | { 76 | if (symbols[i] == str) // str exist 77 | return i; 78 | } 79 | // str does NOT exist, add it to table 80 | symbols.push_back(str); 81 | return symbols.size() - 1; 82 | } 83 | 84 | template 85 | int SymbolTable::getIndex(const QString &str, bool) const 86 | { 87 | for (int i = 0; i < symbols.size(); ++i) 88 | { 89 | if (symbols[i] == str) // str exist 90 | return i; 91 | } 92 | return -1; 93 | } 94 | 95 | template 96 | QString SymbolTable::getStr(int i) const 97 | { 98 | if (i >= 0 && i < symbols.size()) 99 | return symbols[i]; 100 | else 101 | return ""; 102 | } 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 DiscreteTom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 语法分析器的设计与实现-LR 2 | 3 | ## 环境 4 | 5 | - 使用语言 - C++ 6 | - 开发环境 - Qt Creator 5.8.0 + MinGW 7 | - 运行环境 - Windows 10 8 | 9 | >虽然使用Qt Creator开发,程序仍为console application,只是使用了Qt提供的容器库。 10 | 11 | ## 概述 12 | 13 | - 能够自动生成LR(0) DFA与SLR(1)分析表 14 | - 程序内含翻译方案,能够计算输入表达式的结果 15 | 16 | ## 要求(摘自课本 17 | 18 | ### 实验内容 19 | 20 | 编写语法分析程序,实现对算术表达式的语法分析。要求所分析算术表达式由如下文法产生: 21 | 22 | ``` 23 | E -> E+T | E-T | T 24 | T -> T*F | T/F | F 25 | F -> (E) | num 26 | ``` 27 | 28 | ### 实验要求 29 | 30 | - 在对输入的算术表达式进行分析的过程中,依次输出所采用的产生式。 31 | - 构造识别该文法所有活前缀的DFA 32 | - 构造文法的LR分析表 33 | - 编程实现算法4.3,构造LR分析程序 34 | 35 | ### 算法4.3 36 | 37 | - 输入 - 文法G的一张分析表和一个输入符号串ω 38 | - 输出 - 如果ω∈L(G),得到ω自底向上的分析,否则报错 39 | 40 | 方法: 41 | 42 | ```c++ 43 | 初始化 { 44 | 初始状态S0压栈 45 | ω$存入输入缓冲区 46 | 置ip指向ω$的第一个符号 47 | } 48 | do { 49 | 令S是栈顶状态,a是ip指向的符号; 50 | if (action[S, a] = shift S'){ 51 | 把a和S'分别压入符号栈和状态栈的栈顶; 52 | 推进ip,使它进入下一个输入符号; 53 | } else if (action[S, a] = reduce by A -> β){ 54 | 从栈顶弹出|β|个符号; 55 | 令S'是栈顶状态,把A和goto[S', A]分别压入符号栈和状态栈的栈顶; 56 | 输出产生式A -> β; 57 | } else if (action[S, a] = accept) return; 58 | else error(); 59 | } while (1); 60 | ``` 61 | 62 | ## 设计 63 | 64 | ### 文法存储 65 | 66 | #### 符号的存储 67 | 68 | ```c++ 69 | struct Symbol 70 | { 71 | enum SymbolType 72 | { 73 | T, // terminator 74 | NT // non-terminator 75 | }; 76 | SymbolType type; 77 | int index; // index in SymbolTable 78 | }; 79 | ``` 80 | 81 | 定义了枚举常量T(终结符)和NT(非终结符)作为符号类型。定义结构Symbol,包含一个符号类型type和一个在符号表中的索引index 82 | 83 | 定义以下两个特殊的终结符: 84 | 85 | ```c++ 86 | const Symbol EPSILON = {Symbol::SymbolType::T, 0}; // ε 87 | const Symbol END = {Symbol::SymbolType::T, 1}; // $ 88 | ``` 89 | 90 | 严格来说空串`ε`和输入结束符`$`不算是终结符。此处视为终结符方便管理 91 | 92 | #### 符号表 93 | 94 | ```c++ 95 | template 96 | class SymbolTable 97 | { 98 | private: 99 | Symbol::SymbolType type; 100 | QVector symbols; 101 | 102 | public: 103 | SymbolTable(); 104 | int getIndex(const QString &str); // if str not exist, push it into symbols 105 | int getIndex(const QString &str, bool) const; // return -1 if str not exist 106 | QString getStr(int i) const; // return blank QString if i is invalid 107 | int size() const { return symbols.size(); } 108 | Symbol operator[](int n) const 109 | { 110 | Symbol result = {type, n}; 111 | return result; 112 | } 113 | }; 114 | using T_Table = SymbolTable; 115 | using NT_Table = SymbolTable; 116 | ``` 117 | 118 | 符号表分为两种:终结符表和非终结符表。其中终结符表在构造时就需要添加符号`$`和`ε`。为了方便理解,使用`using`对不同类型的符号表进行了重命名,即T_Table和NT_Table。为了实现以上两点,使用**模板**来设计符号表,模板参数isTerminatorTable表示了此符号表是否是终结符号表。 119 | 120 | 符号表中并没有保存符号Symbol,而是保存了每个符号的字符串量,即`QVector symbols`,所以虽然可以使用`operator[]`从符号表获得符号但是不能修改符号表中已存在的内容。可以根据符号的下标来查询字符串量(即getStr方法),也可以根据符号的字符串量来查询下标(即getIndex)。所以符号表只是一个用来查询的表,并不是一个符号的容器。 121 | 122 | 函数解释: 123 | - `int getIndex(const string &str)` - 查询字符串并返回下标。如果没有此字符串则插入此字符串 124 | - `int getIndex(const string &str, bool) const` - 仅查询字符串并获得下标 125 | - `string getStr(int i) const` - 查询对应下标的符号的字符串 126 | - `int size() const` - 查询符号表容量 127 | 128 | #### DFA与自动机 129 | 130 | 为了构造有效项目集规范族,首先定义了有效项目`struct Project` 131 | 132 | ```c++ 133 | struct Project 134 | { 135 | int ntIndex; 136 | int candidateIndex; 137 | int index; // position of Point in a project 138 | }; 139 | ``` 140 | 141 | 其中ntIndex表示此项目的生成式左侧非终结符的下标,candidateIndex表示此项目的生成式是此非终结符的第几个生成式。index表示项目中圆点的位置。 142 | 143 | 自动机的每个状态都是一个项目的集合,即 144 | 145 | ```c++ 146 | using State = QVector; 147 | ``` 148 | 149 | 自动机保存为一个map,此map的key应该是状态和输入符号的二元组,所以定义DFA_Key: 150 | 151 | ```c++ 152 | struct DFA_Key 153 | { 154 | State state; 155 | Symbol s; 156 | }; 157 | ``` 158 | 159 | 那么DFA就是: 160 | 161 | ```c++ 162 | using DFA = QMap; 163 | ``` 164 | 165 | 类似地,分析表也是一个map,分析表的key应该是当前状态(的下标)和输入符号的二元组。因为可以和DFA_Key都包含状态和输入符号,所以直接使用DFA_Key作为分析表的key: 166 | 167 | ```c++ 168 | using SLR_Key = DFA_Key; 169 | ``` 170 | 171 | 而分析表map的value应该是一个分析动作,所以定义Action: 172 | 173 | ```c++ 174 | struct Action 175 | { 176 | enum ActionType 177 | { 178 | Shift, 179 | Reduce, 180 | Goto, 181 | Accept 182 | }; 183 | ActionType type; 184 | int index; 185 | }; 186 | ``` 187 | 188 | 可以看到Action包含一个枚举类型ActionType,有四种取值,分别表示: 189 | - Shift - 移进,此时index表示移进后转移到的状态下标 190 | - Reduce - 规约,此时index表示规约使用的产生式的下标 191 | - Goto - 转移,此时index表示应该转移到的状态下标 192 | - Accept - 接收,此时index无意义 193 | 194 | 那么分析表的数据结构就是: 195 | 196 | ```c++ 197 | using SLR_Table = QMap; 198 | ``` 199 | 200 | #### 文法表 201 | 202 | ```c++ 203 | class GrammaTable 204 | { 205 | private: 206 | // input 207 | QVector grammas; 208 | T_Table tTable; 209 | NT_Table ntTable; 210 | 211 | // error handling 212 | int lineCount; 213 | bool error; 214 | 215 | // process 216 | QVector firsts; 217 | QVector follows; 218 | QVector states; 219 | DFA dfa; 220 | SLR_Table slrTable; 221 | 222 | void killBlank(QString &str) const; // discard blank chars 223 | bool format(QString &str) const; // return false if format is wrong 224 | /** 225 | * killDuplicated: 226 | * eliminate same Candidate in grammas[index] if index != -1 227 | * eliminate same Candidate in each Gramma when index == -1 228 | */ 229 | void killDuplicated(int index = -1); 230 | void killEpsilon(); 231 | void getFirsts(); 232 | First getFirst(const Candidate &candidate) const; 233 | void getFollows(); 234 | void getDFA(); // construct DFA 235 | void getState(State &state); // construct a state 236 | int getIndex(int ntIndex, int candidateIndex) const; 237 | void getSLR_Table(); 238 | int getReduceIndex(const State &s, int &ntIndex, int &candidateIndex) const; 239 | void getCandidateIndex(int index, int &ntIndex, int &candidateIndex) const; 240 | int candidateCount() const; 241 | // return empty candidate if error 242 | Candidate parseInputToCandidate(const QString &str, QVector *values = nullptr) const; 243 | void outputSingleCandidate(int ntIndex, int candidateIndex) const; 244 | void outputProject(const Project &p) const; 245 | void outputSymbol(const Symbol &s) const; 246 | void outputSLR_Key(const SLR_Key &key) const; 247 | void outputAction(const Action &a) const; 248 | 249 | public: 250 | GrammaTable() : lineCount(0), error(false) {} 251 | 252 | int insert(const QString &grammaLine); // return 0 if ok, otherwise return lineCount 253 | /** 254 | * generate first set, follow set, index of candidates and predict table 255 | * return false if error 256 | */ 257 | bool generate(); 258 | void output() const; 259 | 260 | bool ok() const { return !error; } 261 | int currentLineCount() const { return lineCount; } 262 | bool parse(const QString &str, bool calculateResult = false) const; 263 | }; 264 | ``` 265 | 266 | 文法表包括了文法grammas、符号表tTable和ntTable、此文法生成的FIRST集与FOLLOW集、DFA、所有状态和预测分析表。 267 | 268 | 一个候选式Candidate由若干个符号Symbol组成,一个符号的生成式Gramma由若干个候选式Candidate组成 269 | 270 | ```c++ 271 | using Candidate = vector; 272 | using Gramma = vector; 273 | ``` 274 | 275 | FIRST集和FOLLOW集是符号Symbol的集合。 276 | 277 | ```c++ 278 | using First = set; 279 | using Follow = set; 280 | ``` 281 | 282 | 变量lineCount和error用来控制错误,当错误发生时不能够继续输入文法、不能生成FIRST集和FOLLOW集、不能解析输入串。输入错误时会返回lineCount,根据此值可以定位输入的错误。 283 | 284 | 函数概述: 285 | - private 286 | - `void killBlank(QString &str) const;` - 清除输入串中的空白符(空格、制表、换行) 287 | - `bool format(QString &str) const;` - 如果输入串没有`->`则报错 288 | - `void killDuplicated(int index = -1);` - 清除第index个非终结符的重复生成式。index为-1时清除所有非终结符的重复生成式 289 | - `void killEpsilon();` - 清除所有候选式中多余的ε 290 | - `void getFirsts();` - 生成所有非终结符的FIRST集 291 | - `First getFirst(const Candidate &candidate) const;` - 在所有非终结符的FIRST存在的情况下生成单个候选式的FIRST集 292 | - `void getFollows();` - 生成所有非终结符的FOLLOW集 293 | - `void getDFA();` - 生成DFA 294 | - `void getState(State &state);` - 根据当前State进行扩充从而形成一个完整的State 295 | - `void getIndex(int ntIndex, int candidateIndex) const;` - 根据生成式左侧非终结符的下标和生成式是此非终结符的第几个生成式来判断生成式下标 296 | - `void getSLR_Table();` - 生成SLR分析表 297 | - `int getReduceIndex(const State &s, int &ntIndex, int &candidateIndex) const;` - 根据当前状态判断是否能规约。如果能,返回规约使用的生成式下标,并把ntIndex和candidateIndex指向生成式 298 | - `void getCandidateIndex(int index, int &ntIndex, int &candidateIndex) const;` - 根据输入的index计算生成式在grammas中的位置,通过ntIndex和candidateIndex返回 299 | - `int candidateCount() const;` - 返回生成式数量 300 | - `Candidate parseInputToCandidate(const QString &str, QVector *values = nullptr) const;` - 把字符串解析为符号串。如果values不为空,则解析输入串中的数字 301 | - `void outputSingleCandidate(int ntIndex, int candidateIndex) const;` - 输出单个生成式 302 | - `void outputProject(const Project &p) const;` - 输出一个项目 303 | - `void outputSymbol(const Symbol &s) const;` - 输出一个符号 304 | - `void outputSLR_Key(const SLR_Key &key) const;` - 输出一个分析表的key 305 | - `void outputAction(const Action &a) const;` - 输出一个分析动作 306 | - public 307 | - `int insert(const string &grammaLine);` - 插入一行文法。如果文法出错则返回lineCount,否则返回0 308 | - `bool generate();` - 生成FIRST集、FOLLOW集和预测分析表,如果生成失败则返回false 309 | - `void output() const;` - 输出消左递归后的文法、FIRST集、FOLLOW集、预测分析表 310 | - `bool ok() const { return !error; }` - 判断此预测分析表是否能够正常继续输入 311 | - `int currentLineCount() const { return lineCount; }` - 返回当前行数,通常用来确定错误发生的位置 312 | - `bool parse(const string &str, bool calculateResult = false) const;` - 试图解析输入的字符串。如果calculateResult为true则输出结果。如果解析失败则返回false。 313 | 314 | ### 翻译方案设计 315 | 316 | ``` 317 | E' -> E { print(E.v) } 318 | E -> E1 + T { E.v = E1.v + T.v } 319 | E -> E1 - T { E.v = E1.v - T.v } 320 | E -> T { E.v = T.v } 321 | T -> T1 * F { T.v = T1.v * F.v} 322 | T -> T1 / F { T.v = T1.v / F.v} 323 | T -> F { T.v = F.v } 324 | F -> (E) { F.v = E.v } 325 | F -> num { F.v = num.v } 326 | ``` 327 | 328 | 其中所有符号的属性`v`均为综合属性,用来保存和传递结果。`print(E.v)`用来输出结果。 329 | 330 | ### 输入设计与限制 331 | 332 | 文法已内置在main函数中,不需要输入文法。能够输入的内容为: 333 | - 需要解析的符号串 334 | - 需要解析的表达式 335 | 336 | **输入符号串**时,如果输入的符号长度超过1位,需要使用两个`$`符号括起来。在输入符号串时,唯一需要括起来的符号就是`num`。示例输入: 337 | 338 | ``` 339 | $num$ 340 | $num$+$num$*$num$/$num$-$num$ 341 | ($num$+($num$-($num$*($num$/($num$))))) 342 | ``` 343 | 344 | **输入表达式**时,输入的数字也需要用两个`$`括起来,不论数字长短。示例输入: 345 | 346 | ``` 347 | $123$ 348 | $123$+$123$*$123$/$123$-$123$ 349 | ($123$+($123$-($123$*($123$/($123$))))) 350 | ``` 351 | 352 | >之所以使用这样的设计是因为上次写自顶向下语法分析器的时候已经写好了解析两个`$`括起内容的函数 353 | 354 | **表达式解析时的输入限制**:毕竟不是词法分析器,输入数字时限制只能输入正整数,即两个`$`中间不能包含0-9以外的符号,但是输出可以是小数和负数。解析时会把所有由两个`$`括起的内容解析为`num` 355 | 356 | ### 关键函数设计 357 | 358 | >此处只解释DFA和分析表的生成,以及解析函数的工作原理。关于First集和Follow集的构造,在上次自顶向下语法分析时已实现,略去。 359 | 360 | #### DFA的生成 361 | 362 | ```c++ 363 | void GrammaTable::getDFA() 364 | { 365 | // 首先清除所有状态以及之前的DFA 366 | states.clear(); 367 | dfa.clear(); 368 | 369 | // 构造第一个状态,即状态0 370 | State firstState; 371 | firstState.push_back({0, 0, 0}); // 把项目 E' -> .E 装入状态 372 | getState(firstState); // 根据项目中的已有状态,拓展状态而生成完整状态 373 | states.push_back(firstState); // 把状态0装入状态集合 374 | 375 | // 扩展DFA 376 | for (int i = 0; i < states.size(); ++i) 377 | { 378 | // 在拓展的过程中states的size可能会发生变化 379 | // 所以要注意边界 380 | State state = states[i]; // 当前状态 381 | QVector tStates; // 临时保存由当前状态生成的状态(因为状态可能重复 382 | DFA tDfa; // 临时保存由当前状态生成的一些转移函数 383 | 384 | // 开始扩展 385 | for (int j = 0; j < state.size(); ++j) 386 | { 387 | // 遍历当前状态中的每个项目 388 | auto project = state[j]; 389 | if (project.index < grammas[project.ntIndex][project.candidateIndex].size()) 390 | { 391 | // 项目中的圆点可以右移 392 | // 保存右移时接收的符号 393 | auto sym = grammas[project.ntIndex][project.candidateIndex][project.index]; 394 | ++project.index; // 圆点右移 395 | if (!tDfa.contains({state, sym})) 396 | { 397 | // 临时DFA中没有此转换函数,则新建状态和转换函数 398 | State t; 399 | t.push_back(project); // 此状态目前仅有这一个项目 400 | tStates.push_back(t); // 装入临时状态集 401 | tDfa.insert({state, sym}, t); // 装入临时DFA 402 | } 403 | else 404 | { 405 | // 临时DFA中已有此转换函数 406 | // 把此项目装入转换函数的目标状态中 407 | DFA_Key key = {state, sym}; 408 | int index = tStates.indexOf(tDfa[key]); 409 | tStates[index].push_back(project); 410 | tDfa[key] = tStates[index]; 411 | } 412 | } 413 | } 414 | // 至此,当前状态中的每个项目都已经处理过了 415 | // 扩展临时状态集中的状态 416 | for (auto &s : tStates) 417 | { 418 | auto key = tDfa.key(s); 419 | getState(s); 420 | tDfa[key] = s; 421 | } 422 | 423 | // 合并DFA与临时DFA、状态集与已有状态集 424 | // 检查扩展后的临时状态集中的状态是否与已有的状态重复 425 | auto keys = tDfa.keys(); 426 | for (auto key : keys) 427 | { 428 | int index = states.indexOf(tDfa[key]); 429 | if (index == -1) 430 | { 431 | states.push_back(tDfa[key]); 432 | dfa.insert(key, tDfa[key]); 433 | } 434 | else 435 | { 436 | dfa.insert(key, states[index]); 437 | } 438 | } 439 | } 440 | } 441 | ``` 442 | 443 | #### 分析表的生成 444 | 445 | ```c++ 446 | void GrammaTable::getSLR_Table() 447 | { 448 | // 先根据所有状态生成规约和接收的表项 449 | // 遍历所有状态 450 | for (auto state : states) 451 | { 452 | int ntIndex; 453 | int candidateIndex; 454 | // 判断当前状态是否存在规约项 455 | int reduceIndex = getReduceIndex(state, ntIndex, candidateIndex); 456 | if (ntIndex != -1 && candidateIndex != -1) 457 | { 458 | // 当前状态能够规约(默认只有一种规约情况 459 | // 判断是接收还是规约,生成表项 460 | for (auto s : follows[ntIndex]) 461 | { 462 | Action a; 463 | a.index = reduceIndex; 464 | if (reduceIndex == 0) 465 | { 466 | a.type = Action::ActionType::Accept; 467 | } 468 | else 469 | { 470 | a.type = Action::ActionType::Reduce; 471 | } 472 | slrTable.insert({state, s}, a); 473 | } 474 | } 475 | } 476 | // 生成移进和转移的表项 477 | // 遍历DFA 478 | auto keys = dfa.keys(); 479 | for (auto key : keys) 480 | { 481 | if (key.s.type == Symbol::SymbolType::T) 482 | { 483 | // 移进 484 | slrTable.insert(key, {Action::ActionType::Shift, states.indexOf(dfa[key])}); 485 | } 486 | else 487 | { 488 | // 转移 489 | slrTable.insert(key, {Action::ActionType::Goto, states.indexOf(dfa[key])}); 490 | } 491 | } 492 | } 493 | ``` 494 | 495 | #### 输入串的解析 496 | 497 | ```c++ 498 | bool GrammaTable::parse(const QString &str, bool calculateResult) const 499 | { 500 | QVector values; // 用来保存每个符号的value。没有value的符号默认value为0 501 | Candidate candidate; // 用来保存解析输入串后得到的符号串 502 | if (calculateResult) 503 | candidate = parseInputToCandidate(str, &values); 504 | else 505 | candidate = parseInputToCandidate(str); 506 | if (candidate.size() == 0) 507 | { 508 | cout << "Error input.\n"; 509 | return false; 510 | } 511 | candidate.push_back(END); // 在符号串末尾加上终止符$ 512 | 513 | // 声明与初始化变量 514 | QStack stateStack; 515 | QStack symbolStack; 516 | QStack valueStack; 517 | stateStack.push(0); 518 | symbolStack.push(END); 519 | 520 | int index = 0; // 当前分析到的符号串的下标 521 | while (index < candidate.size()) // 没有分析结束时 522 | { 523 | // 根据当前状态和符号,从分析表获取分析动作 524 | SLR_Key key = {states[stateStack.top()], candidate[index]}; 525 | if (!slrTable.contains(key)) // 分析表没有此项,停止分析 526 | break; 527 | auto action = slrTable[key]; 528 | outputAction(action); // 输出动作 529 | cout << endl; 530 | switch (action.type) 531 | { 532 | case Action::ActionType::Accept: // 接收 533 | cout << "Accepted.\n"; 534 | if (calculateResult) 535 | cout << "Result is " << valueStack.top() << endl; 536 | return true; 537 | break; 538 | case Action::ActionType::Reduce: // 规约 539 | { 540 | // calculate value 541 | if (calculateResult) 542 | { 543 | // 直接通过栈顶使用value栈中的内容计算结果 544 | double t = 0; 545 | switch (action.index) 546 | { 547 | case 1: // E -> E1 + T { E.v = E1.v + T.v } 548 | t = valueStack.top(); 549 | valueStack.pop(); 550 | valueStack.pop(); 551 | t += valueStack.top(); 552 | valueStack.pop(); 553 | valueStack.push(t); 554 | break; 555 | case 2: // E -> E1 - T { E.v = E1.v - T.v } 556 | t = valueStack.top(); 557 | valueStack.pop(); 558 | valueStack.pop(); 559 | t = valueStack.top() - t; 560 | valueStack.pop(); 561 | valueStack.push(t); 562 | break; 563 | case 3: // E -> T { E.v = T.v } 564 | break; 565 | case 4: // T -> T1 * F { T.v = T1.v * F.v} 566 | t = valueStack.top(); 567 | valueStack.pop(); 568 | valueStack.pop(); 569 | t *= valueStack.top(); 570 | valueStack.pop(); 571 | valueStack.push(t); 572 | break; 573 | case 5: // T -> T1 / F { T.v = T1.v / F.v} 574 | t = valueStack.top(); 575 | valueStack.pop(); 576 | valueStack.pop(); 577 | t = valueStack.top() / t; 578 | valueStack.pop(); 579 | valueStack.push(t); 580 | break; 581 | case 6: // T -> F { T.v = F.v } 582 | break; 583 | case 7: // F -> (E) { F.v = E.v } 584 | valueStack.pop(); 585 | t = valueStack.top(); 586 | valueStack.pop(); 587 | valueStack.top() = t; 588 | break; 589 | case 8: // F -> num { F.v = num.v } 590 | break; 591 | default: 592 | break; 593 | } 594 | } 595 | // 规约状态栈和符号栈 596 | int ntIndex; 597 | int candidateIndex; 598 | getCandidateIndex(action.index, ntIndex, candidateIndex); 599 | for (int i = 0; i < grammas[ntIndex][candidateIndex].size(); ++i) 600 | { 601 | stateStack.pop(); 602 | symbolStack.pop(); 603 | } 604 | // 规约后的状态转移 605 | SLR_Key gotoKey = {states[stateStack.top()], {Symbol::SymbolType::NT, ntIndex}}; 606 | auto gotoAction = slrTable[gotoKey]; 607 | outputAction(gotoAction); // output 608 | cout << endl; 609 | stateStack.push(gotoAction.index); 610 | symbolStack.push(ntTable[ntIndex]); 611 | --index; // do not increase index 612 | break; 613 | } 614 | case Action::ActionType::Shift: // 移进 615 | stateStack.push(action.index); 616 | symbolStack.push(candidate[index]); 617 | if (calculateResult) 618 | valueStack.push(values[index]); 619 | break; 620 | default: 621 | break; 622 | } 623 | ++index; 624 | } 625 | cout << "This line not belongs to this gramma.\n"; 626 | return false; 627 | } 628 | ``` 629 | 630 | ### 工作流程设计 631 | 632 | #### generate函数 633 | 634 | ```c++ 635 | bool GrammaTable::generate() 636 | { 637 | // 判断输入文法时是否出错 638 | if (error) 639 | return false; 640 | // 未出错 641 | getFirsts(); // 生成FIRST集 642 | getFollows(); // 生成FOLLOW集 643 | getDFA(); // 生成DFA 644 | getSLR_Table(); // 生成分析表 645 | return true; 646 | } 647 | ``` 648 | 649 | #### main函数 650 | 651 | ```c++ 652 | GrammaTable gt; // 声明文法表 653 | QString t; // 用于保存输入串 654 | 655 | // 文法表注入目标文法 656 | gt.insert("E' -> E"); 657 | gt.insert("E -> E+T | E-T | T"); 658 | gt.insert("T -> T*F | T/F | F"); 659 | gt.insert("F -> (E) | $num$"); 660 | 661 | gt.generate(); // 生成FIRST集、FOLLOW集、DFA和分析表 662 | cout << "Output:\n"; 663 | gt.output(); // 输出 664 | 665 | while (1) 666 | { 667 | // 输出提示信息 668 | cout << "\nPress 1: Just parse input.\nPress 2: Calculate result.\nOtherwise: Exit\n"; 669 | // 获取当前模式 670 | int mode = getch() - '0'; 671 | if (mode != 1 && mode != 2) // error input 672 | mode = 3; 673 | if (mode == 3) 674 | return 0; 675 | 676 | bool flag = true; 677 | while (flag) 678 | { 679 | cout << "\nInput a line to parse, input blank line to stop.\n"; 680 | getline(cin, t); 681 | if (t.length()) 682 | { 683 | // 未输入空串,解析 684 | gt.parse(t, mode == 2); 685 | } 686 | else 687 | { 688 | // 输入空串,结束循环 689 | flag = false; 690 | } 691 | } 692 | } 693 | ``` 694 | 695 | ## 测试与说明 696 | 697 | ### 初始输出 698 | 699 | 启动程序后生成First集、Follow集、DFA和分析表并输出这些内容: 700 | 701 | ``` 702 | ****************************************************************** 703 | Compiler-Grammatical-Analyzer-LR(CGA-LR) 704 | Written By DiscreteTom 705 | See source code and report BUG at 706 | https://github.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR 707 | ****************************************************************** 708 | 709 | Output: 710 | Format gramma: 711 | E' -> E 712 | E -> E+T | E-T | T 713 | T -> T*F | T/F | F 714 | F -> (E) | num 715 | 716 | Candidates with index: 717 | 0 E' -> E 718 | 1 E -> E+T 719 | 2 E -> E-T 720 | 3 E -> T 721 | 4 T -> T*F 722 | 5 T -> T/F 723 | 6 T -> F 724 | 7 F -> (E) 725 | 8 F -> num 726 | 727 | First sets: 728 | First(E'): num ( 729 | First(E): num ( 730 | First(T): num ( 731 | First(F): num ( 732 | 733 | Follow sets: 734 | Follow(E'): $ 735 | Follow(E): - + $ ) 736 | Follow(T): - + $ ) / * 737 | Follow(F): - + $ ) / * 738 | 739 | LR(0) DFA states: 740 | State[0]: 741 | E' -> .E 742 | E -> .E+T 743 | E -> .E-T 744 | E -> .T 745 | T -> .T*F 746 | T -> .T/F 747 | T -> .F 748 | F -> .(E) 749 | F -> .num 750 | State[1]: 751 | E' -> E. 752 | E -> E.+T 753 | E -> E.-T 754 | State[2]: 755 | E -> T. 756 | T -> T.*F 757 | T -> T./F 758 | State[3]: 759 | T -> F. 760 | State[4]: 761 | F -> (.E) 762 | E -> .E+T 763 | E -> .E-T 764 | E -> .T 765 | T -> .T*F 766 | T -> .T/F 767 | T -> .F 768 | F -> .(E) 769 | F -> .num 770 | State[5]: 771 | F -> num. 772 | State[6]: 773 | E -> E+.T 774 | T -> .T*F 775 | T -> .T/F 776 | T -> .F 777 | F -> .(E) 778 | F -> .num 779 | State[7]: 780 | E -> E-.T 781 | T -> .T*F 782 | T -> .T/F 783 | T -> .F 784 | F -> .(E) 785 | F -> .num 786 | State[8]: 787 | T -> T*.F 788 | F -> .(E) 789 | F -> .num 790 | State[9]: 791 | T -> T/.F 792 | F -> .(E) 793 | F -> .num 794 | State[10]: 795 | F -> (E.) 796 | E -> E.+T 797 | E -> E.-T 798 | State[11]: 799 | E -> E+T. 800 | T -> T.*F 801 | T -> T./F 802 | State[12]: 803 | E -> E-T. 804 | T -> T.*F 805 | T -> T./F 806 | State[13]: 807 | T -> T*F. 808 | State[14]: 809 | T -> T/F. 810 | State[15]: 811 | F -> (E). 812 | 813 | LR(0) DFA transition functions: 814 | 1 + '+' -> 6 815 | 1 + '-' -> 7 816 | 11 + '*' -> 8 817 | 11 + '/' -> 9 818 | 12 + '*' -> 8 819 | 12 + '/' -> 9 820 | 2 + '*' -> 8 821 | 2 + '/' -> 9 822 | 8 + 'F' -> 13 823 | 8 + '(' -> 4 824 | 8 + 'num' -> 5 825 | 9 + 'F' -> 14 826 | 9 + '(' -> 4 827 | 9 + 'num' -> 5 828 | 10 + '+' -> 6 829 | 10 + '-' -> 7 830 | 10 + ')' -> 15 831 | 6 + 'T' -> 11 832 | 6 + 'F' -> 3 833 | 6 + '(' -> 4 834 | 6 + 'num' -> 5 835 | 7 + 'T' -> 12 836 | 7 + 'F' -> 3 837 | 7 + '(' -> 4 838 | 7 + 'num' -> 5 839 | 0 + 'E' -> 1 840 | 0 + 'T' -> 2 841 | 0 + 'F' -> 3 842 | 0 + '(' -> 4 843 | 0 + 'num' -> 5 844 | 4 + 'E' -> 10 845 | 4 + 'T' -> 2 846 | 4 + 'F' -> 3 847 | 4 + '(' -> 4 848 | 4 + 'num' -> 5 849 | 850 | SLR_1 table: 851 | 13 + '$' -> R4 852 | 13 + '+' -> R4 853 | 13 + '-' -> R4 854 | 13 + '*' -> R4 855 | 13 + '/' -> R4 856 | 13 + ')' -> R4 857 | 14 + '$' -> R5 858 | 14 + '+' -> R5 859 | 14 + '-' -> R5 860 | 14 + '*' -> R5 861 | 14 + '/' -> R5 862 | 14 + ')' -> R5 863 | 3 + '$' -> R6 864 | 3 + '+' -> R6 865 | 3 + '-' -> R6 866 | 3 + '*' -> R6 867 | 3 + '/' -> R6 868 | 3 + ')' -> R6 869 | 15 + '$' -> R7 870 | 15 + '+' -> R7 871 | 15 + '-' -> R7 872 | 15 + '*' -> R7 873 | 15 + '/' -> R7 874 | 15 + ')' -> R7 875 | 5 + '$' -> R8 876 | 5 + '+' -> R8 877 | 5 + '-' -> R8 878 | 5 + '*' -> R8 879 | 5 + '/' -> R8 880 | 5 + ')' -> R8 881 | 1 + '$' -> ACC 882 | 1 + '+' -> S6 883 | 1 + '-' -> S7 884 | 11 + '$' -> R1 885 | 11 + '+' -> R1 886 | 11 + '-' -> R1 887 | 11 + '*' -> S8 888 | 11 + '/' -> S9 889 | 11 + ')' -> R1 890 | 12 + '$' -> R2 891 | 12 + '+' -> R2 892 | 12 + '-' -> R2 893 | 12 + '*' -> S8 894 | 12 + '/' -> S9 895 | 12 + ')' -> R2 896 | 2 + '$' -> R3 897 | 2 + '+' -> R3 898 | 2 + '-' -> R3 899 | 2 + '*' -> S8 900 | 2 + '/' -> S9 901 | 2 + ')' -> R3 902 | 8 + 'F' -> GOTO 13 903 | 8 + '(' -> S4 904 | 8 + 'num' -> S5 905 | 9 + 'F' -> GOTO 14 906 | 9 + '(' -> S4 907 | 9 + 'num' -> S5 908 | 10 + '+' -> S6 909 | 10 + '-' -> S7 910 | 10 + ')' -> S15 911 | 6 + 'T' -> GOTO 11 912 | 6 + 'F' -> GOTO 3 913 | 6 + '(' -> S4 914 | 6 + 'num' -> S5 915 | 7 + 'T' -> GOTO 12 916 | 7 + 'F' -> GOTO 3 917 | 7 + '(' -> S4 918 | 7 + 'num' -> S5 919 | 0 + 'E' -> GOTO 1 920 | 0 + 'T' -> GOTO 2 921 | 0 + 'F' -> GOTO 3 922 | 0 + '(' -> S4 923 | 0 + 'num' -> S5 924 | 4 + 'E' -> GOTO 10 925 | 4 + 'T' -> GOTO 2 926 | 4 + 'F' -> GOTO 3 927 | 4 + '(' -> S4 928 | 4 + 'num' -> S5 929 | 930 | Press 1: Just parse input. 931 | Press 2: Calculate result. 932 | Otherwise: Exit 933 | 934 | ``` 935 | 936 | ### 符号串解析 937 | 938 | 在初始界面按下1进入符号串解析状态。 939 | 940 | 输入: 941 | 942 | ``` 943 | $num$+$num$*$num$/$num$-$num$ 944 | ($num$+($num$-($num$*($num$/($num$))))) 945 | $num$ 946 | ($num$+($num$-($num$*($num$/($num$)))) 947 | $num$+($num$-($num$*($num$/($num$))))) 948 | ($num$+($num$-($nu*($num$/($num$)))) 949 | ``` 950 | 951 | 输出: 952 | 953 | ``` 954 | Input a line to parse, input blank line to stop. 955 | $num$+$num$*$num$/$num$-$num$ 956 | S5 957 | R8 958 | GOTO 3 959 | R6 960 | GOTO 2 961 | R3 962 | GOTO 1 963 | S6 964 | S5 965 | R8 966 | GOTO 3 967 | R6 968 | GOTO 11 969 | S8 970 | S5 971 | R8 972 | GOTO 13 973 | R4 974 | GOTO 11 975 | S9 976 | S5 977 | R8 978 | GOTO 14 979 | R5 980 | GOTO 11 981 | R1 982 | GOTO 1 983 | S7 984 | S5 985 | R8 986 | GOTO 3 987 | R6 988 | GOTO 12 989 | R2 990 | GOTO 1 991 | ACC 992 | Accepted. 993 | 994 | Input a line to parse, input blank line to stop. 995 | ($num$+($num$-($num$*($num$/($num$))))) 996 | S4 997 | S5 998 | R8 999 | GOTO 3 1000 | R6 1001 | GOTO 2 1002 | R3 1003 | GOTO 10 1004 | S6 1005 | S4 1006 | S5 1007 | R8 1008 | GOTO 3 1009 | R6 1010 | GOTO 2 1011 | R3 1012 | GOTO 10 1013 | S7 1014 | S4 1015 | S5 1016 | R8 1017 | GOTO 3 1018 | R6 1019 | GOTO 2 1020 | S8 1021 | S4 1022 | S5 1023 | R8 1024 | GOTO 3 1025 | R6 1026 | GOTO 2 1027 | S9 1028 | S4 1029 | S5 1030 | R8 1031 | GOTO 3 1032 | R6 1033 | GOTO 2 1034 | R3 1035 | GOTO 10 1036 | S15 1037 | R7 1038 | GOTO 14 1039 | R5 1040 | GOTO 2 1041 | R3 1042 | GOTO 10 1043 | S15 1044 | R7 1045 | GOTO 13 1046 | R4 1047 | GOTO 2 1048 | R3 1049 | GOTO 10 1050 | S15 1051 | R7 1052 | GOTO 3 1053 | R6 1054 | GOTO 12 1055 | R2 1056 | GOTO 10 1057 | S15 1058 | R7 1059 | GOTO 3 1060 | R6 1061 | GOTO 11 1062 | R1 1063 | GOTO 10 1064 | S15 1065 | R7 1066 | GOTO 3 1067 | R6 1068 | GOTO 2 1069 | R3 1070 | GOTO 1 1071 | ACC 1072 | Accepted. 1073 | 1074 | Input a line to parse, input blank line to stop. 1075 | $num$ 1076 | S5 1077 | R8 1078 | GOTO 3 1079 | R6 1080 | GOTO 2 1081 | R3 1082 | GOTO 1 1083 | ACC 1084 | Accepted. 1085 | 1086 | Input a line to parse, input blank line to stop. 1087 | ($num$+($num$-($num$*($num$/($num$)))) 1088 | S4 1089 | S5 1090 | R8 1091 | GOTO 3 1092 | R6 1093 | GOTO 2 1094 | R3 1095 | GOTO 10 1096 | S6 1097 | S4 1098 | S5 1099 | R8 1100 | GOTO 3 1101 | R6 1102 | GOTO 2 1103 | R3 1104 | GOTO 10 1105 | S7 1106 | S4 1107 | S5 1108 | R8 1109 | GOTO 3 1110 | R6 1111 | GOTO 2 1112 | S8 1113 | S4 1114 | S5 1115 | R8 1116 | GOTO 3 1117 | R6 1118 | GOTO 2 1119 | S9 1120 | S4 1121 | S5 1122 | R8 1123 | GOTO 3 1124 | R6 1125 | GOTO 2 1126 | R3 1127 | GOTO 10 1128 | S15 1129 | R7 1130 | GOTO 14 1131 | R5 1132 | GOTO 2 1133 | R3 1134 | GOTO 10 1135 | S15 1136 | R7 1137 | GOTO 13 1138 | R4 1139 | GOTO 2 1140 | R3 1141 | GOTO 10 1142 | S15 1143 | R7 1144 | GOTO 3 1145 | R6 1146 | GOTO 12 1147 | R2 1148 | GOTO 10 1149 | S15 1150 | R7 1151 | GOTO 3 1152 | R6 1153 | GOTO 11 1154 | R1 1155 | GOTO 10 1156 | This line not belongs to this gramma. 1157 | 1158 | Input a line to parse, input blank line to stop. 1159 | $num$+($num$-($num$*($num$/($num$))))) 1160 | S5 1161 | R8 1162 | GOTO 3 1163 | R6 1164 | GOTO 2 1165 | R3 1166 | GOTO 1 1167 | S6 1168 | S4 1169 | S5 1170 | R8 1171 | GOTO 3 1172 | R6 1173 | GOTO 2 1174 | R3 1175 | GOTO 10 1176 | S7 1177 | S4 1178 | S5 1179 | R8 1180 | GOTO 3 1181 | R6 1182 | GOTO 2 1183 | S8 1184 | S4 1185 | S5 1186 | R8 1187 | GOTO 3 1188 | R6 1189 | GOTO 2 1190 | S9 1191 | S4 1192 | S5 1193 | R8 1194 | GOTO 3 1195 | R6 1196 | GOTO 2 1197 | R3 1198 | GOTO 10 1199 | S15 1200 | R7 1201 | GOTO 14 1202 | R5 1203 | GOTO 2 1204 | R3 1205 | GOTO 10 1206 | S15 1207 | R7 1208 | GOTO 13 1209 | R4 1210 | GOTO 2 1211 | R3 1212 | GOTO 10 1213 | S15 1214 | R7 1215 | GOTO 3 1216 | R6 1217 | GOTO 12 1218 | R2 1219 | GOTO 10 1220 | S15 1221 | R7 1222 | GOTO 3 1223 | R6 1224 | GOTO 11 1225 | R1 1226 | GOTO 1 1227 | This line not belongs to this gramma. 1228 | 1229 | Input a line to parse, input blank line to stop. 1230 | ($num$+($num$-($nu*($num$/($num$)))) 1231 | Error input. 1232 | 1233 | Input a line to parse, input blank line to stop. 1234 | ``` 1235 | 1236 | 按下回车输入空行以回到初始界面。 1237 | 1238 | ### 表达式解析 1239 | 1240 | 在初始界面按下2进入表达式解析状态。 1241 | 1242 | 输入: 1243 | 1244 | ``` 1245 | $123$ 1246 | $66$+$66$*$66$/$66$-$66$ 1247 | ($123$+($789$-($789$*($123$/($123$))))) 1248 | ($123$+($123$-($123$*($123$/($123$)))) 1249 | $123$+($123$-($123$*($123$/($123$))))) 1250 | ``` 1251 | 1252 | 输出: 1253 | 1254 | ``` 1255 | Press 1: Just parse input. 1256 | Press 2: Calculate result. 1257 | Otherwise: Exit 1258 | 1259 | Input a line to parse, input blank line to stop. 1260 | $123$ 1261 | S5 1262 | R8 1263 | GOTO 3 1264 | R6 1265 | GOTO 2 1266 | R3 1267 | GOTO 1 1268 | ACC 1269 | Accepted. 1270 | Result is 123 1271 | 1272 | Input a line to parse, input blank line to stop. 1273 | $66$+$66$*$66$/$66$-$66$ 1274 | S5 1275 | R8 1276 | GOTO 3 1277 | R6 1278 | GOTO 2 1279 | R3 1280 | GOTO 1 1281 | S6 1282 | S5 1283 | R8 1284 | GOTO 3 1285 | R6 1286 | GOTO 11 1287 | S8 1288 | S5 1289 | R8 1290 | GOTO 13 1291 | R4 1292 | GOTO 11 1293 | S9 1294 | S5 1295 | R8 1296 | GOTO 14 1297 | R5 1298 | GOTO 11 1299 | R1 1300 | GOTO 1 1301 | S7 1302 | S5 1303 | R8 1304 | GOTO 3 1305 | R6 1306 | GOTO 12 1307 | R2 1308 | GOTO 1 1309 | ACC 1310 | Accepted. 1311 | Result is 66 1312 | 1313 | Input a line to parse, input blank line to stop. 1314 | ($123$+($789$-($789$*($123$/($123$))))) 1315 | S4 1316 | S5 1317 | R8 1318 | GOTO 3 1319 | R6 1320 | GOTO 2 1321 | R3 1322 | GOTO 10 1323 | S6 1324 | S4 1325 | S5 1326 | R8 1327 | GOTO 3 1328 | R6 1329 | GOTO 2 1330 | R3 1331 | GOTO 10 1332 | S7 1333 | S4 1334 | S5 1335 | R8 1336 | GOTO 3 1337 | R6 1338 | GOTO 2 1339 | S8 1340 | S4 1341 | S5 1342 | R8 1343 | GOTO 3 1344 | R6 1345 | GOTO 2 1346 | S9 1347 | S4 1348 | S5 1349 | R8 1350 | GOTO 3 1351 | R6 1352 | GOTO 2 1353 | R3 1354 | GOTO 10 1355 | S15 1356 | R7 1357 | GOTO 14 1358 | R5 1359 | GOTO 2 1360 | R3 1361 | GOTO 10 1362 | S15 1363 | R7 1364 | GOTO 13 1365 | R4 1366 | GOTO 2 1367 | R3 1368 | GOTO 10 1369 | S15 1370 | R7 1371 | GOTO 3 1372 | R6 1373 | GOTO 12 1374 | R2 1375 | GOTO 10 1376 | S15 1377 | R7 1378 | GOTO 3 1379 | R6 1380 | GOTO 11 1381 | R1 1382 | GOTO 10 1383 | S15 1384 | R7 1385 | GOTO 3 1386 | R6 1387 | GOTO 2 1388 | R3 1389 | GOTO 1 1390 | ACC 1391 | Accepted. 1392 | Result is 123 1393 | 1394 | Input a line to parse, input blank line to stop. 1395 | ($123$+($123$-($123$*($123$/($123$)))) 1396 | S4 1397 | S5 1398 | R8 1399 | GOTO 3 1400 | R6 1401 | GOTO 2 1402 | R3 1403 | GOTO 10 1404 | S6 1405 | S4 1406 | S5 1407 | R8 1408 | GOTO 3 1409 | R6 1410 | GOTO 2 1411 | R3 1412 | GOTO 10 1413 | S7 1414 | S4 1415 | S5 1416 | R8 1417 | GOTO 3 1418 | R6 1419 | GOTO 2 1420 | S8 1421 | S4 1422 | S5 1423 | R8 1424 | GOTO 3 1425 | R6 1426 | GOTO 2 1427 | S9 1428 | S4 1429 | S5 1430 | R8 1431 | GOTO 3 1432 | R6 1433 | GOTO 2 1434 | R3 1435 | GOTO 10 1436 | S15 1437 | R7 1438 | GOTO 14 1439 | R5 1440 | GOTO 2 1441 | R3 1442 | GOTO 10 1443 | S15 1444 | R7 1445 | GOTO 13 1446 | R4 1447 | GOTO 2 1448 | R3 1449 | GOTO 10 1450 | S15 1451 | R7 1452 | GOTO 3 1453 | R6 1454 | GOTO 12 1455 | R2 1456 | GOTO 10 1457 | S15 1458 | R7 1459 | GOTO 3 1460 | R6 1461 | GOTO 11 1462 | R1 1463 | GOTO 10 1464 | This line not belongs to this gramma. 1465 | 1466 | Input a line to parse, input blank line to stop. 1467 | $123$+($123$-($123$*($123$/($123$))))) 1468 | S5 1469 | R8 1470 | GOTO 3 1471 | R6 1472 | GOTO 2 1473 | R3 1474 | GOTO 1 1475 | S6 1476 | S4 1477 | S5 1478 | R8 1479 | GOTO 3 1480 | R6 1481 | GOTO 2 1482 | R3 1483 | GOTO 10 1484 | S7 1485 | S4 1486 | S5 1487 | R8 1488 | GOTO 3 1489 | R6 1490 | GOTO 2 1491 | S8 1492 | S4 1493 | S5 1494 | R8 1495 | GOTO 3 1496 | R6 1497 | GOTO 2 1498 | S9 1499 | S4 1500 | S5 1501 | R8 1502 | GOTO 3 1503 | R6 1504 | GOTO 2 1505 | R3 1506 | GOTO 10 1507 | S15 1508 | R7 1509 | GOTO 14 1510 | R5 1511 | GOTO 2 1512 | R3 1513 | GOTO 10 1514 | S15 1515 | R7 1516 | GOTO 13 1517 | R4 1518 | GOTO 2 1519 | R3 1520 | GOTO 10 1521 | S15 1522 | R7 1523 | GOTO 3 1524 | R6 1525 | GOTO 12 1526 | R2 1527 | GOTO 10 1528 | S15 1529 | R7 1530 | GOTO 3 1531 | R6 1532 | GOTO 11 1533 | R1 1534 | GOTO 1 1535 | This line not belongs to this gramma. 1536 | 1537 | Input a line to parse, input blank line to stop. 1538 | ``` 1539 | 1540 | 按下回车输入空行以回到初始界面。 1541 | 1542 | ## 运行截图 1543 | 1544 | - 开发者信息、文法和带下标的生成式 1545 | 1546 | ![1](img/1.png) 1547 | 1548 | - First集、Follow集与DFA状态 1549 | 1550 | ![2](img/2.png) 1551 | 1552 | - DFA转移函数 1553 | 1554 | ![3](img/3.png) 1555 | 1556 | - SLR(1)分析表 1557 | 1558 | ![4](img/4.png) 1559 | 1560 | - 提示信息 1561 | 1562 | ![5](img/5.png) 1563 | 1564 | - 解析简单符号串 1565 | 1566 | ![6](img/6.png) 1567 | 1568 | - 解析复杂符号串 1569 | 1570 | ![7](img/7.png) 1571 | 1572 | - 解析简单表达式 1573 | 1574 | ![8](img/8.png) 1575 | 1576 | - 解析复杂表达式 1577 | 1578 | ![9](img/9.png) 1579 | 1580 | 1581 | -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/3.png -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/4.png -------------------------------------------------------------------------------- /img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/5.png -------------------------------------------------------------------------------- /img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/6.png -------------------------------------------------------------------------------- /img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/7.png -------------------------------------------------------------------------------- /img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/8.png -------------------------------------------------------------------------------- /img/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscreteTom/Compiler-Grammatical-Analyzer-LR/2e9f2aa771663be4d390c7843b2e1ba4e0d37308/img/9.png --------------------------------------------------------------------------------