Skip to content

Commit

Permalink
nixd/Sema: lowering for inherited attrs
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Sep 19, 2023
1 parent 4011c13 commit c2a1b07
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 0 deletions.
2 changes: 2 additions & 0 deletions nixd/include/nixd/Sema/Lowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ struct Lowering {
nix::Expr *lower(syntax::Node *Root);
nix::ExprLambda *lowerFunction(syntax::Function *Fn);
nix::Formal lowerFormal(const syntax::Formal &Formal);
nix::AttrPath lowerAttrPath(const syntax::AttrPath &Path);
nix::ExprAttrs *lowerBinds(const syntax::Binds &Binds);
};

} // namespace nixd
108 changes: 108 additions & 0 deletions nixd/lib/Sema/Lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
#include "nixd/Syntax/Diagnostic.h"
#include "nixd/Syntax/Nodes.h"

#include <nix/nixexpr.hh>

#include <llvm/Support/FormatVariadic.h>

namespace nixd {

nix::Formal Lowering::lowerFormal(const syntax::Formal &Formal) {
Expand Down Expand Up @@ -109,6 +113,107 @@ nix::ExprLambda *Lowering::lowerFunction(syntax::Function *Fn) {
return NewLambda;
}

static syntax::Diagnostic mkDiagDup(std::string Name, RangeIdx Range,
RangeIdx Prev) {
syntax::Diagnostic Diag;
Diag.Kind = syntax::Diagnostic::Warning;
Diag.Msg = llvm::formatv("duplicated attr {0}", Name);
Diag.Range = Range;

syntax::Note Note;
Note.Msg = llvm::formatv("previously defined here");
Note.Range = Prev;
return Diag;
}

nix::ExprAttrs *Lowering::lowerBinds(const syntax::Binds &Binds) {
// The official parser does this work during the lowering process, via
// @addAttr in Parser.y

using syntax::Node;

auto *Result = new nix::ExprAttrs;
Ctx.Pool.record(Result);

// Use a map to detect duplicated fields
// Not using the map inside nix::ExprAttrs because we want to preserve the
// range
std::map<nix::Symbol, Node *> Fields;

for (auto *Attr : Binds.Attributes) {
switch (Attr->getKind()) {
case Node::NK_Attribute: {
auto *Attribute = dynamic_cast<syntax::Attribute *>(Attr);
nix::Expr *E = lower(Attribute->Body);
break;
}
case Node::NK_InheritedAttribute: {
auto *IA = dynamic_cast<syntax::InheritedAttribute *>(Attr);
if (IA->InheritedAttrs->Names.empty()) {
syntax::Diagnostic Diag;
Diag.Kind = syntax::Diagnostic::Warning;
Diag.Msg = "empty inherit expression";
Diag.Range = Attr->Range;
Diags.emplace_back(std::move(Diag));
}

// inherit (expr) attr1 attr2
// lowering `(expr)` to nix expression
nix::Expr *Subject = nullptr;

if (IA->E)
Subject = lower(IA->E);

for (Node *Name : IA->InheritedAttrs->Names) {

// Extract nix::Symbol, report error if we encountered dynamic fields
nix::Symbol Sym;
switch (Name->getKind()) {
case Node::NK_Identifier: {
Sym = dynamic_cast<syntax::Identifier *>(Name)->Symbol;
break;
}
case Node::NK_String: {
Sym = STable.create(dynamic_cast<syntax::String *>(Name)->S);
break;
}
default: {
syntax::Diagnostic Diag;
Diag.Kind = syntax::Diagnostic::Error;
Diag.Msg = "dynamic attributes are not allowed in inherit";
Diag.Range = Name->Range;
Diags.emplace_back(std::move(Diag));
}
}

// Check if it is duplicated
if (Fields.contains(Sym)) {
std::string SymStr = STable[Sym];
auto Diag = mkDiagDup(SymStr, Name->Range, Fields[Sym]->Range);
Diags.emplace_back(std::move(Diag));
continue;
}

// Insert an AttrDef to the result
nix::Expr *AttrBody;
if (Subject) {
// inherit (expr) attr1 attr2
AttrBody = new nix::ExprSelect(Name->Range.Begin, Subject, Sym);
} else {
// inherit attr1 attr2 ...
AttrBody = new nix::ExprVar(Name->Range.Begin, Sym);
}
Ctx.Pool.record(AttrBody);
nix::ExprAttrs::AttrDef Def(AttrBody, Name->Range.Begin, true);
Result->attrs.insert({Sym, Def});
}
}
}
}

return Result;
}

nix::Expr *Lowering::lower(nixd::syntax::Node *Root) {
if (!Root)
return nullptr;
Expand Down Expand Up @@ -136,6 +241,9 @@ nix::Expr *Lowering::lower(nixd::syntax::Node *Root) {
Ctx.Pool.record(new nix::ExprWith(With->Range.Begin, Attrs, Body));
return NixWith;
}
case Node::NK_Binds: {
auto *Binds = dynamic_cast<syntax::Binds *>(Root);
}
}

return nullptr;
Expand Down

0 comments on commit c2a1b07

Please sign in to comment.