编译原理虎书作业4.2 记录&总结

原题:

Program 4.4:



本题有两点要注意,题目要求写出递归下降分析器,而给出的文法显然是有左递归的,因此要消除左递归;

另外就是本题的Yacc程序使用了优先级策略,在写出需要处理的文法时,我们要先通过引入额外的非终结符的方式解决优先级的问题,避免歧义。

消除直接左递归有标准的解决方案:



之后就是ll文法的老套路,找first集合、follow集合,找select集合写出文法分析表。

找select集合的方法:

相关知识已经足够,接下来就是一步一步写了。

原yacc程序形式化,并去除左递归以及添加优先级处理之后的语法:
1.prog->stm 			

2.stm->ID ASSIGN exp stm'
3.stm->PRINT LPAREN exps RPAREN stm'
4.stm'->semi_stm stm'
5.stm'->
6.semi_stm->SEMICOLON stm

7.exps->exp exps'
8.exps'->COMMA exp exps'
9.exps'->

10.exp->term exp'
11.exp->stm COMMA exp
12.exp'->PLUS term exp'
13.exp'->MINUS term exp'
14.exp'->

15.term->factor term'
16.term'->TIMES factor term'
17.term'->DIV factor term'
18.term'->

19.factor->INT
20.factor->ID
21.factor->LPAREN exp RPAREN


 	nullable first					follow
 prog   n        PRINT ID				\	
 stm 	n        PRINT ID 				SEMICOLON COMMA
 stm'   y        SEMICOLON				SEMICOLON COMMA
 semi_stm n      SEMICOLON				SEMICOLON
 exps   n        LPAREN PRINT ID INT 	                RPAREN
 exps'  y        COMMA					RPAREN
 exp    n        LPAREN PRINT ID INT 	                SEMICOLON COMMA RPAREN 
 exp'   y        PLUS MINUS				SEMICOLON COMMA RPAREN 
 term   n        INT ID LPAREN                          SEMICOLON COMMA RPAREN PLUS MINUS
 term'  y	 TIMES DIV				SEMICOLON COMMA RPAREN PLUS MINUS
 factor n        INT ID LPAREN			        SEMICOLON COMMA RPAREN PLUS MINUS TIMES DIV

ll(1)分析表:



最后给出我的代码:


enum token {
  ID,
  INT,
  ASSIGN,
  SEMICOLON,
  PRINT,
  LPAREN,
  RPAREN,
  COMMA,
  PLUS,
  MINUS,
  TIMES,
  DIV
};

union tokenval {
  string id;
  int num;
};

enum token tok;
union tokenval tokval;

typedef struct table *Table_;
Table_ {
  string id;
  int value;
  Table_ tail
};
Table_ Table(string id, int value, struct table *tail);
Table_ table = NULL;

void advance() { tok = getToken(); }
void eat(enum token t) {
  if (tok == t)
    advance();
  else
    error();
}
int lookup(Table_ table, string id) {
  assert(table != NULL);
  if (id == table.id)
    return table.value;
  else
    return lookup(table.tail, id);
}
void update(Table_ *tabptr, string id, int value) {
  *tabptr = Table(id, value, *tabptr);
}

// Assum that the tok's initial value is the first input token. If not, in
// Prog(), we can set it.

int Prog_follow[] = {};
int Prog() {
  switch (tok) {
    case ID:
    case PRINT: {
      return Stm();
    }
    default: {
      printf("Error: expect ID or PRINT!\n");
      return 0;
    }
  }
}

int Stm_follow[] = {SEMICOLON, COMMA};
int Stm() {
  switch (tok) {
    case ID: {  // tok is ID
      string id = tokval.id;
      if (lookahead() == ASSIGN) {
        advance();                  // tok is ASSIGN
        advance();                  // tok is exp's first token
        update(&table, id, Exp());  // update; semantic action

      } else {
        printf("Error: expect ASSIGN\n");
      }
      return StmPrime();
    }
    case PRINT: {  // tok is PRINT
      string id = tokval.id;
      if (lookahead() == LPAREN) {
        advance();     // tok is LPAREN
        advance();     // tok is exps's first token
        Exps();        // do exps
        printf("\n");  // semantic action
      } else {
        printf("expected ASSIGN");
      }
      eatOrSkipTo(RPAREN, Stm_follow);  // eat RPAREN

      return StmPrime();
    }
    default: {
      printf("Error: expect ID or PRINT!\n");
      skipto(Stm_follow);
      return 0;
    }
  }
}

int StmPrime_follow[] = {SEMICOLON, COMMA};
int StmPrime() {
  switch (tok) {
    case SEMICOLON: {  // tok is SEMICOLON
      SemiStm();       // do semi_stm which will change tok
      StmPrime();      // do stm'
      return 0;
    }
    case COMMA:
    default: {
      skipto(StmPrime_follow);
      return 0;
    }
  }
}

