Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libnixf: parse lambda_arg #338

Merged
merged 1 commit into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libnixf/include/nixf/Basic/DiagnosticKinds.inc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ DIAG("parse-select-extra-dot", SelectExtraDot, Error, "extra `.` after expressio
DIAG("parse-unexpected-between", UnexpectedBetween, Error, "unexpected {} between {} and {}")
DIAG("parse-unexpected", UnexpectedText, Error, "unexpected text")
DIAG("parse-missing-sep-formals", MissingSepFormals, Error, "missing seperator `,` between two lambda formals")
DIAG("parse-lambda-arg-extra-at", LambdaArgExtraAt, Error, "extra `@` for lambda arg")
DIAG("let-dynamic", LetDynamic, Error,
"dynamic attributes are not allowed in let ... in ... expression")
DIAG("empty-inherit", EmptyInherit, Warning, "empty inherit expression")
Expand Down
3 changes: 3 additions & 0 deletions libnixf/include/nixf/Basic/NodeKinds.inc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ NODE(AttrPath)
NODE(Binding)
NODE(Inherit)
NODE(Binds)
NODE(LambdaArg)
NODE(Formals)
NODE(Formal)

#endif // NODE

Expand Down
86 changes: 86 additions & 0 deletions libnixf/include/nixf/Basic/Nodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,92 @@ class ExprList : public Expr {
}
};

class Formal : public Node {
std::unique_ptr<Misc> Comma;
std::unique_ptr<Identifier> ID;
std::unique_ptr<Expr> Default;
std::unique_ptr<Misc> Ellipsis; // ...

public:
Formal(LexerCursorRange Range, std::unique_ptr<Misc> Comma,
std::unique_ptr<Identifier> ID, std::unique_ptr<Expr> Default)
: Node(NK_Formal, Range), Comma(std::move(Comma)), ID(std::move(ID)),
Default(std::move(Default)) {}

Formal(LexerCursorRange Range, std::unique_ptr<Misc> Comma,
std::unique_ptr<Misc> Ellipsis)
: Node(NK_Formal, Range), Comma(std::move(Comma)),
Ellipsis(std::move(Ellipsis)) {
assert(this->Ellipsis && "Ellipsis must not be null");
}

[[nodiscard]] Misc &ellipsis() const {
assert(Ellipsis && "Ellipsis must not be null");
return *Ellipsis;
}

[[nodiscard]] bool isEllipsis() const { return Ellipsis != nullptr; }

[[nodiscard]] Identifier *id() const { return ID.get(); }

[[nodiscard]] Misc *comma() const { return Comma.get(); }

[[nodiscard]] Expr *defaultExpr() const { return Default.get(); }

[[nodiscard]] ChildVector children() const override {
if (isEllipsis()) {
return {Ellipsis.get()};
}
return {ID.get(), Default.get()};
}
};

/// \brief Lambda formal arguments.
///
/// Things to check:
/// 1. Ellipsis can only occur at the end of the formals.
/// { ..., pkgs } -> { pkgs, ... }
/// 2. Ellipsis can only occur once.
/// { b, ..., a, ... } -> { a, ... }
class Formals : public Node {
std::vector<std::unique_ptr<Formal>> Members;

public:
Formals(LexerCursorRange Range, std::vector<std::unique_ptr<Formal>> Members)
: Node(NK_Formals, Range), Members(std::move(Members)) {}

[[nodiscard]] const std::vector<std::unique_ptr<Formal>> &members() const {
return Members;
}

[[nodiscard]] ChildVector children() const override {
ChildVector Children;
Children.reserve(Members.size());
for (const auto &Member : Members) {
Children.emplace_back(Member.get());
}
return Children;
}
};

class LambdaArg : public Node {
std::unique_ptr<Identifier> ID;
std::unique_ptr<Formals> F;

public:
LambdaArg(LexerCursorRange Range, std::unique_ptr<Identifier> ID,
std::unique_ptr<Formals> F)
: Node(NK_LambdaArg, Range), ID(std::move(ID)), F(std::move(F)) {}

[[nodiscard]] Identifier *id() { return ID.get(); }

[[nodiscard]] Formals *formals() const { return F.get(); }

[[nodiscard]] ChildVector children() const override {
return {ID.get(), F.get()};
}
};

//===----------------------------------------------------------------------===//
// Semantic nodes
//===----------------------------------------------------------------------===//
Expand Down
131 changes: 131 additions & 0 deletions libnixf/src/Parse/Parser.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
/// \file
/// \brief Parser implementation.
#include "Parser.h"
#include "Token.h"

#include "nixf/Basic/Nodes.h"
#include "nixf/Basic/Range.h"

#include <cassert>
#include <charconv>

namespace {
Expand Down Expand Up @@ -595,4 +599,131 @@ std::unique_ptr<ExprList> Parser::parseExprList() {
std::move(Exprs));
}

