diff --git a/libnixf/include/nixf/Basic/NodeKinds.inc b/libnixf/include/nixf/Basic/NodeKinds.inc index f0bd23589..06fecacd1 100644 --- a/libnixf/include/nixf/Basic/NodeKinds.inc +++ b/libnixf/include/nixf/Basic/NodeKinds.inc @@ -41,5 +41,6 @@ EXPR(ExprBinOp) EXPR(ExprUnaryOp) EXPR(ExprIf) EXPR(ExprAssert) +EXPR(ExprLet) #endif // EXPR diff --git a/libnixf/include/nixf/Basic/Nodes/Expr.h b/libnixf/include/nixf/Basic/Nodes/Expr.h index 74fe3866f..9ade208f4 100644 --- a/libnixf/include/nixf/Basic/Nodes/Expr.h +++ b/libnixf/include/nixf/Basic/Nodes/Expr.h @@ -126,4 +126,31 @@ class ExprAssert : public Expr { } }; +class ExprLet : public Expr { + // 'let' binds 'in' expr + + std::unique_ptr KwLet; // 'let', not null + std::unique_ptr B; + std::unique_ptr KwIn; + std::unique_ptr E; + +public: + ExprLet(LexerCursorRange Range, std::unique_ptr KwLet, + std::unique_ptr B, std::unique_ptr KwIn, + std::unique_ptr E) + : Expr(NK_ExprLet, Range), KwLet(std::move(KwLet)), B(std::move(B)), + KwIn(std::move(KwIn)), E(std::move(E)) { + assert(this->KwLet && "KwLet should not be empty!"); + } + + [[nodiscard]] Binds *binds() const { return B.get(); } + [[nodiscard]] Expr *expr() const { return E.get(); } + [[nodiscard]] Misc &let() const { return *KwLet; } + [[nodiscard]] Misc *in() const { return KwIn.get(); } + + [[nodiscard]] ChildVector children() const override { + return {KwLet.get(), B.get(), KwIn.get(), E.get()}; + } +}; + } // namespace nixf diff --git a/libnixf/src/Parse/ParseExpr.cpp b/libnixf/src/Parse/ParseExpr.cpp index 9fca4b5b5..9454e9e3b 100644 --- a/libnixf/src/Parse/ParseExpr.cpp +++ b/libnixf/src/Parse/ParseExpr.cpp @@ -112,6 +112,10 @@ std::unique_ptr Parser::parseExpr() { return parseExprIf(); case tok_kw_assert: return parseExprAssert(); + case tok_kw_let: + if (peek(1).kind() != tok_l_curly) + return parseExprLet(); + break; default: break; } @@ -229,4 +233,42 @@ std::unique_ptr Parser::parseExprAssert() { std::move(Cond), std::move(Value)); } +std::unique_ptr Parser::parseExprLet() { + LexerCursor LCur = lCur(); + Token TokLet = peek(); + assert(TokLet.kind() == tok_kw_let && + "first token should be tok_kw_let in parseExprLet()"); + + auto Let = std::make_unique(TokLet.range()); + + consume(); // 'let' + + auto SyncIn = withSync(tok_kw_in); + + assert(LastToken && "LastToken should be set after consume()"); + + auto Binds = parseBinds(); + + ExpectResult ExpKwIn = expect(tok_kw_in); + + if (!ExpKwIn.ok()) + // missing 'in' + return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, + std::move(Let), std::move(Binds), + /*KwIn=*/nullptr, + /*E=*/nullptr); + + auto In = std::make_unique(ExpKwIn.tok().range()); + + consume(); // 'in' + + auto E = parseExpr(); + if (!E) + diagNullExpr(Diags, LastToken->rCur(), "let ... in"); + + return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, + std::move(Let), std::move(Binds), + std::move(In), std::move(E)); +} + } // namespace nixf diff --git a/libnixf/src/Parse/ParserImpl.h b/libnixf/src/Parse/ParserImpl.h index ba436c7f2..8aafd7d77 100644 --- a/libnixf/src/Parse/ParserImpl.h +++ b/libnixf/src/Parse/ParserImpl.h @@ -32,6 +32,7 @@ class LambdaArg; class ExprLambda; class ExprIf; class ExprAssert; +class ExprLet; namespace detail { @@ -336,6 +337,11 @@ class Parser { /// \endcode std::unique_ptr parseExprAssert(); + /// \code + /// epxr_let : 'let' binds 'in' expr + /// \endcode + std::unique_ptr parseExprLet(); + std::unique_ptr parse() { return parseExpr(); } }; diff --git a/libnixf/test/Parse/ParseExpr.cpp b/libnixf/test/Parse/ParseExpr.cpp index 69952e4e4..f0a8bcb08 100644 --- a/libnixf/test/Parse/ParseExpr.cpp +++ b/libnixf/test/Parse/ParseExpr.cpp @@ -275,4 +275,31 @@ TEST(Parser, ExprAssert) { ASSERT_EQ(Assert.value()->kind(), Node::NK_ExprString); } +TEST(Parser, ExprLet) { + auto Src = R"(let x = 1; in 1)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + ASSERT_EQ(AST->kind(), Node::NK_ExprLet); + + Binds &B = *static_cast(AST.get())->binds(); + ASSERT_EQ(B.bindings().size(), 1); +} + +TEST(Parser, ExprLet_Binds) { + auto Src = R"(let in 1)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + ASSERT_EQ(AST->kind(), Node::NK_ExprLet); + + ASSERT_FALSE(static_cast(AST.get())->binds()); +} + } // namespace