From 475da5c849c3c4af4f7907fb960fe930e7dc4235 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Feb 2024 12:59:23 +0800 Subject: [PATCH 1/6] libnixf/Parse: parse expr_if --- libnixf/include/nixf/Basic/NodeKinds.inc | 1 + libnixf/include/nixf/Basic/Nodes/Expr.h | 20 +++++++ libnixf/src/Parse/ParseExpr.cpp | 76 ++++++++++++++++++++++++ libnixf/src/Parse/ParserImpl.h | 5 ++ 4 files changed, 102 insertions(+) diff --git a/libnixf/include/nixf/Basic/NodeKinds.inc b/libnixf/include/nixf/Basic/NodeKinds.inc index 2e1673139..cc4b940ba 100644 --- a/libnixf/include/nixf/Basic/NodeKinds.inc +++ b/libnixf/include/nixf/Basic/NodeKinds.inc @@ -39,5 +39,6 @@ EXPR(ExprList) EXPR(ExprLambda) EXPR(ExprBinOp) EXPR(ExprUnaryOp) +EXPR(ExprIf) #endif // EXPR diff --git a/libnixf/include/nixf/Basic/Nodes/Expr.h b/libnixf/include/nixf/Basic/Nodes/Expr.h index a3fafec32..7759971eb 100644 --- a/libnixf/include/nixf/Basic/Nodes/Expr.h +++ b/libnixf/include/nixf/Basic/Nodes/Expr.h @@ -88,4 +88,24 @@ class ExprList : public Expr { } }; +class ExprIf : public Expr { + std::unique_ptr Cond; + std::unique_ptr Then; + std::unique_ptr Else; + +public: + ExprIf(LexerCursorRange Range, std::unique_ptr Cond, + std::unique_ptr Then, std::unique_ptr Else) + : Expr(NK_ExprIf, Range), Cond(std::move(Cond)), Then(std::move(Then)), + Else(std::move(Else)) {} + + [[nodiscard]] Expr *cond() const { return Cond.get(); } + [[nodiscard]] Expr *then() const { return Then.get(); } + [[nodiscard]] Expr *elseExpr() const { return Else.get(); } + + [[nodiscard]] ChildVector children() const override { + return {Cond.get(), Then.get(), Else.get()}; + } +}; + } // namespace nixf diff --git a/libnixf/src/Parse/ParseExpr.cpp b/libnixf/src/Parse/ParseExpr.cpp index cd3342ea9..579c7211b 100644 --- a/libnixf/src/Parse/ParseExpr.cpp +++ b/libnixf/src/Parse/ParseExpr.cpp @@ -108,6 +108,8 @@ std::unique_ptr Parser::parseExpr() { } break; } // case tok_l_curly + case tok_kw_if: + return parseExprIf(); default: break; } @@ -115,4 +117,78 @@ std::unique_ptr Parser::parseExpr() { return parseExprOp(); } +std::unique_ptr Parser::parseExprIf() { + LexerCursor LCur = lCur(); // if + Token TokIf = peek(); + assert(TokIf.kind() == tok_kw_if && "parseExprIf should start with `if`"); + consume(); // if + assert(LastToken && "LastToken should be set after consume()"); + auto Cond = parseExpr(); + if (!Cond) { + Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "condition"); + D.fix("remove `if` keyword").edit(TextEdit::mkRemoval(TokIf.range())); + D.fix("insert dummy condition") + .edit(TextEdit::mkInsertion(TokIf.rCur(), "true")); + + // If the next token is not `then`, stop here. + if (peek().kind() != tok_kw_then) + return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, + std::move(Cond), nullptr, nullptr); + } + + Token TokThen = peek(); + if (TokThen.kind() != tok_kw_then) { + Diagnostic &D = Diags.emplace_back(Diagnostic::DK_Expected, + LexerCursorRange{LastToken->rCur()}); + D << std::string(tok::spelling(tok_kw_then)); + D.fix("insert `then` keyword") + .edit(TextEdit::mkInsertion(TokThen.lCur(), " then")); + Note &N = D.note(Note::NK_ToMachThis, TokIf.range()); + N << std::string(tok::spelling(tok_kw_if)); + + return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, + std::move(Cond), nullptr, nullptr); + } + + consume(); // then + + auto Then = parseExpr(); + if (!Then) { + Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "then"); + Note &N = D.note(Note::NK_ToMachThis, TokIf.range()); + N << std::string(tok::spelling(tok_kw_if)); + + // If the next token is not `then`, stop here. + if (peek().kind() != tok_kw_else) + return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, + std::move(Cond), std::move(Then), + nullptr); + } + + Token TokElse = peek(); + if (TokElse.kind() != tok_kw_else) { + Diagnostic &D = + Diags.emplace_back(Diagnostic::DK_Expected, TokElse.range()); + D << std::string(tok::spelling(tok_kw_else)); + D.fix("insert `else` keyword") + .edit(TextEdit::mkInsertion(TokElse.lCur(), " else")); + + return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, + std::move(Cond), std::move(Then), nullptr); + } + + consume(); // else + + auto Else = parseExpr(); + if (!Else) { + Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "else"); + Note &N = D.note(Note::NK_ToMachThis, TokIf.range()); + N << std::string(tok::spelling(tok_kw_if)); + } + + return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, + std::move(Cond), std::move(Then), + std::move(Else)); +} + } // namespace nixf diff --git a/libnixf/src/Parse/ParserImpl.h b/libnixf/src/Parse/ParserImpl.h index afb93238b..19794c100 100644 --- a/libnixf/src/Parse/ParserImpl.h +++ b/libnixf/src/Parse/ParserImpl.h @@ -323,6 +323,11 @@ class Parser { /// %nonassoc NEGATE /// \endcode std::unique_ptr parseExprOp() { return parseExprOpBP(0); } + + /// \code + /// expr_if : 'if' expr 'then' expr 'else' expr + /// \endcode + std::unique_ptr parseExprIf(); std::unique_ptr parse() { return parseExpr(); } }; From 2fd06dfdf824485eb4d958ebed7f2f3ca1444c82 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Feb 2024 13:04:50 +0800 Subject: [PATCH 2/6] libnixf/Parse: return `ExprIf` for parseExprIf method --- libnixf/src/Parse/ParseExpr.cpp | 2 +- libnixf/src/Parse/ParserImpl.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libnixf/src/Parse/ParseExpr.cpp b/libnixf/src/Parse/ParseExpr.cpp index 579c7211b..3794c2e14 100644 --- a/libnixf/src/Parse/ParseExpr.cpp +++ b/libnixf/src/Parse/ParseExpr.cpp @@ -117,7 +117,7 @@ std::unique_ptr Parser::parseExpr() { return parseExprOp(); } -std::unique_ptr Parser::parseExprIf() { +std::unique_ptr Parser::parseExprIf() { LexerCursor LCur = lCur(); // if Token TokIf = peek(); assert(TokIf.kind() == tok_kw_if && "parseExprIf should start with `if`"); diff --git a/libnixf/src/Parse/ParserImpl.h b/libnixf/src/Parse/ParserImpl.h index 19794c100..560d1ba7d 100644 --- a/libnixf/src/Parse/ParserImpl.h +++ b/libnixf/src/Parse/ParserImpl.h @@ -30,6 +30,7 @@ class Formal; class Formals; class LambdaArg; class ExprLambda; +class ExprIf; namespace detail { @@ -327,7 +328,7 @@ class Parser { /// \code /// expr_if : 'if' expr 'then' expr 'else' expr /// \endcode - std::unique_ptr parseExprIf(); + std::unique_ptr parseExprIf(); std::unique_ptr parse() { return parseExpr(); } }; From b7c992ec6b84f6c23e7b47829876c6f7ac7e44d4 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Feb 2024 13:08:39 +0800 Subject: [PATCH 3/6] libnixf/Parse: add /* */ hints for parsing expr_if --- libnixf/src/Parse/ParseExpr.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libnixf/src/Parse/ParseExpr.cpp b/libnixf/src/Parse/ParseExpr.cpp index 3794c2e14..d28b35308 100644 --- a/libnixf/src/Parse/ParseExpr.cpp +++ b/libnixf/src/Parse/ParseExpr.cpp @@ -133,7 +133,8 @@ std::unique_ptr Parser::parseExprIf() { // If the next token is not `then`, stop here. if (peek().kind() != tok_kw_then) return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, - std::move(Cond), nullptr, nullptr); + std::move(Cond), /*Then=*/nullptr, + /*Else=*/nullptr); } Token TokThen = peek(); @@ -147,7 +148,8 @@ std::unique_ptr Parser::parseExprIf() { N << std::string(tok::spelling(tok_kw_if)); return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, - std::move(Cond), nullptr, nullptr); + std::move(Cond), /*Then=*/nullptr, + /*Else=*/nullptr); } consume(); // then @@ -162,7 +164,7 @@ std::unique_ptr Parser::parseExprIf() { if (peek().kind() != tok_kw_else) return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond), std::move(Then), - nullptr); + /*Else=*/nullptr); } Token TokElse = peek(); @@ -174,7 +176,8 @@ std::unique_ptr Parser::parseExprIf() { .edit(TextEdit::mkInsertion(TokElse.lCur(), " else")); return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, - std::move(Cond), std::move(Then), nullptr); + std::move(Cond), std::move(Then), + /*Else=*/nullptr); } consume(); // else From 1989578cfbec555cac9bc2af1b068fa298ff967f Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Feb 2024 13:10:01 +0800 Subject: [PATCH 4/6] libnixf/Parse: remove early exit comments in expr_if As the code is self-explanatory, the comments are not needed. --- libnixf/src/Parse/ParseExpr.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libnixf/src/Parse/ParseExpr.cpp b/libnixf/src/Parse/ParseExpr.cpp index d28b35308..cc586e895 100644 --- a/libnixf/src/Parse/ParseExpr.cpp +++ b/libnixf/src/Parse/ParseExpr.cpp @@ -130,7 +130,6 @@ std::unique_ptr Parser::parseExprIf() { D.fix("insert dummy condition") .edit(TextEdit::mkInsertion(TokIf.rCur(), "true")); - // If the next token is not `then`, stop here. if (peek().kind() != tok_kw_then) return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond), /*Then=*/nullptr, @@ -160,7 +159,6 @@ std::unique_ptr Parser::parseExprIf() { Note &N = D.note(Note::NK_ToMachThis, TokIf.range()); N << std::string(tok::spelling(tok_kw_if)); - // If the next token is not `then`, stop here. if (peek().kind() != tok_kw_else) return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond), std::move(Then), From 4e8f6fe8f6f180bd66cb04dc4c18d0ab4e275279 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sun, 11 Feb 2024 12:47:05 +0800 Subject: [PATCH 5/6] libnixf/test/Parse: unit test for expr_if --- libnixf/test/Parse/ParseExpr.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libnixf/test/Parse/ParseExpr.cpp b/libnixf/test/Parse/ParseExpr.cpp index 5a056eff5..21f117bc1 100644 --- a/libnixf/test/Parse/ParseExpr.cpp +++ b/libnixf/test/Parse/ParseExpr.cpp @@ -234,4 +234,25 @@ TEST(Parser, ExprDispatch_r_curly_id_ellipsis) { ASSERT_EQ(AST->kind(), Node::NK_ExprLambda); } +TEST(Parser, ExprIf) { + // if ... then ... else ... + auto Src = R"(if 1 then cc else "d")"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + ASSERT_EQ(AST->kind(), Node::NK_ExprIf); + + ASSERT_TRUE(AST->lCur().isAt(0, 0, 0)); + ASSERT_TRUE(AST->rCur().isAt(0, 21, 21)); + + ExprIf &If = *static_cast(AST.get()); + + ASSERT_EQ(If.cond()->kind(), Node::NK_ExprInt); + ASSERT_EQ(If.then()->kind(), Node::NK_ExprVar); + ASSERT_EQ(If.elseExpr()->kind(), Node::NK_ExprString); +} + } // namespace From 537f86c70dc1b19ba8217181689ccf60737c4e52 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sun, 11 Feb 2024 15:24:10 +0800 Subject: [PATCH 6/6] libnixf/Parse: sync tokens for expr_if --- libnixf/src/Parse/ParseExpr.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/libnixf/src/Parse/ParseExpr.cpp b/libnixf/src/Parse/ParseExpr.cpp index cc586e895..67fc5def9 100644 --- a/libnixf/src/Parse/ParseExpr.cpp +++ b/libnixf/src/Parse/ParseExpr.cpp @@ -123,6 +123,10 @@ std::unique_ptr Parser::parseExprIf() { assert(TokIf.kind() == tok_kw_if && "parseExprIf should start with `if`"); consume(); // if assert(LastToken && "LastToken should be set after consume()"); + + auto SyncThen = withSync(tok_kw_then); + auto SyncElse = withSync(tok_kw_else); + auto Cond = parseExpr(); if (!Cond) { Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "condition"); @@ -136,16 +140,11 @@ std::unique_ptr Parser::parseExprIf() { /*Else=*/nullptr); } - Token TokThen = peek(); - if (TokThen.kind() != tok_kw_then) { - Diagnostic &D = Diags.emplace_back(Diagnostic::DK_Expected, - LexerCursorRange{LastToken->rCur()}); - D << std::string(tok::spelling(tok_kw_then)); - D.fix("insert `then` keyword") - .edit(TextEdit::mkInsertion(TokThen.lCur(), " then")); + ExpectResult ExpKwThen = expect(tok_kw_then); + if (!ExpKwThen.ok()) { + Diagnostic &D = ExpKwThen.diag(); Note &N = D.note(Note::NK_ToMachThis, TokIf.range()); N << std::string(tok::spelling(tok_kw_if)); - return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond), /*Then=*/nullptr, /*Else=*/nullptr); @@ -165,18 +164,11 @@ std::unique_ptr Parser::parseExprIf() { /*Else=*/nullptr); } - Token TokElse = peek(); - if (TokElse.kind() != tok_kw_else) { - Diagnostic &D = - Diags.emplace_back(Diagnostic::DK_Expected, TokElse.range()); - D << std::string(tok::spelling(tok_kw_else)); - D.fix("insert `else` keyword") - .edit(TextEdit::mkInsertion(TokElse.lCur(), " else")); - + ExpectResult ExpKwElse = expect(tok_kw_else); + if (!ExpKwElse.ok()) return std::make_unique(LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond), std::move(Then), /*Else=*/nullptr); - } consume(); // else