int SemiStm_follow = {SEMICOLON};
int SemiStm() {
  switch (tok) {
    case SEMICOLON: {
      advance();
      Stm();
      return 0;
    }
    default: {
      printf("Error: expect semicolon!\n");
      skipto(SemiStm_follow);
      return 0;
    }
  }
}

int Exps_follow = {RPAREN};
int Exps() {
  switch (tok) {
    case LPAREN:
    case PRINT:
    case ID:
    case INT: {
      printf("%d ", Exp());
      ExpsPrime();
    }
    default: {
      printf("Error: expect LPAREN,PRINT,ID or INT!\n");
      skipto(Exps_follow);
      return 0;
    }
  }
  return 0;
}

int ExpsPrime_follow[] = {RPAREN};
int ExpsPrime() {
  switch (tok) {
    case COMMA: {
      advance();  // tok is exp first token
      printf("%d ", Exp());
      ExpsPrime();
    }
    default: {
      skipto(ExpsPrime_follow);
      return 0;
    }
  }
  return 0;
}

int Exp_follow[] = {SEMICOLON, COMMA, RPAREN};
int Exp() {
  switch (tok) {
    case ID: {
      if (lookahead() == ASSIGN) {  // next token is ASSIGN, use stm production
        Stm();
        advance();  // comma
        int return_value =
            ExpPrime(Exp());  // pass this value to exp' as a tricky iteration

        return return_value;  // semantic action

      } else {
        return ExpPrime(
            Term());  // use term production and pass this value to exp'
      }
    }
    case INT: {
      return ExpPrime(
          Term());  // use term production and pass this value to exp'
    }
    case PRINT: {
      Stm();
      advance();  // comma
      int return_value = Exp();
      ExpPrime();           // do exp'
      return return_value;  // semantic action
    }
    case LPAREN: {
      return Term();
    }
    default: {
      printf("Error: expect LPAREN,PRINT,ID or INT!\n");
      skipto(Exp_follow);
      return 0;
    }
  }
}

int ExpPrime_follow[] = {SEMICOLON, COMMA, RPAREN};
int ExpPrime(int a) {
  switch (tok) {
    case PLUS: {
      advance();  // tok is term first token
      return ExpPrime(a + Term());
    }
    case MINUS: {
      advance();  // tok is term first token
      return ExpPrime(a - Term());
    }
    default: {
      skipto(ExpPrime_follow);
      return a;
    }
  }
  return 0;
}

int Term_follow[] = {SEMICOLON, COMMA, RPAREN, PLUS, MINUS};
int Term() {
  switch (tok) {
    case ID:
    case INT:
    case LPAREN: {
      return TermPrime(
          Factor());  // use factor production and pass this value to term'
    }
    default: {
      printf("Error: expect LPAREN,ID or INT!\n");
      skipto(Term_follow);
      return 0;
    }
  }
}

int TermPrime_follow[] = {SEMICOLON, COMMA, RPAREN, PLUS, MINUS};
int TermPrime(int a) {
  switch (tok) {
    case TIMES: {
      advance();  // tok is factor first token
      return TermPrime(a * Factor());
    }
    case DIV: {
      advance();  // tok is factor first token
      return ExpPrime(a / Factor());
    }
    default: {
      skipto(TermPrime_follow);
      return a;
    }
  }
  return 0;
}

int factor_follow[] = {SEMICOLON, COMMA, RPAREN, PLUS, MINUS, TIMES, DIV};
int Factor() {
  switch (tok) {
    case ID: {
      return lookup(table, tokval.id);  // semantic action
    }
    case INT: {
      return tokval.num;  // semantic action
    }
    case LPAREN: {
      advance();  // tok is exp first token now
      int return_value = Exp();
      eatOrSkipTo(RPAREN, factor_follow);  // eat RPAREN
      return return_value;
    }
    default: {
      printf("Error: expect LPAREN,ID or INT!\n");
      skipto(Term_follow);
      return 0;
    }
  }
}

总结:这道作业题花费了我非常非常长的时间,原因在于我刚开始没考虑优先级而且在写出文法分析表后,发现有duplicate item,然后就开始纠结,咨询了其他同学后,发现duplicate的item中有空产生式,而空产生式是可以处理的(至少我没看出问题QAQ),接着兴冲冲地开写,结果发现自己优先级还是没处理好……最后又折腾了两个小时才写好。加起来的时间都要有一整天了sigh.


猜你喜欢

转载自blog.csdn.net/ken_for_learning/article/details/79852540
今日推荐