LLVM12第三章之语法分析

1、Cale语言介绍

1.1、cale语言的语法形式(无二义性且非左递归文法)

在这里插入图片描述

1.2、cale语言示例

在这里插入图片描述

2、语法分析器

2.1、Parser 类定义

Parser用于解析词法分析的token,并构建AST语法树。
该类中主要有8个方法:

  1. void error()方法用于打印语法分析的错误信息。
  2. void advance()方法用于获取下一个要识别的token对象。
  3. bool expect(Token::TokenKind Kind)方法用于判断当前识别的token是否为预期的类型。如果不是,打印错误信息并返回true,否则返回false。
  4. bool consume(Token::TokenKind Kind)方法的含义是如果当前token类型为预期类型,让token属性获取到下一个Token类型的对象,并返回false。如果当前token类型不为预期类型,打印错误信息并返回true。
  5. AST *parseCalc(); Expr *parseExpr(); Expr *parseTerm(); Expr *parseFactor();四个方法分别用于解析Calc、Expr、Term、Factor四个非终结符。
class Parser {
  Lexer &Lex;  // 词法分析器对象
  Token Tok;   // 词法分析得到的Token对象
  bool HasError;   // 语法分析中是否存在语法错误的标识

  void error() {
    llvm::errs() << "Unexpected: " << Tok.getText() << "\n";
    HasError = true;
  }

  void advance() { Lex.next(Tok); }

  bool expect(Token::TokenKind Kind) {
    if (!Tok.is(Kind)) {
      error();
      return true;
    }
    return false;
  }

  bool consume(Token::TokenKind Kind) {
    if (expect(Kind))
      return true;
    advance();
    return false;
  }

  AST *parseCalc();
  Expr *parseExpr();
  Expr *parseTerm();
  Expr *parseFactor();

public:
  Parser(Lexer &Lex) : Lex(Lex), HasError(false) {
    advance();
  }
  AST *parse();
  bool hasError() { return HasError; }
};

2.2、AST类定义

2.2.1、AST(虚函数类)
class AST {
public:
  virtual ~AST() {}
  virtual void accept(ASTVisitor &V) = 0;
};
2.2.2、Expr(虚函数类)
class Expr : public AST {
public:
  Expr() {}
};
2.2.3、Factor类

Factor类记录一个标识符或一个数字,其中Kind为ValueKind::Ident或者ValueKind::Number。

class Factor : public Expr {
public:
  enum ValueKind { Ident, Number };

private:
  ValueKind Kind;
  llvm::StringRef Val;

public:
  Factor(ValueKind Kind, llvm::StringRef Val)
      : Kind(Kind), Val(Val) {}
  ValueKind getKind() { return Kind; }
  llvm::StringRef getVal() { return Val; }
  virtual void accept(ASTVisitor &V) override {
    V.visit(*this);
  }
};
2.2.4、BinaryOp类

BinaryOp类用于记录一个二元运算表达式。其中Op记录二元运算的符号,Left、Right分别记录二元运算的左、右表达式。

class BinaryOp : public Expr {
public:
  enum Operator { Plus, Minus, Mul, Div };

private:
  Expr *Left;
  Expr *Right;
  Operator Op;

public:
  BinaryOp(Operator Op, Expr *L, Expr *R)
      : Op(Op), Left(L), Right(R) {}
  Expr *getLeft() { return Left; }
  Expr *getRight() { return Right; }
  Operator getOperator() { return Op; }
  virtual void accept(ASTVisitor &V) override {
    V.visit(*this);
  }
};
2.2.5、WithDecl类

Cala类用于封装一个Cala语言的语句,其中采用VarVector容器记录所有形参的名称,E指向该语句的表达式。比如在with a,b:a*(4+b)中,VarVector存储两个字符串a、b,E指向表达式对象a*(4+b)。

