1、Cale语言介绍
1.1、cale语言的语法形式(无二义性且非左递归文法)
1.2、cale语言示例
2、语法分析器
2.1、Parser 类定义
Parser用于解析词法分析的token,并构建AST语法树。
该类中主要有8个方法:
- void error()方法用于打印语法分析的错误信息。
- void advance()方法用于获取下一个要识别的token对象。
- bool expect(Token::TokenKind Kind)方法用于判断当前识别的token是否为预期的类型。如果不是,打印错误信息并返回true,否则返回false。
- bool consume(Token::TokenKind Kind)方法的含义是如果当前token类型为预期类型,让token属性获取到下一个Token类型的对象,并返回false。如果当前token类型不为预期类型,打印错误信息并返回true。
- 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;
}