From c2a1b07fe378f71a73f379aeeb9b84a35997c21e Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Tue, 19 Sep 2023 22:23:28 +0800 Subject: [PATCH] nixd/Sema: lowering for inherited attrs --- nixd/include/nixd/Sema/Lowering.h | 2 + nixd/lib/Sema/Lowering.cpp | 108 ++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/nixd/include/nixd/Sema/Lowering.h b/nixd/include/nixd/Sema/Lowering.h index 312c6f976..d69bf507e 100644 --- a/nixd/include/nixd/Sema/Lowering.h +++ b/nixd/include/nixd/Sema/Lowering.h @@ -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 diff --git a/nixd/lib/Sema/Lowering.cpp b/nixd/lib/Sema/Lowering.cpp index 39c07c9e5..0efa8fb0f 100644 --- a/nixd/lib/Sema/Lowering.cpp +++ b/nixd/lib/Sema/Lowering.cpp @@ -3,6 +3,10 @@ #include "nixd/Syntax/Diagnostic.h" #include "nixd/Syntax/Nodes.h" +#include + +#include + namespace nixd { nix::Formal Lowering::lowerFormal(const syntax::Formal &Formal) { @@ -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 Fields; + + for (auto *Attr : Binds.Attributes) { + switch (Attr->getKind()) { + case Node::NK_Attribute: { + auto *Attribute = dynamic_cast(Attr); + nix::Expr *E = lower(Attribute->Body); + break; + } + case Node::NK_InheritedAttribute: { + auto *IA = dynamic_cast(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(Name)->Symbol; + break; + } + case Node::NK_String: { + Sym = STable.create(dynamic_cast(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; @@ -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(Root); + } } return nullptr;