std::unique_ptr<Formal> Parser::parseFormal() {
// formal : ,? ID
// | ,? ID '?' expr
// | ,? ...

LexerCursor LCur = lCur();
std::unique_ptr<Misc> Comma = nullptr;
if (Token Tok = peek(); Tok.kind() == tok_comma) {
consume();
Comma = std::make_unique<Misc>(Tok.range());
}
if (Token Tok = peek(); Tok.kind() == tok_id) {
consume(); // ID
assert(LastToken && "LastToken should be set after consume()");
auto ID =
std::make_unique<Identifier>(Tok.range(), std::string(Tok.view()));
if (peek().kind() != tok_question)
return std::make_unique<Formal>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Comma), std::move(ID), nullptr);
consume(); // ?
std::unique_ptr<Expr> Default = parseExpr();
if (!Default)
diagNullExpr(Diags, LastToken->rCur(), "default value");
return std::make_unique<Formal>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Comma), std::move(ID),
std::move(Default));
}
if (Token Tok = peek(); Tok.kind() == tok_ellipsis) {
consume(); // ...
assert(LastToken && "LastToken should be set after consume()");
std::unique_ptr<Misc> Ellipsis = std::make_unique<Misc>(Tok.range());
return std::make_unique<Formal>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Comma), std::move(Ellipsis));
}

if (Comma) {
assert(LastToken && "LastToken should be set after consume()");
return std::make_unique<Formal>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Comma), /*ID=*/nullptr,
/*Default=*/nullptr);
}
return nullptr;
}

std::unique_ptr<Formals> Parser::parseFormals() {
ExpectResult ER = expect(tok_l_curly);
if (!ER.ok())
return nullptr;
Token TokLCurly = ER.tok();
consume(); // {
assert(LastToken && "LastToken should be set after consume()");
auto SyncRCurly = withSync(tok_r_curly);
auto SyncComma = withSync(tok_comma);
auto SyncQuestion = withSync(tok_question);
auto SyncID = withSync(tok_id);
LexerCursor LCur = ER.tok().lCur();
std::vector<std::unique_ptr<Formal>> Members;
while (true) {
if (Token Tok = peek(); Tok.kind() == tok_r_curly)
break;
std::unique_ptr<Formal> Formal = parseFormal();
if (Formal) {
Members.emplace_back(std::move(Formal));
continue;
}
if (removeUnexpected())
continue;
break;
}
if (ExpectResult ER = expect(tok_r_curly); ER.ok())
consume();
else
ER.diag().note(Note::NK_ToMachThis, TokLCurly.range())
<< std::string(tok::spelling(tok_l_curly));
return std::make_unique<Formals>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(Members));
}

std::unique_ptr<LambdaArg> Parser::parseLambdaArg() {
LexerCursor LCur = lCur();
if (Token TokID = peek(); TokID.kind() == tok_id) {
consume(); // ID
assert(LastToken && "LastToken should be set after consume()");
auto ID =
std::make_unique<Identifier>(TokID.range(), std::string(TokID.view()));
if (peek().kind() != tok_at)
return std::make_unique<LambdaArg>(
LexerCursorRange{LCur, LastToken->rCur()}, std::move(ID), nullptr);

consume(); // @
std::unique_ptr<Formals> Formals = parseFormals();
if (!Formals) {
// extra "@", consider remove it.
Diagnostic &D =
Diags.emplace_back(Diagnostic::DK_LambdaArgExtraAt, TokID.range());
D.fix("remove extra @").edit(TextEdit::mkRemoval(TokID.range()));
D.fix("insert dummy formals")
.edit(TextEdit::mkInsertion(TokID.rCur(), R"({})"));
}
return std::make_unique<LambdaArg>(
LexerCursorRange{LCur, LastToken->rCur()}, std::move(ID),
std::move(Formals));
}

std::unique_ptr<Formals> Formals = parseFormals();
if (!Formals)
return nullptr;
assert(LastToken && "LastToken should be set after valid formals");
Token TokAt = peek();
if (TokAt.kind() != tok_at)
return std::make_unique<LambdaArg>(
LexerCursorRange{LCur, LastToken->rCur()}, nullptr, std::move(Formals));
consume(); // @
ExpectResult ER = expect(tok_id);
if (!ER.ok()) {
ER.diag().note(Note::NK_ToMachThis, TokAt.range())
<< std::string(tok::spelling(tok_at));
return std::make_unique<LambdaArg>(
LexerCursorRange{LCur, LastToken->rCur()}, nullptr, std::move(Formals));
}
consume(); // ID
auto ID = std::make_unique<Identifier>(ER.tok().range(),
std::string(ER.tok().view()));
return std::make_unique<LambdaArg>(LexerCursorRange{LCur, LastToken->rCur()},
std::move(ID), std::move(Formals));
}

} // namespace nixf
23 changes: 23 additions & 0 deletions libnixf/src/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Lexer.h"

#include "nixf/Basic/Nodes.h"
#include "nixf/Basic/Range.h"

#include <climits>
#include <deque>
Expand Down Expand Up @@ -96,6 +97,8 @@ class Parser {
return false;
}

LexerCursor lCur() { return peek().lCur(); }

public:
Parser(std::string_view Src, std::vector<Diagnostic> &Diags)
: Src(Src), Lex(Src, Diags), Diags(Diags) {
Expand Down Expand Up @@ -211,6 +214,26 @@ class Parser {
/// \endcode
std::unique_ptr<ExprList> parseExprList();

/// \code
/// formal : ,? ID
/// | ,? ID '?' expr
/// | ,? ...
/// \endcode
std::unique_ptr<Formal> parseFormal();

/// \code
/// formals : '{' formal* '}'
/// \endcode
std::unique_ptr<Formals> parseFormals();

/// \code
/// lambda_arg : ID
/// | ID @ {' formals '}'
/// | '{' formals '}'
/// | '{' formals '}' @ ID
/// \endcode
std::unique_ptr<LambdaArg> parseLambdaArg();

std::unique_ptr<Expr> parseExpr() {
return parseExprApp(); // TODO!
}
Expand Down
Loading