/CTD_FinalTerm

Môn chương trình dịch Việt Nhật - ĐH Bách Khoa Hà Nội. Đề cuối kì chương trình dịch thầy Đức.

Primary LanguageC

Môn Thực Hành Chương trình dịch 20192.

Thông tin

Giáo viên: Trần Vĩnh Đức

Môn học: Thực hành chương trình dịch.

Lớp: CNTT Việt Nhật - ĐH Bách Khoa Hà Nội.

Cách làm

Trong các file có mã TODO theo bài -> Search theo TODO

Cách chạy code

Fix lỗi thiếu thư viện curse.h trên ubuntu khi chạy bị báo lỗi.

$ sudo apt-get install libncurses5
$ sudo apt-get install libncurses5-dev

Trong thư mục completed chạy từng câu lệnh sau trong terminal:

$ make clean 
$ make
$ ./kplc ../tests/example2.kpl ../tests/ex2  -dump 

Make lại file chạy kplc => Chạy để biên dịch file example2.kpl thành file binary ex2. Thêm option -dump để xem mã trong Code Buffer sau khi đọc file KPL

Trong thư mục interpreter chạy các lệnh sau trong terminal:

$ make clean
$ make 
$ ./kplrun ../tests/ex2

Make lại file chạy kplrun => Chạy file binary ex2 vừa tạo ra để xem kết quả.

Bài 1: Thêm hàm mũ vào văn phạm.

Code TODO:1 => Code ở branch: feauture/bai1

token.h

  • Thêm SB_EXP vào enum TokenType

token.c

  • Thêm case SB_EXP vào tokenToString()

scanner.c

  • Thêm case SB_EXP vào printToken()
  • Sửa lại case CHAR_TIMES trong ham getToken

parser.c

  • Thêm case SB_EXP vào compileTerm2 => Đọc lại văn phạm.

codegen.h

  • Thêm định nghĩa hàm genEXP(void)

codegen.c

  • Thêm hàm genEXP(void) => Sinh code của hàm mũ

instructions.h trong thư mục completed, interpreter

  • Thêm định nghĩa hàm emitEXP() xử lý việc tính toán hàm mũ
  • Thêm định nghĩa OP_EXP vào enum Opcode => opcode của phép toán mũ

instructions.c trong thư mục completed, interpreter

  • Thêm hàm emitEXP => Tạo mã lệnh xử lý phép toán mũ trong Code buffer
  • Thêm case OP_EXP vào hàm printInstruction() => In lệnh OP_EXP

vm.c

  • Thêm thư viện #include <math.h> => Tính hàm mũ
  • Thêm case OP_EXP vào hàm run() => Xử lý tính toán trong stack

Bài 2: Thêm switch case vào văn phạm.

Code TODO:2 => Code ở branch feature/bai2

token.h

  • Thêm KW_SWITCH, KW_CASE, KW_DEFAULT, KW_BREAK vào enum TokenType
  • Sửa lại KEYWORDS_COUNT = 24 => Thêm 4 keyword mới vào Token

token.c

  • Thêm các keyword mới vào struct keywords[]
  • Thêm case của keyword vào tokenToString()

scanner.c

  • Thêm case của keyword mới vào printToken();

parser.h

  • Thêm định nghĩa hàm compileSwitchSt();

parser.c

  • #define MAX_CASE 10 để tạo các jInstruction trong hàm case.
  • Thêm case KW_SWITCH vào complileStatement => Đọc thêm văn phạm mới.
  • Thêm case KW_BREAK, KW_DEFAULT, KW_CASE vào compileStatement() => Follow của compileStatement(); (Sau khi compileStatement thì nó sẽ gặp những case này thì sẽ bỏ qua không xử lý Statement).
  • Thêm case KW_BREAK, KW_DEFAULT, KW_CASE, KW_BEGIN vào hàm compileExpression3() và hàm compileTerm2()=> Follow của hàm compileExpression(); Sau khi compileExpression() ở switch thì nó sẽ gặp những case này cần bỏ qua.
  • Viết thêm hàm compileSwitchSt();
void compileSwitchSt(void ) {
  Instruction* fjInstruction;
  Instruction* jInstructions[MAX_CASE];
  Type* type;
  int count = 0;

  eat(KW_SWITCH);                                   // Ăn KW_SWITCH
  type = compileExpression();                       // compileExpression() => Đẩy giá trị của biến lên trong switch lên đầu stack
  eat(KW_BEGIN);                                    // Ăn KW_BEGIN 
  
  while(lookAhead->tokenType != KW_END) {           
    genCV();                                        // Duplicate giá trị biến vừa đẩy lên stack                           
    switch(lookAhead->tokenType) {                  // Check KW_CASE hay là KW_DEFAULT
      case KW_CASE:
        eat(KW_CASE);
        switch (lookAhead->tokenType) {
          case TK_NUMBER:
            eat(TK_NUMBER);
            checkIntType(type);
            genLC(currentToken->value);             // Load giá trị của constant lên đầu stack
            break;
          case TK_CHAR:
            eat(TK_CHAR);
            checkCharType(type);
            genLC(currentToken->value);             // Load giá trị của constant lên đầu stack
            break;
          default:
            error(ERR_INVALID_CONSTANT, lookAhead->lineNo, lookAhead->colNo);
        }
        // genEQ()  => So sánh 2 giá trị trên cùng của stack 
        genEQ();                                    
        // Tạo 1 False Jump nếu đúng thì thực hiện tiếp còn ko thì nhảy đến vị trí được updateFJ();
        Instruction* fjInstruction = genFJ(DC_VALUE);       
        eat(SB_COLON);
        compileStatements();
        if(lookAhead->tokenType == KW_BREAK) {
          eat(KW_BREAK);
          // Tạo 1 Jump nếu như code trong case đó được thực hiện và gặp KW_BREAK thì nhảy ra ngoài switch-case bằng cách updateJ() ngoài switch-case
          jInstructions[count] = genJ(DC_VALUE);            
          count++;
        }
        // Nếu FJ trả về false thì sẽ nhảy đến đây.
        updateFJ(fjInstruction, getCurrentCodeAddress());       
        break;
      case KW_DEFAULT:
        eat(KW_DEFAULT);
        eat(SB_COLON);
        compileStatements();
        break;
      default:
        error(ERR_INVALID_STATEMENT, lookAhead->lineNo, lookAhead->colNo);
        break;
    }
  } 
  // Update tất cả các hàm J để sau khi tính toán gặp KW_sBREAK thì sẽ nhảy ra đây
  for(int i = 0; i < count; i++) {
    updateJ(jInstructions[i], getCurrentCodeAddress());
  }
  eat(KW_END);              // Ăn KW_END 
}