/// NumberExprAST - Expression class for numeric literals like "1.0".
class NumberExprAST : public ExprAST {
double Val;
+
public:
- NumberExprAST(double val) : Val(val) {}
+ NumberExprAST(double Val) : Val(Val) {}
};
The code above shows the definition of the base ExprAST class and one
/// VariableExprAST - Expression class for referencing a variable, like "a".
class VariableExprAST : public ExprAST {
std::string Name;
+
public:
- VariableExprAST(const std::string &name) : Name(name) {}
+ VariableExprAST(const std::string &Name) : Name(Name) {}
};
/// BinaryExprAST - Expression class for a binary operator.
class BinaryExprAST : public ExprAST {
char Op;
- ExprAST *LHS, *RHS;
+ std::unique_ptr<ExprAST> LHS, RHS;
+
public:
- BinaryExprAST(char op, ExprAST *lhs, ExprAST *rhs)
- : Op(op), LHS(lhs), RHS(rhs) {}
+ BinaryExprAST(char op, std::unique_ptr<ExprAST> LHS,
+ std::unique_ptr<ExprAST> RHS)
+ : Op(op), LHS(std::move(LHS)), RHS(std::move(RHS)) {}
};
/// CallExprAST - Expression class for function calls.
class CallExprAST : public ExprAST {
std::string Callee;
- std::vector<ExprAST*> Args;
+ std::vector<std::unique_ptr<ExprAST>> Args;
+
public:
- CallExprAST(const std::string &callee, std::vector<ExprAST*> &args)
- : Callee(callee), Args(args) {}
+ CallExprAST(const std::string &Callee,
+ std::vector<std::unique_ptr<ExprAST>> Args)
+ : Callee(Callee), Args(std::move(Args)) {}
};
This is all (intentionally) rather straight-forward: variables capture
class PrototypeAST {
std::string Name;
std::vector<std::string> Args;
+
public:
- PrototypeAST(const std::string &name, const std::vector<std::string> &args)
- : Name(name), Args(args) {}
+ PrototypeAST(const std::string &name, std::vector<std::string> Args)
+ : Name(name), Args(std::move(Args)) {}
};
/// FunctionAST - This class represents a function definition itself.
class FunctionAST {
- PrototypeAST *Proto;
- ExprAST *Body;
+ std::unique_ptr<PrototypeAST> Proto;
+ std::unique_ptr<ExprAST> Body;
+
public:
- FunctionAST(PrototypeAST *proto, ExprAST *body)
- : Proto(proto), Body(body) {}
+ FunctionAST(std::unique_ptr<PrototypeAST> Proto,
+ std::unique_ptr<ExprAST> Body)
+ : Proto(std::move(Proto)), Body(std::move(Body)) {}
};
In Kaleidoscope, functions are typed with just a count of their
.. code-block:: c++
- ExprAST *X = new VariableExprAST("x");
- ExprAST *Y = new VariableExprAST("y");
- ExprAST *Result = new BinaryExprAST('+', X, Y);
+ auto LHS = llvm::make_unique<VariableExprAST>("x");
+ auto RHS = llvm::make_unique<VariableExprAST>("y");
+ auto Result = std::make_unique<BinaryExprAST>('+', std::move(LHS),
+ std::move(RHS));
In order to do this, we'll start by defining some basic helper routines:
/// Error* - These are little helper functions for error handling.
- ExprAST *Error(const char *Str) { fprintf(stderr, "Error: %s\n", Str);return 0;}
- PrototypeAST *ErrorP(const char *Str) { Error(Str); return 0; }
- FunctionAST *ErrorF(const char *Str) { Error(Str); return 0; }
+ std::unique_ptr<ExprAST> Error(const char *Str) {
+ fprintf(stderr, "Error: %s\n", Str);
+ return nullptr;
+ }
+ std::unique_ptr<PrototypeAST> ErrorP(const char *Str) {
+ Error(Str);
+ return nullptr;
+ }
The ``Error`` routines are simple helper routines that our parser will
use to handle errors. The error recovery in our parser will not be the
.. code-block:: c++
/// numberexpr ::= number
- static ExprAST *ParseNumberExpr() {
- ExprAST *Result = new NumberExprAST(NumVal);
+ static std::unique_ptr<ExprAST> ParseNumberExpr() {
+ auto Result = llvm::make_unique<NumberExprAST>(NumVal);
getNextToken(); // consume the number
- return Result;
+ return std::move(Result);
}
This routine is very simple: it expects to be called when the current
.. code-block:: c++
/// parenexpr ::= '(' expression ')'
- static ExprAST *ParseParenExpr() {
- getNextToken(); // eat (.
- ExprAST *V = ParseExpression();
- if (!V) return 0;
+ static std::unique_ptr<ExprAST> ParseParenExpr() {
+ getNextToken(); // eat (.
+ auto V = ParseExpression();
+ if (!V)
+ return nullptr;
if (CurTok != ')')
return Error("expected ')'");
- getNextToken(); // eat ).
+ getNextToken(); // eat ).
return V;
}
/// identifierexpr
/// ::= identifier
/// ::= identifier '(' expression* ')'
- static ExprAST *ParseIdentifierExpr() {
+ static std::unique_ptr<ExprAST> ParseIdentifierExpr() {
std::string IdName = IdentifierStr;
getNextToken(); // eat identifier.
if (CurTok != '(') // Simple variable ref.
- return new VariableExprAST(IdName);
+ return llvm::make_unique<VariableExprAST>(IdName);
// Call.
getNextToken(); // eat (
- std::vector<ExprAST*> Args;
+ std::vector<std::unique_ptr<ExprAST>> Args;
if (CurTok != ')') {
while (1) {
- ExprAST *Arg = ParseExpression();
- if (!Arg) return 0;
- Args.push_back(Arg);
+ if (auto Arg = ParseExpression())
+ Args.push_back(std::move(Arg));
+ else
+ return nullptr;
- if (CurTok == ')') break;
+ if (CurTok == ')')
+ break;
if (CurTok != ',')
return Error("Expected ')' or ',' in argument list");
// Eat the ')'.
getNextToken();
- return new CallExprAST(IdName, Args);
+ return llvm::make_unique<CallExprAST>(IdName, std::move(Args));
}
This routine follows the same style as the other routines. (It expects
can define a helper function to wrap it together into one entry point.
We call this class of expressions "primary" expressions, for reasons
that will become more clear `later in the
-tutorial <LangImpl6.html#unary>`_. In order to parse an arbitrary
+tutorial <LangImpl6.html#user-defined-unary-operators>`_. In order to parse an arbitrary
primary expression, we need to determine what sort of expression it is:
.. code-block:: c++
/// ::= identifierexpr
/// ::= numberexpr
/// ::= parenexpr
- static ExprAST *ParsePrimary() {
+ static std::unique_ptr<ExprAST> ParsePrimary() {
switch (CurTok) {
- default: return Error("unknown token when expecting an expression");
- case tok_identifier: return ParseIdentifierExpr();
- case tok_number: return ParseNumberExpr();
- case '(': return ParseParenExpr();
+ default:
+ return Error("unknown token when expecting an expression");
+ case tok_identifier:
+ return ParseIdentifierExpr();
+ case tok_number:
+ return ParseNumberExpr();
+ case '(':
+ return ParseParenExpr();
}
}
With the helper above defined, we can now start parsing binary
expressions. The basic idea of operator precedence parsing is to break
down an expression with potentially ambiguous binary operators into
-pieces. Consider ,for example, the expression "a+b+(c+d)\*e\*f+g".
+pieces. Consider, for example, the expression "a+b+(c+d)\*e\*f+g".
Operator precedence parsing considers this as a stream of primary
expressions separated by binary operators. As such, it will first parse
the leading primary expression "a", then it will see the pairs [+, b]
/// expression
/// ::= primary binoprhs
///
- static ExprAST *ParseExpression() {
- ExprAST *LHS = ParsePrimary();
- if (!LHS) return 0;
+ static std::unique_ptr<ExprAST> ParseExpression() {
+ auto LHS = ParsePrimary();
+ if (!LHS)
+ return nullptr;
- return ParseBinOpRHS(0, LHS);
+ return ParseBinOpRHS(0, std::move(LHS));
}
``ParseBinOpRHS`` is the function that parses the sequence of pairs for
/// binoprhs
/// ::= ('+' primary)*
- static ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS) {
+ static std::unique_ptr<ExprAST> ParseBinOpRHS(int ExprPrec,
+ std::unique_ptr<ExprAST> LHS) {
// If this is a binop, find its precedence.
while (1) {
int TokPrec = GetTokPrecedence();
getNextToken(); // eat binop
// Parse the primary expression after the binary operator.
- ExprAST *RHS = ParsePrimary();
- if (!RHS) return 0;
+ auto RHS = ParsePrimary();
+ if (!RHS)
+ return nullptr;
As such, this code eats (and remembers) the binary operator and then
parses the primary expression that follows. This builds up the whole
}
// Merge LHS/RHS.
- LHS = new BinaryExprAST(BinOp, LHS, RHS);
+ LHS = llvm::make_unique<BinaryExprAST>(BinOp, std::move(LHS),
+ std::move(RHS));
} // loop around to the top of the while loop.
}
// the pending operator take RHS as its LHS.
int NextPrec = GetTokPrecedence();
if (TokPrec < NextPrec) {
- RHS = ParseBinOpRHS(TokPrec+1, RHS);
- if (RHS == 0) return 0;
+ RHS = ParseBinOpRHS(TokPrec+1, std::move(RHS));
+ if (!RHS)
+ return nullptr;
}
// Merge LHS/RHS.
- LHS = new BinaryExprAST(BinOp, LHS, RHS);
+ LHS = llvm::make_unique<BinaryExprAST>(BinOp, std::move(LHS),
+ std::move(RHS));
} // loop around to the top of the while loop.
}
/// prototype
/// ::= id '(' id* ')'
- static PrototypeAST *ParsePrototype() {
+ static std::unique_ptr<PrototypeAST> ParsePrototype() {
if (CurTok != tok_identifier)
return ErrorP("Expected function name in prototype");
// success.
getNextToken(); // eat ')'.
- return new PrototypeAST(FnName, ArgNames);
+ return llvm::make_unique<PrototypeAST>(FnName, std::move(ArgNames));
}
Given this, a function definition is very simple, just a prototype plus
.. code-block:: c++
/// definition ::= 'def' prototype expression
- static FunctionAST *ParseDefinition() {
+ static std::unique_ptr<FunctionAST> ParseDefinition() {
getNextToken(); // eat def.
- PrototypeAST *Proto = ParsePrototype();
- if (Proto == 0) return 0;
+ auto Proto = ParsePrototype();
+ if (!Proto) return nullptr;
- if (ExprAST *E = ParseExpression())
- return new FunctionAST(Proto, E);
- return 0;
+ if (auto E = ParseExpression())
+ return llvm::make_unique<FunctionAST>(std::move(Proto), std::move(E));
+ return nullptr;
}
In addition, we support 'extern' to declare functions like 'sin' and
.. code-block:: c++
/// external ::= 'extern' prototype
- static PrototypeAST *ParseExtern() {
+ static std::unique_ptr<PrototypeAST> ParseExtern() {
getNextToken(); // eat extern.
return ParsePrototype();
}
.. code-block:: c++
/// toplevelexpr ::= expression
- static FunctionAST *ParseTopLevelExpr() {
- if (ExprAST *E = ParseExpression()) {
+ static std::unique_ptr<FunctionAST> ParseTopLevelExpr() {
+ if (auto E = ParseExpression()) {
// Make an anonymous proto.
- PrototypeAST *Proto = new PrototypeAST("", std::vector<std::string>());
- return new FunctionAST(Proto, E);
+ auto Proto = llvm::make_unique<PrototypeAST>("", std::vector<std::string>());
+ return llvm::make_unique<FunctionAST>(std::move(Proto), std::move(E));
}
- return 0;
+ return nullptr;
}
Now that we have all the pieces, let's build a little driver that will
The driver for this simply invokes all of the parsing pieces with a
top-level dispatch loop. There isn't much interesting here, so I'll just
-include the top-level loop. See `below <#code>`_ for full code in the
+include the top-level loop. See `below <#full-code-listing>`_ for full code in the
"Top-Level Parsing" section.
.. code-block:: c++
while (1) {
fprintf(stderr, "ready> ");
switch (CurTok) {
- case tok_eof: return;
- case ';': getNextToken(); break; // ignore top-level semicolons.
- case tok_def: HandleDefinition(); break;
- case tok_extern: HandleExtern(); break;
- default: HandleTopLevelExpression(); break;
+ case tok_eof:
+ return;
+ case ';': // ignore top-level semicolons.
+ getNextToken();
+ break;
+ case tok_def:
+ HandleDefinition();
+ break;
+ case tok_extern:
+ HandleExtern();
+ break;
+ default:
+ HandleTopLevelExpression();
+ break;
}
}
}