class WithDecl : public AST
{
  using VarVector = llvm::SmallVector<llvm::StringRef, 8>;
  VarVector Vars;
  Expr *E;

public:
  WithDecl(llvm::SmallVector<llvm::StringRef, 8> Vars,
           Expr *E)
      : Vars(Vars), E(E) {}
  VarVector::const_iterator begin() { return Vars.begin(); }
  VarVector::const_iterator end() { return Vars.end(); }
  Expr *getExpr() { return E; }
  virtual void accept(ASTVisitor &V) override
  {
    V.visit(*this);
  }
};

2.3、Parser实现(自顶向下的语法分析)

2.3.1、parseCalc()方法实现

parseCalc()方法用于解析一条完整的Calc语句。Calc语句的推导式为在这里插入图片描述

AST *Parser::parseCalc() {
  Expr *E; // 该指针指向Calc语句的表达式对象
  llvm::SmallVector<llvm::StringRef, 8> Vars;  // 该容器用于保存calc语句的形参
  // 如果当前Token为with关键字
  if (Tok.is(Token::KW_with)) {
    // 获取下一个Token
    advance();
    // 如果下一个Token类型不是标识符,跳转到错误处理语句
    if (expect(Token::ident))
      goto _error;
    // 该标识符为形参,保存到Vars容器中
    Vars.push_back(Tok.getText());
    // 获取下一个Token对象
    advance();
    // 如果Token对象类型为逗号,表示后续还有形参,通过while循环遍历所有形参Token,保存到Var容器中
    while (Tok.is(Token::comma)) {
      advance();
      if (expect(Token::ident))
        goto _error;
      Vars.push_back(Tok.getText());
      advance();
    }
    // 读取完形参后,如果下一个Token不是冒号,跳转到错误处理语句
    if (consume(Token::colon))
      goto _error;
  }
  // 解析Calc语句中的表达式
  E = parseExpr();
  // 如果表达式后续的Token不是文本终止符,跳转到错误处理
  if (expect(Token::eoi))
    goto _error;
  // 一条Calc语句可能没有with关键字和形参,此时直接返回表达式E
  if (Vars.empty())
    return E;
  else
  // 此时Calc语句既有形参,也有表达式,返回WithDecl对象
    return new WithDecl(Vars, E);
  // 错误处理:遍历后续所有Token但不处理,遍历完后返回nullptr。
_error:
  while (Tok.getKind() != Token::eoi)
    advance();
  return nullptr;
}
2.3.2、parseExpr()方法实现

parseExpr()方法用于解析一个expr。expr的推导式为:
在这里插入图片描述


Expr *Parser::parseExpr() {
  // 解析一个term
  Expr *Left = parseTerm();
  // while循环解析后续的运算符号+term
  while (Tok.isOneOf(Token::plus, Token::minus)) {
    BinaryOp::Operator Op = Tok.is(Token::plus)
                                ? BinaryOp::Plus
                                : BinaryOp::Minus;
    advance();
    Expr *Right = parseTerm();
    Left = new BinaryOp(Op, Left, Right);
  }
  return Left;
}
2.3.3、parseTerm()方法

在这里插入图片描述

Expr *Parser::parseTerm() {
  Expr *Left = parseFactor();
  while (Tok.isOneOf(Token::star, Token::slash)) {
    BinaryOp::Operator Op =
        Tok.is(Token::star) ? BinaryOp::Mul : BinaryOp::Div;
    advance();
    Expr *Right = parseFactor();
    Left = new BinaryOp(Op, Left, Right);
  }
  return Left;
}
2.3.4、

在这里插入图片描述


Expr *Parser::parseFactor() {
  Expr *Res = nullptr;
  switch (Tok.getKind()) {
  case Token::number:
    Res = new Factor(Factor::Number, Tok.getText());
    advance(); break;
  case Token::ident:
    Res = new Factor(Factor::Ident, Tok.getText());
    advance(); break;
  case Token::l_paren:
    advance();
    Res = parseExpr();
    if (!consume(Token::r_paren)) break;
  default:
  /// lifetime extends past that of the StringRef.
    if (!Res)
      error();
    while (!Tok.isOneOf(Token::r_paren, Token::star,
                        Token::plus, Token::minus,
                        Token::slash, Token::eoi))
      advance();
  }
  return Res;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值