Nix 2.26.3
Nix, the purely functional package manager; unstable internal interfaces
 
Loading...
Searching...
No Matches
parser-state.hh
Go to the documentation of this file.
1#pragma once
3
4#include <limits>
5
6#include "eval.hh"
7
8namespace nix {
9
16{
17 const char * p;
18 size_t l;
19 bool hasIndentation;
20 operator std::string_view() const { return {p, l}; }
21};
22
23// This type must be trivially copyable; see YYLTYPE_IS_TRIVIAL in parser.y.
25{
26 int beginOffset;
27 int endOffset;
28
29 // backup to recover from yyless(0)
30 int stashedBeginOffset, stashedEndOffset;
31
32 void stash() {
33 stashedBeginOffset = beginOffset;
34 stashedEndOffset = endOffset;
35 }
36
37 void unstash() {
38 beginOffset = stashedBeginOffset;
39 endOffset = stashedEndOffset;
40 }
41
43 int doc_comment_first_column, doc_comment_last_column;
44};
45
47{
56 int docCommentDistance = std::numeric_limits<int>::max();
57
64
68 std::unordered_map<PosIdx, DocComment> & positionToDocComment;
69
70 PosTable & positions;
71 PosTable::Origin origin;
72
73 PosIdx at(const ParserLocation & loc);
74};
75
77{
78 const LexerState & lexerState;
79 SymbolTable & symbols;
80 PosTable & positions;
81 Expr * result;
82 SourcePath basePath;
83 PosTable::Origin origin;
84 const ref<SourceAccessor> rootFS;
85 const Expr::AstSymbols & s;
86 const EvalSettings & settings;
87
88 void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
89 void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
90 void addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
91 void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
92 Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
93 Expr * stripIndentation(const PosIdx pos,
94 std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
95 PosIdx at(const ParserLocation & loc);
96};
97
98inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
99{
100 throw ParseError({
101 .msg = HintFmt("attribute '%1%' already defined at %2%",
102 showAttrPath(symbols, attrPath), positions[prevPos]),
103 .pos = positions[pos]
104 });
105}
106
107inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
108{
109 throw ParseError({
110 .msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
111 .pos = positions[pos]
112 });
113}
114
115inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc)
116{
117 AttrPath::iterator i;
118 // All attrpaths have at least one attr
119 assert(!attrPath.empty());
120 auto pos = at(loc);
121 // Checking attrPath validity.
122 // ===========================
123 for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
124 ExprAttrs * nested;
125 if (i->symbol) {
126 ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
127 if (j != attrs->attrs.end()) {
128 nested = dynamic_cast<ExprAttrs *>(j->second.e);
129 if (!nested) {
130 attrPath.erase(i + 1, attrPath.end());
131 dupAttr(attrPath, pos, j->second.pos);
132 }
133 } else {
134 nested = new ExprAttrs;
135 attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
136 }
137 } else {
138 nested = new ExprAttrs;
139 attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
140 }
141 attrs = nested;
142 }
143 // Expr insertion.
144 // ==========================
145 if (i->symbol) {
146 addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos));
147 } else {
148 attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
149 }
150
151 auto it = lexerState.positionToDocComment.find(pos);
152 if (it != lexerState.positionToDocComment.end()) {
153 e->setDocComment(it->second);
154 lexerState.positionToDocComment.emplace(at(exprLoc), it->second);
155 }
156}
157
162inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
163{
164 ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol);
165 if (j != attrs->attrs.end()) {
166 // This attr path is already defined. However, if both
167 // e and the expr pointed by the attr path are two attribute sets,
168 // we want to merge them.
169 // Otherwise, throw an error.
170 auto ae = dynamic_cast<ExprAttrs *>(def.e);
171 auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
172
173 // N.B. In a world in which we are less bound by our past mistakes, we
174 // would also test that jAttrs and ae are not recursive. The effect of
175 // not doing so is that any `rec` marker on ae is discarded, and any
176 // `rec` marker on jAttrs will apply to the attributes in ae.
177 // See https://github.com/NixOS/nix/issues/9020.
178 if (jAttrs && ae) {
179 if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
180 jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
181 for (auto & ad : ae->attrs) {
182 if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
183 auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
184 auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
185 from.displ += jAttrs->inheritFromExprs->size();
186 }
187 attrPath.emplace_back(AttrName(ad.first));
188 addAttr(jAttrs, attrPath, ad.first, std::move(ad.second));
189 attrPath.pop_back();
190 }
191 ae->attrs.clear();
192 jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(),
193 std::make_move_iterator(ae->dynamicAttrs.begin()),
194 std::make_move_iterator(ae->dynamicAttrs.end()));
195 ae->dynamicAttrs.clear();
196 if (ae->inheritFromExprs) {
197 jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
198 std::make_move_iterator(ae->inheritFromExprs->begin()),
199 std::make_move_iterator(ae->inheritFromExprs->end()));
200 ae->inheritFromExprs = nullptr;
201 }
202 } else {
203 dupAttr(attrPath, def.pos, j->second.pos);
204 }
205 } else {
206 // This attr path is not defined. Let's create it.
207 attrs->attrs.emplace(symbol, def);
208 def.e->setName(symbol);
209 }
210}
211
212inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
213{
214 std::sort(formals->formals.begin(), formals->formals.end(),
215 [] (const auto & a, const auto & b) {
216 return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
217 });
218
219 std::optional<std::pair<Symbol, PosIdx>> duplicate;
220 for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
221 if (formals->formals[i].name != formals->formals[i + 1].name)
222 continue;
223 std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos};
224 duplicate = std::min(thisDup, duplicate.value_or(thisDup));
225 }
226 if (duplicate)
227 throw ParseError({
228 .msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
229 .pos = positions[duplicate->second]
230 });
231
232 if (arg && formals->has(arg))
233 throw ParseError({
234 .msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]),
235 .pos = positions[pos]
236 });
237
238 return formals;
239}
240
241inline Expr * ParserState::stripIndentation(const PosIdx pos,
242 std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
243{
244 if (es.empty()) return new ExprString("");
245
246 /* Figure out the minimum indentation. Note that by design
247 whitespace-only final lines are not taken into account. (So
248 the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
249 bool atStartOfLine = true; /* = seen only whitespace in the current line */
250 size_t minIndent = 1000000;
251 size_t curIndent = 0;
252 for (auto & [i_pos, i] : es) {
253 auto * str = std::get_if<StringToken>(&i);
254 if (!str || !str->hasIndentation) {
255 /* Anti-quotations and escaped characters end the current start-of-line whitespace. */
256 if (atStartOfLine) {
257 atStartOfLine = false;
258 if (curIndent < minIndent) minIndent = curIndent;
259 }
260 continue;
261 }
262 for (size_t j = 0; j < str->l; ++j) {
263 if (atStartOfLine) {
264 if (str->p[j] == ' ')
265 curIndent++;
266 else if (str->p[j] == '\n') {
267 /* Empty line, doesn't influence minimum
268 indentation. */
269 curIndent = 0;
270 } else {
271 atStartOfLine = false;
272 if (curIndent < minIndent) minIndent = curIndent;
273 }
274 } else if (str->p[j] == '\n') {
275 atStartOfLine = true;
276 curIndent = 0;
277 }
278 }
279 }
280
281 /* Strip spaces from each line. */
282 auto * es2 = new std::vector<std::pair<PosIdx, Expr *>>;
283 atStartOfLine = true;
284 size_t curDropped = 0;
285 size_t n = es.size();
286 auto i = es.begin();
287 const auto trimExpr = [&] (Expr * e) {
288 atStartOfLine = false;
289 curDropped = 0;
290 es2->emplace_back(i->first, e);
291 };
292 const auto trimString = [&] (const StringToken & t) {
293 std::string s2;
294 for (size_t j = 0; j < t.l; ++j) {
295 if (atStartOfLine) {
296 if (t.p[j] == ' ') {
297 if (curDropped++ >= minIndent)
298 s2 += t.p[j];
299 }
300 else if (t.p[j] == '\n') {
301 curDropped = 0;
302 s2 += t.p[j];
303 } else {
304 atStartOfLine = false;
305 curDropped = 0;
306 s2 += t.p[j];
307 }
308 } else {
309 s2 += t.p[j];
310 if (t.p[j] == '\n') atStartOfLine = true;
311 }
312 }
313
314 /* Remove the last line if it is empty and consists only of
315 spaces. */
316 if (n == 1) {
317 std::string::size_type p = s2.find_last_of('\n');
318 if (p != std::string::npos && s2.find_first_not_of(' ', p + 1) == std::string::npos)
319 s2 = std::string(s2, 0, p + 1);
320 }
321
322 // Ignore empty strings for a minor optimisation and AST simplification
323 if (s2 != "") {
324 es2->emplace_back(i->first, new ExprString(std::move(s2)));
325 }
326 };
327 for (; i != es.end(); ++i, --n) {
328 std::visit(overloaded { trimExpr, trimString }, i->second);
329 }
330
331 // If there is nothing at all, return the empty string directly.
332 // This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
333 if (es2->size() == 0) {
334 auto *const result = new ExprString("");
335 delete es2;
336 return result;
337 }
338
339 /* If this is a single string, then don't do a concatenation. */
340 if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
341 auto *const result = (*es2)[0].second;
342 delete es2;
343 return result;
344 }
345 return new ExprConcatStrings(pos, true, es2);
346}
347
348inline PosIdx LexerState::at(const ParserLocation & loc)
349{
350 return positions.add(origin, loc.beginOffset);
351}
352
353inline PosIdx ParserState::at(const ParserLocation & loc)
354{
355 return positions.add(origin, loc.beginOffset);
356}
357
358}
Definition fmt.hh:136
Definition pos-idx.hh:9
Definition pos-table.hh:16
Definition pos-table.hh:13
Definition symbol-table.hh:82
Definition symbol-table.hh:58
Definition ref.hh:15
auto i
Definition lexer.l:2745
std::ostream & str
Definition lexer.l:1728
Symbol symbol
Definition lexer.l:5834
std::shared_ptr< T > p
Definition lexer.l:1269
std::string std::string_view from
Definition lexer.l:2591
T t
Definition lexer.l:154
ExprAttrs::AttrDefs::iterator j
Definition lexer.l:8572
std::unordered_map< std::string_view, std::pair< const std::string *, uint32_t > > symbols
Definition lexer.l:1010
Definition nixexpr.hh:66
Definition eval-settings.hh:13
Definition nixexpr.hh:232
@ InheritedFrom
Definition nixexpr.hh:239
Definition nixexpr.hh:229
Definition nixexpr.hh:186
Definition nixexpr.hh:198
Definition nixexpr.hh:82
Definition nixexpr.hh:81
Definition nixexpr.hh:307
Definition parser-state.hh:47
int docCommentDistance
Definition parser-state.hh:56
std::unordered_map< PosIdx, DocComment > & positionToDocComment
Maps some positions to a DocComment, where the comment is relevant to the location.
Definition parser-state.hh:68
ParserLocation lastDocCommentLoc
Definition parser-state.hh:63
Definition parser-state.hh:25
int doc_comment_first_column
Definition parser-state.hh:43
Definition parser-state.hh:77
Definition source-path.hh:22
Definition parser-state.hh:16