Skip to content

Commit

Permalink
libnixf/Parse: parse expr_if (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc authored Feb 11, 2024
2 parents f12a29f + 537f86c commit f2d8b09
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 0 deletions.
1 change: 1 addition & 0 deletions libnixf/include/nixf/Basic/NodeKinds.inc
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ EXPR(ExprList)
EXPR(ExprLambda)
EXPR(ExprBinOp)
EXPR(ExprUnaryOp)
EXPR(ExprIf)

#endif // EXPR
20 changes: 20 additions & 0 deletions libnixf/include/nixf/Basic/Nodes/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,24 @@ class ExprList : public Expr {
}
};

class ExprIf : public Expr {
std::unique_ptr<Expr> Cond;
std::unique_ptr<Expr> Then;
std::unique_ptr<Expr> Else;

public:
ExprIf(LexerCursorRange Range, std::unique_ptr<Expr> Cond,
std::unique_ptr<Expr> Then, std::unique_ptr<Expr> 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
69 changes: 69 additions & 0 deletions libnixf/src/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,80 @@ std::unique_ptr<Expr> Parser::parseExpr() {
}
break;
} // case tok_l_curly
case tok_kw_if:
return parseExprIf();
default:
break;
}

return parseExprOp();
}

std::unique_ptr<ExprIf> 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 SyncThen = withSync(tok_kw_then);
auto SyncElse = withSync(tok_kw_else);

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 (peek().kind() != tok_kw_then)
return std::make_unique<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Cond), /*Then=*/nullptr,
/*Else=*/nullptr);
}

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<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Cond), /*Then=*/nullptr,
/*Else=*/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 (peek().kind() != tok_kw_else)
return std::make_unique<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Cond), std::move(Then),
/*Else=*/nullptr);
}

ExpectResult ExpKwElse = expect(tok_kw_else);
if (!ExpKwElse.ok())
return std::make_unique<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Cond), std::move(Then),
/*Else=*/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<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Cond), std::move(Then),
std::move(Else));
}

} // namespace nixf
6 changes: 6 additions & 0 deletions libnixf/src/Parse/ParserImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Formal;
class Formals;
class LambdaArg;
class ExprLambda;
class ExprIf;

namespace detail {

Expand Down Expand Up @@ -323,6 +324,11 @@ class Parser {
/// %nonassoc NEGATE
/// \endcode
std::unique_ptr<Expr> parseExprOp() { return parseExprOpBP(0); }

/// \code
/// expr_if : 'if' expr 'then' expr 'else' expr
/// \endcode
std::unique_ptr<ExprIf> parseExprIf();
std::unique_ptr<Expr> parse() { return parseExpr(); }
};

Expand Down
21 changes: 21 additions & 0 deletions libnixf/test/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Diagnostic> 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<ExprIf *>(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

0 comments on commit f2d8b09

Please sign in to comment.