nixd
Loading...
Searching...
No Matches
AST.cpp
Go to the documentation of this file.
1#include "AST.h"
2
4
5#include <unordered_set>
6
7using namespace nixf;
8
9namespace {
10
11/// \brief The attrpath contains dynamic attrname.
12struct AttrPathHasDynamicError : std::exception {
13 [[nodiscard]] const char *what() const noexcept override {
14 return "the attrpath has dynamic attribute name";
15 }
16};
17
18/// \brief Find nested attrpath.
19/// e.g. a.b.c.d
20/// ^<- a.b.c.d
21/// { a = 1; b = { c = d; }; }
22/// ^ b.c
23///
24/// SelectAttrPath := { a.b.c.d = 1; }
25/// ^~~~~~~<--------- such "selection"
26/// ValueAttrPath := N is a "value", find how it's nested.
27void getSelectAttrPath(const nixf::AttrName &N,
29 std::vector<std::string> &Path) {
30 const nixf::Node *Up = PM.query(N);
31 assert(Up && "Naked attrname!");
32 assert(Up->kind() == Node::NK_AttrPath &&
33 "Invoked in non-attrpath name! (Slipped inherit?)");
34 const auto &APath = static_cast<const nixf::AttrPath &>(*Up);
35 // Iterate on attr names
36 Path.reserve(APath.names().size());
37 for (const auto &Name : APath.names()) {
38 if (!Name->isStatic())
39 throw AttrPathHasDynamicError();
40 Path.emplace_back(Name->staticName());
41 if (Name.get() == &N)
42 break;
43 }
44}
45
46/// \copydoc getSelectAttrPath
47void getValueAttrPath(const nixf::Node &N, const nixf::ParentMapAnalysis &PM,
48 std::vector<std::string> &Path) {
49 if (PM.isRoot(N))
50 return;
51
52 const nixf::Node *Up = PM.query(N);
53 if (!Up)
54 return;
55
56 Up = PM.upExpr(*Up);
57
58 if (!Up)
59 return;
60
61 // Only attrs can "nest something"
62 if (Up->kind() != Node::NK_ExprAttrs)
63 return;
64
65 std::vector<std::string_view> Basic;
66 // Recursively search up all nested entries
67 if (!PM.isRoot(*Up))
68 getValueAttrPath(*Up, PM, Path);
69
70 // Find out how "N" gets nested.
71 const auto &UpAttrs = static_cast<const nixf::ExprAttrs &>(*Up);
72 assert(UpAttrs.binds() && "empty binds cannot nest anything!");
73 for (const std::shared_ptr<nixf::Node> &Attr : UpAttrs.binds()->bindings()) {
74 assert(Attr);
75 if (Attr->kind() == Node::NK_Inherit) // Cannot deal with inherits
76 continue;
77 assert(Attr->kind() == Node::NK_Binding);
78 const auto &Binding = static_cast<const nixf::Binding &>(*Attr);
79 if (Binding.value().get() == &N) {
80 getSelectAttrPath(*Binding.path().names().back(), PM, Path);
81 return;
82 }
83 }
84 assert(false && "must have corresbonding value");
85 __builtin_unreachable();
86}
87
88void getNestedAttrPath(const nixf::AttrName &N,
90 std::vector<std::string> &Path) {
91 if (const auto *Expr = PM.upExpr(N))
92 getValueAttrPath(*Expr, PM, Path);
93 getSelectAttrPath(N, PM, Path);
94}
95
96} // namespace
97
98[[nodiscard]] const EnvNode *nixd::upEnv(const nixf::Node &Desc,
99 const VariableLookupAnalysis &VLA,
100 const ParentMapAnalysis &PM) {
101 const nixf::Node *N = &Desc; // @nonnull
102 while (!VLA.env(N) && PM.query(*N) && !PM.isRoot(*N))
103 N = PM.query(*N);
104 assert(N);
105 return VLA.env(N);
106}
107
109 const ParentMapAnalysis &PM) {
110 // Firstly find the first "env" enclosed with this variable.
111 const EnvNode *Env = upEnv(N, VLA, PM);
112 if (!Env)
113 return false;
114
115 // Then, search up until there are some `with`.
116 for (; Env; Env = Env->parent()) {
117 if (!Env->isWith())
118 continue;
119 // this env is "with" expression.
120 const Node *With = Env->syntax();
121 assert(With && With->kind() == Node::NK_ExprWith);
122 const Node *WithBody = static_cast<const ExprWith &>(*With).with();
123 if (!WithBody)
124 continue; // skip incomplete with epxression.
125
126 // Se if it is "ExprVar“. Stupid.
127 if (WithBody->kind() != Node::NK_ExprVar)
128 continue;
129
130 // Hardcoded "pkgs", even more stupid.
131 if (static_cast<const ExprVar &>(*WithBody).id().name() == idioms::Pkgs)
132 return true;
133 }
134 return false;
135}
136
137// Idioms.
138namespace {
139
140using IdiomSetT = std::unordered_set<std::string_view>;
141
142IdiomSetT IdiomSet{nixd::idioms::Pkgs, nixd::idioms::Lib};
143
144auto ItLib = IdiomSet.find(nixd::idioms::Lib);
145auto ItPkgs = IdiomSet.find(nixd::idioms::Pkgs);
146
147nixd::Selector getKnownIdiomSelector(IdiomSetT::iterator It) {
148 // Unknown name, cannot deal with it.
149 if (It == IdiomSet.end())
151
152 return [&]() -> nixd::Selector {
153 if (It == ItLib) {
154 return {std::string(nixd::idioms::Lib)};
155 }
156 if (It == ItPkgs) {
157 return {};
158 }
159 assert(false && "Unhandled idiom iterator?");
160 __builtin_unreachable();
161 return {};
162 }();
163}
164
165nixd::Selector varSelector(const nixf::ExprVar &Var) {
166 return getKnownIdiomSelector(IdiomSet.find(Var.id().name()));
167};
168
169nixd::Selector withSelector(const nixf::ExprWith &With,
171 const nixf::ParentMapAnalysis &PM) {
172 if (!With.with())
174 switch (With.with()->kind()) {
175 case Node::NK_ExprVar:
177 static_cast<const nixf::ExprVar &>(*With.with()), VLA, PM);
178 case Node::NK_ExprSelect:
180 static_cast<const nixf::ExprSelect &>(*With.with()), VLA, PM);
181 default:
182 break;
183 }
185}
186
187} // namespace
188
190 nixd::Selector BaseSelector) {
191 if (Select.path())
193 *static_cast<const nixf::AttrPath *>(Select.path()),
194 std::move(BaseSelector));
195 return BaseSelector;
196}
197
201 const nixf::ParentMapAnalysis &PM) {
202 // Only check if the variable can be recogonized by some idiom.
203
205 auto Result = VLA.query(Var);
206 switch (Result.Kind) {
207 case ResultKind::Undefined:
208 case ResultKind::Defined:
209 return varSelector(Var);
210 case ResultKind::FromWith: {
211 assert(Result.Def && "FromWith variables should contains definition");
212 const nixf::Definition &Def = *Result.Def;
213 if (!Def.syntax())
214 throw NotAnIdiomException();
215
216 // The syntax
217 //
218 // with pkgs; with lib; [ ]
219 //
220 // does provide both "pkgs" + "lib" scopes.
221 //
222 // However, in current implementation we will only consider nested "with".
223 // That is, only "lib" variables will be considered.
224 const nixf::Node &Syntax = *Def.syntax();
225 const nixf::Node *With = PM.query(Syntax);
226 assert(With && "parent of kwWith should be the with expression");
227 assert(With->kind() == nixf::Node::NK_ExprWith);
228 Selector WithSelector =
229 withSelector(static_cast<const nixf::ExprWith &>(*With), VLA, PM);
230
231 // Append variable name after "with" expression selector.
232 // e.g.
233 //
234 // with pkgs; [ fo ]
235 // ^
236 // The result will be {pkgs, fo}
237 WithSelector.emplace_back(Var.id().name());
238
239 return WithSelector;
240 }
241 case ResultKind::NoSuchVar:
242 throw NoSuchVarException();
243 }
244 assert(false && "switch fallthrough!");
245 __builtin_unreachable();
246 return {};
247}
248
250 Selector BaseSelector) {
251 const auto &Names = AP.names();
252 for (const auto &Name : Names) {
253 if (!Name->isStatic())
254 throw DynamicNameException();
255 BaseSelector.emplace_back(Name->staticName());
256 }
257 return BaseSelector;
258}
259
262 const nixf::ParentMapAnalysis &PM) {
263 if (Sel.expr().kind() != Node::NK_ExprVar)
264 throw NotVariableSelect();
265
266 const auto &Var = static_cast<ExprVar &>(Sel.expr());
267
268 auto BaseSelector = mkVarSelector(Var, VLA, PM);
269
270 return mkSelector(Sel, std::move(BaseSelector));
271}
272
273std::pair<std::vector<std::string>, std::string>
275 if (N.kind() != Node::NK_Identifier)
276 return {};
277
278 // FIXME: impl scoped packages
279 std::string Prefix = static_cast<const Identifier &>(N).name();
280 return {{}, Prefix};
281}
282
284 const nixf::ParentMapAnalysis &PM,
285 std::vector<std::string> &Path) {
286 using R = nixd::FindAttrPathResult;
287 // If this is in "inherit", don't consider it is an attrpath.
288 if (PM.upTo(N, Node::NK_Inherit))
289 return R::Inherit;
290
291 if (const Node *Name = PM.upTo(N, Node::NK_AttrName)) {
292 try {
293 getNestedAttrPath(static_cast<const AttrName &>(*Name), PM, Path);
294 } catch (AttrPathHasDynamicError &E) {
295 return R::WithDynamic;
296 }
297 return R::OK;
298 }
299
300 // Consider this is an "extra" dot.
301 if (const Node *DotNode = PM.upTo(N, Node::NK_Dot)) {
302 const auto &D = static_cast<const Dot &>(*DotNode);
303
304 if (D.prev().kind() != Node::NK_AttrName)
305 return R::NotAttrPath;
306
307 try {
308 getNestedAttrPath(static_cast<const AttrName &>(D.prev()), PM, Path);
309 Path.emplace_back("");
310 return R::OK;
311 } catch (AttrPathHasDynamicError &E) {
312 return R::WithDynamic;
313 }
314 }
315
316 return R::NotAttrPath;
317}
This file declares some common analysis (tree walk) on the AST.
Types used in nixpkgs provider.
const std::vector< std::shared_ptr< AttrName > > & names() const
Definition Attrs.h:100
const AttrPath & path() const
Definition Attrs.h:128
const std::shared_ptr< Expr > & value() const
Definition Attrs.h:133
Represents a definition.
const Node * syntax() const
Holds a "." in the language.
Definition Basic.h:126
A set of variable definitions, which may inherit parent environment.
bool isWith() const
const Node * syntax() const
Where this node comes from.
EnvNode * parent() const
Expr & expr() const
Definition Expr.h:20
AttrPath * path() const
Definition Expr.h:27
const Identifier & id() const
Definition Simple.h:200
Expr * with() const
Definition Expr.h:172
Identifier. Variable names, attribute names, etc.
Definition Basic.h:114
const std::string & name() const
Definition Basic.h:120
NodeKind kind() const
Definition Basic.h:34
const Node * upExpr(const Node &N) const
Search up until the node becomes a concrete expression. a ^<--— ID -> ExprVar.
static bool isRoot(const Node *Up, const Node &N)
const Node * upTo(const Node &N, Node::NodeKind Kind) const
Search up until some kind of node is found.
const Node * query(const Node &N) const
const EnvNode * env(const Node *N) const
LookupResult query(const ExprVar &Var) const
Query the which name/with binds to specific varaible.
std::string Path
Definition Path.h:24
Selector mkVarSelector(const nixf::ExprVar &Var, const nixf::VariableLookupAnalysis &VLA, const nixf::ParentMapAnalysis &PM)
Construct a nixd::Selector from Var.
Definition AST.cpp:199
Selector mkSelector(const nixf::AttrPath &AP, Selector BaseSelector)
Construct a nixd::Selector from AP.
Definition AST.cpp:249
constexpr std::string_view Pkgs
Hardcoded name for "pkgs.xxx", or "with pkgs;".
Definition AST.h:20
constexpr std::string_view Lib
Hardcoded name for nixpkgs "lib".
Definition AST.h:26
FindAttrPathResult findAttrPath(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, std::vector< std::string > &Path)
Heuristically find attrpath suitable for "attrpath" completion.
Definition AST.cpp:283
const nixf::EnvNode * upEnv(const nixf::Node &Desc, const nixf::VariableLookupAnalysis &VLA, const nixf::ParentMapAnalysis &PM)
Search up until there are some node associated with "EnvNode".
Definition AST.cpp:98
bool havePackageScope(const nixf::Node &N, const nixf::VariableLookupAnalysis &VLA, const nixf::ParentMapAnalysis &PM)
Determine whether or not some node has enclosed "with pkgs; [ ]".
Definition AST.cpp:108
std::vector< std::string > Selector
A list of strings that "select"s into a attribute set.
Definition AttrSet.h:42
FindAttrPathResult
Definition AST.h:119
std::pair< std::vector< std::string >, std::string > getScopeAndPrefix(const nixf::Node &N, const nixf::ParentMapAnalysis &PM)
get variable scope, and it's prefix name.
Definition AST.cpp:274
The attrpath has a dynamic name, thus it cannot be trivially transformed to "static" selector.
Definition AST.h:71
No such variable.
Definition AST.h:49
The pattern of this variable cannot be recognized by known idioms.
Definition AST.h:34