自己动手实现一个脚本语言第三篇-------文法分析(二)歧义性的解决---未完成

895 阅读3分钟

语法生成树会生成歧义,这会影响我们程序解析的正确性。因此消除歧义十分重要。 我们将使用递归向下解析法来消除歧义。

语法的歧义性,

《Compilers Second Edition Principles, Techniques, & Tools》204页 由上图我们可以得知,同一个表达式根据不同的解析方法可以生成歧义的语法,这不是我们想要的,为了解决歧义性,根据乔姆斯基《文法分析》中的方法就是通过定义优先级和关联性规则的方式,来消除语法的歧义性。

优先级: 优先级高的运算符会先于优先级低的运算符

关联性:在一串优先级相同的运算符中,左关联指的是从左到右进行运算,右关联指的是从右到左进行运算。

这里定义优先级,就是先计算乘除,再计算加减。

ps:关于编译器前端的文法处理,这一本书讲的不错,但是没有中文版,想钻研的朋友去看看。

不同的编译器,语法的优先级规则也是不同的,这个根据你的语言的需求考虑,我们这里为了简化编译器的设计,我们考虑的是遵循和c语言一样的语法优先级

    名字                        操作符                       关联性

优先级从上向下,依次递减。

文法的解析法有很多种,如LL文法,LR文法,LALR文法等等。 我们这里采用的是递归向下解析法,这是所有文法中最简单的一种。虽然他很简单,但是可以支持复杂的错误处理,在很多语言的解析器里面,它都被使用的很频繁。

遇到终结符, 我们的解析就会去匹配词素

遇到非终结符, 我们的解析将继续。

遇到| 我们的解析就要进行判断

遇到*或者+ 我们的解析就要进行循环

遇到? 我们的解析进行判断

写代码开始

import java.util.List;                                 

import static com.craftinginterpreters.lox.TokenType.*;

class Parser {                                         
  private final List<Token> tokens;                    
  private int current = 0;                             

  Parser(List<Token> tokens) {                         
    this.tokens = tokens;                              
  }                                                    
}       

创建一个解析器 生成一个创造器, 声明词素,和一个intcurrent

我们要实现如下图的功能

第一个expression ---------->equality

private Expr expression() {
    return equality();       
 }         

然后是

    equality → comparison ( ( "!=" | "==" ) comparison )* ;

实现方法是这样的

 private Expr equality() {                         
    Expr expr = comparison();

    while (match(BANG_EQUAL, EQUAL_EQUAL)) {        
      Token operator = previous();                  
      Expr right = comparison();                    
      expr = new Expr.Binary(expr, operator, right);
    }                                               

    return expr;                                    
  }          

我们的equality方法中,包含了一个match方法

  private boolean match(TokenType... types) {
    for (TokenType type : types) {           
      if (check(type)) {                     
        advance();                           
        return true;                         
      }                                      
    }
    return false;                            
  }     

如果匹配成功,我们的解析器就前进,如果失败就退回去

 private Token advance() {   
    if (!isAtEnd()) current++;
    return previous();        
  }     

然后我们还需要几个辅助函数

 private boolean isAtEnd() {      
    return peek().type == EOF;     
  }

  private Token peek() {           
    return tokens.get(current);    
  }                                

  private Token previous() {       
    return tokens.get(current - 1);
  }             

我们继续

comparison → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;

翻译成java就是

  private Expr equality() {                         
    Expr expr = comparison();
    while (match(BANG_EQUAL, EQUAL_EQUAL)) {        
      Token operator = previous();                  
      Expr right = comparison();                    
      expr = new Expr.Binary(expr, operator, right);
    }                                               
    return expr;                                    
  }            

然后我们

  private boolean match(TokenType... types) {
    for (TokenType type : types) {           
      if (check(type)) {                     
        advance();                           
        return true;                         
      }                                      
    }
    return false;                            
  }     

接下来

 private boolean check(TokenType type) {
    if (isAtEnd()) return false;         
    return peek().type == type;          
  } 

  private Token advance() {   
    if (!isAtEnd()) current++;
    return previous();        
  }    
  
   private boolean isAtEnd() {      
     return peek().type == EOF;     
 }

  private Token peek() {           
    return tokens.get(current);    
  }                                

  private Token previous() {       
    return tokens.get(current - 1);
  }    

接下来继续

comparison → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;

然后

  private Expr comparison() {                                
    Expr expr = addition();

    while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
      Token operator = previous();                           
      Expr right = addition();                               
      expr = new Expr.Binary(expr, operator, right);         
    }                                                        

    return expr;                                             
  }         

private Expr addition() {
Expr expr = multiplication();

while (match(MINUS, PLUS)) {                    
  Token operator = previous();                  
  Expr right = multiplication();                
  expr = new Expr.Binary(expr, operator, right);
}                                               

  return expr;                                    
 }                                                 


  private Expr multiplication() {                   
    Expr expr = unary();                            

    while (match(SLASH, STAR)) {                    
      Token operator = previous();                  
      Expr right = unary();                         
      expr = new Expr.Binary(expr, operator, right);
    }                                               

    return expr;                                    
  }    

不得不说实现一个编译器是真的难, 今天有点累,下次继续。