1 : #include "nixexpr.hh"
2 : #include "derivations.hh"
3 : #include "util.hh"
4 : #include "aterm.hh"
5 :
6 : #include "nixexpr-ast.hh"
7 : #include "nixexpr-ast.cc"
8 :
9 :
10 : namespace nix {
11 :
12 :
13 : string showPos(ATerm pos)
14 21 : {
15 21 : ATerm path;
16 21 : int line, column;
17 21 : if (matchNoPos(pos)) return "undefined position";
18 19 : if (!matchPos(pos, path, line, column))
19 0 : throw badTerm("position expected", pos);
20 19 : return (format("`%1%', line %2%") % aterm2String(path) % line).str();
21 : }
22 :
23 :
24 : ATerm bottomupRewrite(TermFun & f, ATerm e)
25 1071 : {
26 1071 : checkInterrupt();
27 :
28 1071 : if (ATgetType(e) == AT_APPL) {
29 998 : AFun fun = ATgetAFun(e);
30 998 : int arity = ATgetArity(fun);
31 998 : ATerm args[arity];
32 :
33 1811 : for (int i = 0; i < arity; ++i)
34 813 : args[i] = bottomupRewrite(f, ATgetArgument(e, i));
35 :
36 998 : e = (ATerm) ATmakeApplArray(fun, args);
37 : }
38 :
39 73 : else if (ATgetType(e) == AT_LIST) {
40 73 : ATermList in = (ATermList) e;
41 73 : ATermList out = ATempty;
42 :
43 295 : for (ATermIterator i(in); i; ++i)
44 222 : out = ATinsert(out, bottomupRewrite(f, *i));
45 :
46 73 : e = (ATerm) ATreverse(out);
47 : }
48 :
49 1071 : return f(e);
50 : }
51 :
52 :
53 : void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos)
54 526 : {
55 526 : ATermList bnds;
56 526 : if (!matchAttrs(e, bnds))
57 0 : throw TypeError("attribute set expected");
58 :
59 3836 : for (ATermIterator i(bnds); i; ++i) {
60 3310 : ATerm name;
61 3310 : Expr e;
62 3310 : ATerm pos;
63 3310 : if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
64 3310 : attrs.set(name, withPos ? makeAttrRHS(e, pos) : e);
65 : }
66 : }
67 :
68 :
69 : Expr queryAttr(Expr e, const string & name)
70 114 : {
71 114 : ATerm dummy;
72 114 : return queryAttr(e, name, dummy);
73 : }
74 :
75 :
76 : Expr queryAttr(Expr e, const string & name, ATerm & pos)
77 433 : {
78 433 : ATermList bnds;
79 433 : if (!matchAttrs(e, bnds))
80 0 : throw TypeError("attribute set expected");
81 :
82 1333 : for (ATermIterator i(bnds); i; ++i) {
83 1332 : ATerm name2, pos2;
84 1332 : Expr e;
85 1332 : if (!matchBind(*i, name2, e, pos2))
86 0 : abort(); /* can't happen */
87 1332 : if (aterm2String(name2) == name) {
88 432 : pos = pos2;
89 432 : return e;
90 : }
91 : }
92 :
93 1 : return 0;
94 : }
95 :
96 :
97 : Expr makeAttrs(const ATermMap & attrs)
98 352 : {
99 352 : ATermList bnds = ATempty;
100 2720 : for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
101 2368 : Expr e;
102 2368 : ATerm pos;
103 2368 : if (!matchAttrRHS(i->value, e, pos))
104 0 : abort(); /* can't happen */
105 2368 : bnds = ATinsert(bnds, makeBind(i->key, e, pos));
106 : }
107 352 : return makeAttrs(bnds);
108 : }
109 :
110 :
111 : Expr substitute(const Substitution & subs, Expr e)
112 25316 : {
113 25316 : checkInterrupt();
114 :
115 : //if (subs.size() == 0) return e;
116 :
117 25316 : ATerm name, pos, e2;
118 :
119 : /* As an optimisation, don't substitute in subterms known to be
120 : closed. */
121 25316 : if (matchClosed(e, e2)) return e;
122 :
123 24283 : if (matchVar(e, name)) {
124 2545 : Expr sub = subs.lookup(name);
125 2545 : if (sub == makeRemoved()) sub = 0;
126 2545 : Expr wrapped;
127 : /* Add a "closed" wrapper around terms that aren't already
128 : closed. The check is necessary to prevent repeated
129 : wrapping, e.g., closed(closed(closed(...))), which kills
130 : caching. */
131 2545 : return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e;
132 : }
133 :
134 : /* In case of a function, filter out all variables bound by this
135 : function. */
136 21738 : ATermList formals;
137 21738 : ATerm body;
138 21738 : if (matchFunction(e, formals, body, pos)) {
139 9 : ATermMap map(ATgetLength(formals));
140 25 : for (ATermIterator i(formals); i; ++i) {
141 16 : ATerm d1, d2;
142 16 : if (!matchFormal(*i, name, d1, d2)) abort();
143 16 : map.set(name, makeRemoved());
144 : }
145 9 : Substitution subs2(&subs, &map);
146 9 : return makeFunction(
147 : (ATermList) substitute(subs2, (ATerm) formals),
148 : substitute(subs2, body), pos);
149 : }
150 :
151 21729 : if (matchFunction1(e, name, body, pos)) {
152 380 : ATermMap map(1);
153 380 : map.set(name, makeRemoved());
154 380 : return makeFunction1(name, substitute(Substitution(&subs, &map), body), pos);
155 : }
156 :
157 : /* Idem for a mutually recursive attribute set. */
158 21349 : ATermList rbnds, nrbnds;
159 21349 : if (matchRec(e, rbnds, nrbnds)) {
160 19 : ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
161 57 : for (ATermIterator i(rbnds); i; ++i)
162 38 : if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
163 0 : else abort(); /* can't happen */
164 22 : for (ATermIterator i(nrbnds); i; ++i)
165 3 : if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
166 0 : else abort(); /* can't happen */
167 19 : return makeRec(
168 : (ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds),
169 : (ATermList) substitute(subs, (ATerm) nrbnds));
170 : }
171 :
172 21330 : if (ATgetType(e) == AT_APPL) {
173 16452 : AFun fun = ATgetAFun(e);
174 16452 : int arity = ATgetArity(fun);
175 16452 : ATerm args[arity];
176 16452 : bool changed = false;
177 :
178 37857 : for (int i = 0; i < arity; ++i) {
179 21405 : ATerm arg = ATgetArgument(e, i);
180 21405 : args[i] = substitute(subs, arg);
181 21405 : if (args[i] != arg) changed = true;
182 : }
183 :
184 16452 : return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
185 : }
186 :
187 4878 : if (ATgetType(e) == AT_LIST) {
188 1113 : unsigned int len = ATgetLength((ATermList) e);
189 1113 : ATerm es[len];
190 1113 : ATermIterator i((ATermList) e);
191 3831 : for (unsigned int j = 0; i; ++i, ++j)
192 2718 : es[j] = substitute(subs, *i);
193 1113 : ATermList out = ATempty;
194 3831 : for (unsigned int j = len; j; --j)
195 2718 : out = ATinsert(out, es[j - 1]);
196 1113 : return (ATerm) out;
197 : }
198 :
199 3765 : return e;
200 : }
201 :
202 :
203 : /* We use memoisation to prevent exponential complexity on heavily
204 : shared ATerms (remember, an ATerm is a graph, not a tree!). Note
205 : that using an STL set is fine here wrt to ATerm garbage collection
206 : since all the ATerms in the set are already reachable from
207 : somewhere else. */
208 : static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
209 9002 : {
210 9002 : if (done.find(e) != done.end()) return;
211 7198 : done.insert(e);
212 :
213 7198 : ATerm name, pos, value;
214 7198 : ATermList formals;
215 7198 : ATerm with, body;
216 7198 : ATermList rbnds, nrbnds;
217 :
218 7198 : if (matchVar(e, name)) {
219 520 : if (!defs.get(name))
220 3 : throw EvalError(format("undefined variable `%1%'")
221 : % aterm2String(name));
222 : }
223 :
224 6678 : else if (matchFunction(e, formals, body, pos)) {
225 43 : ATermMap defs2(defs);
226 158 : for (ATermIterator i(formals); i; ++i) {
227 115 : ATerm d1, d2;
228 115 : if (!matchFormal(*i, name, d1, d2)) abort();
229 115 : defs2.set(name, (ATerm) ATempty);
230 : }
231 157 : for (ATermIterator i(formals); i; ++i) {
232 115 : ATerm valids, deflt;
233 529 : set<Expr> done2;
234 115 : if (!matchFormal(*i, name, valids, deflt)) abort();
235 115 : checkVarDefs2(done, defs, valids);
236 114 : checkVarDefs2(done2, defs2, deflt);
237 : }
238 42 : set<Expr> done2;
239 42 : checkVarDefs2(done2, defs2, body);
240 : }
241 :
242 6635 : else if (matchFunction1(e, name, body, pos)) {
243 163 : ATermMap defs2(defs);
244 163 : defs2.set(name, (ATerm) ATempty);
245 163 : set<Expr> done2;
246 163 : checkVarDefs2(done2, defs2, body);
247 : }
248 :
249 6472 : else if (matchRec(e, rbnds, nrbnds)) {
250 68 : checkVarDefs2(done, defs, (ATerm) nrbnds);
251 68 : ATermMap defs2(defs);
252 278 : for (ATermIterator i(rbnds); i; ++i) {
253 210 : if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
254 210 : defs2.set(name, (ATerm) ATempty);
255 : }
256 70 : for (ATermIterator i(nrbnds); i; ++i) {
257 2 : if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
258 2 : defs2.set(name, (ATerm) ATempty);
259 : }
260 68 : set<Expr> done2;
261 68 : checkVarDefs2(done2, defs2, (ATerm) rbnds);
262 : }
263 :
264 6404 : else if (matchWith(e, with, body, pos)) {
265 : /* We can't check the body without evaluating the definitions
266 : (which is an arbitrary expression), so we don't do that
267 : here but only when actually evaluating the `with'. */
268 6 : checkVarDefs2(done, defs, with);
269 : }
270 :
271 6398 : else if (ATgetType(e) == AT_APPL) {
272 4914 : int arity = ATgetArity(ATgetAFun(e));
273 12160 : for (int i = 0; i < arity; ++i)
274 7254 : checkVarDefs2(done, defs, ATgetArgument(e, i));
275 : }
276 :
277 1484 : else if (ATgetType(e) == AT_LIST)
278 1477 : for (ATermIterator i((ATermList) e); i; ++i)
279 1068 : checkVarDefs2(done, defs, *i);
280 : }
281 :
282 :
283 : void checkVarDefs(const ATermMap & defs, Expr e)
284 104 : {
285 104 : set<Expr> done;
286 104 : checkVarDefs2(done, defs, e);
287 : }
288 :
289 :
290 : Expr makeBool(bool b)
291 175 : {
292 175 : return b ? eTrue : eFalse;
293 : }
294 :
295 :
296 : string showType(Expr e)
297 0 : {
298 0 : ATerm t1, t2, t3;
299 0 : ATermList l1;
300 0 : ATermBlob b1;
301 0 : int i1;
302 0 : if (matchStr(e, t1)) return "a string";
303 0 : if (matchPath(e, t1)) return "a path";
304 0 : if (matchUri(e, t1)) return "a path";
305 0 : if (matchNull(e)) return "null";
306 0 : if (matchInt(e, i1)) return "an integer";
307 0 : if (matchBool(e, t1)) return "a boolean";
308 0 : if (matchFunction(e, l1, t1, t2)) return "a function";
309 0 : if (matchFunction1(e, t1, t2, t3)) return "a function";
310 0 : if (matchAttrs(e, l1)) return "an attribute set";
311 0 : if (matchList(e, l1)) return "a list";
312 0 : if (matchPrimOp(e, i1, b1, l1)) return "a partially applied built-in function";
313 0 : if (matchContext(e, l1, t1)) return "a context containing " + showType(t1);
314 0 : return "an unknown type";
315 : }
316 :
317 :
318 : string showValue(Expr e)
319 0 : {
320 0 : ATerm s;
321 0 : int i;
322 0 : if (matchStr(e, s)) {
323 0 : string t = aterm2String(s), u;
324 0 : for (string::iterator i = t.begin(); i != t.end(); ++i)
325 0 : if (*i == '\"' || *i == '\\') u += "\\" + *i;
326 0 : else if (*i == '\n') u += "\\n";
327 0 : else if (*i == '\r') u += "\\r";
328 0 : else if (*i == '\t') u += "\\t";
329 0 : else u += *i;
330 0 : return "\"" + u + "\"";
331 : }
332 0 : if (matchPath(e, s)) return aterm2String(s);
333 0 : if (matchUri(e, s)) return aterm2String(s);
334 0 : if (matchNull(e)) return "null";
335 0 : if (matchInt(e, i)) return (format("%1%") % i).str();
336 0 : if (e == eTrue) return "true";
337 0 : if (e == eFalse) return "false";
338 : /* !!! incomplete */
339 0 : return "<unknown>";
340 : }
341 :
342 :
343 121 : }
|