diff --git a/libnixf/include/nixf/Basic/NodeKinds.inc b/libnixf/include/nixf/Basic/NodeKinds.inc index 06fecacd1..a76fb4671 100644 --- a/libnixf/include/nixf/Basic/NodeKinds.inc +++ b/libnixf/include/nixf/Basic/NodeKinds.inc @@ -39,6 +39,7 @@ EXPR(ExprList) EXPR(ExprLambda) EXPR(ExprBinOp) EXPR(ExprUnaryOp) +EXPR(ExprOpHasAttr) EXPR(ExprIf) EXPR(ExprAssert) EXPR(ExprLet) diff --git a/libnixf/include/nixf/Basic/Nodes/Op.h b/libnixf/include/nixf/Basic/Nodes/Op.h index 3456eeca2..cad538ff6 100644 --- a/libnixf/include/nixf/Basic/Nodes/Op.h +++ b/libnixf/include/nixf/Basic/Nodes/Op.h @@ -2,6 +2,7 @@ #include "Basic.h" +#include "nixf/Basic/Nodes/Attrs.h" #include "nixf/Basic/TokenKinds.h" #include @@ -53,6 +54,24 @@ class ExprBinOp : public ExprOp { } }; +class ExprOpHasAttr : public ExprOp { + std::unique_ptr E; + std::unique_ptr Path; + +public: + ExprOpHasAttr(LexerCursorRange Range, std::unique_ptr O, + std::unique_ptr E, std::unique_ptr Path) + : ExprOp(NK_ExprOpHasAttr, Range, std::move(O)), E(std::move(E)), + Path(std::move(Path)) {} + + [[nodiscard]] Expr *expr() const { return E.get(); } + [[nodiscard]] AttrPath *attrpath() const { return Path.get(); } + + [[nodiscard]] ChildVector children() const override { + return {E.get(), Path.get()}; + } +}; + class ExprUnaryOp : public ExprOp { std::unique_ptr E; diff --git a/libnixf/src/Parse/ParseOp.cpp b/libnixf/src/Parse/ParseOp.cpp index af6352310..bf7c9b449 100644 --- a/libnixf/src/Parse/ParseOp.cpp +++ b/libnixf/src/Parse/ParseOp.cpp @@ -132,6 +132,18 @@ std::unique_ptr Parser::parseExprOpBP(unsigned LeftRBP) { std::move(Prefix), std::move(RHS)); break; } + case tok_question: { + // expr_op '?' attrpath + consume(); + assert(LastToken && "consume() should have set LastToken"); + auto O = std::make_unique(Tok.range(), Tok.kind()); + + std::unique_ptr Path = parseAttrPath(); + LexerCursorRange Range{Prefix->lCur(), LastToken->rCur()}; + Prefix = std::make_unique( + Range, std::move(O), std::move(Prefix), std::move(Path)); + break; + } default: return Prefix; } diff --git a/libnixf/test/Parse/ParseOp.cpp b/libnixf/test/Parse/ParseOp.cpp index a7cbcc338..840f7fb91 100644 --- a/libnixf/test/Parse/ParseOp.cpp +++ b/libnixf/test/Parse/ParseOp.cpp @@ -42,4 +42,33 @@ TEST(Parser, UnaryOp) { ASSERT_EQ(UnaryOp.expr()->kind(), Node::NK_ExprSelect); } +TEST(Parser, OpHasAttr) { + auto Src = R"(a ? b.c.d)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + + ASSERT_EQ(AST->kind(), Node::NK_ExprOpHasAttr); + + ASSERT_EQ(Diags.size(), 0); +} + +TEST(Parser, OpHasAttr_empty) { + auto Src = R"(a ?)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + + ASSERT_EQ(AST->kind(), Node::NK_ExprOpHasAttr); + + // libnixf accepts emtpy attrpath while parsing. + ASSERT_EQ(Diags.size(), 0); +} + } // namespace