nixd
Loading...
Searching...
No Matches
ParseAttrs.cpp
Go to the documentation of this file.
1#include "Parser.h"
2
3using namespace nixf;
4using namespace detail;
5
6std::shared_ptr<AttrName> Parser::parseAttrName() {
7 switch (Token Tok = peek(); Tok.kind()) {
8 case tok_kw_or:
9 Diags.emplace_back(Diagnostic::DK_OrIdentifier, Tok.range());
10 [[fallthrough]];
11 case tok_id: {
12 consume();
13 auto ID =
14 std::make_shared<Identifier>(Tok.range(), std::string(Tok.view()));
15 return std::make_shared<AttrName>(std::move(ID), Tok.range());
16 }
17 case tok_dquote: {
18 std::shared_ptr<ExprString> String = parseString(/*IsIndented=*/false);
19 return std::make_shared<AttrName>(std::move(String));
20 }
21 case tok_dollar_curly: {
22 std::shared_ptr<Interpolation> Expr = parseInterpolation();
23 return std::make_shared<AttrName>(std::move(Expr));
24 }
25 default:
26 return nullptr;
27 }
28}
29
30std::shared_ptr<AttrPath> Parser::parseAttrPath() {
31 auto First = parseAttrName();
32 if (!First)
33 return nullptr;
34 LexerCursor Begin = First->lCur();
35 assert(LastToken && "LastToken should be set after valid attrname");
36 std::vector<std::shared_ptr<AttrName>> AttrNames;
37 const AttrName *PrevName = First.get();
38 const AttrName *NextName = nullptr;
39 AttrNames.emplace_back(std::move(First));
40 std::vector<std::shared_ptr<Dot>> Dots;
41 auto SyncDot = withSync(tok_dot);
42 while (true) {
43 if (Token Tok = peek(); Tok.kind() == tok_dot) {
44 consume();
45 auto Next = parseAttrName();
46 NextName = Next.get();
47 // Make a "dot" node.
48 auto Do = std::make_shared<Dot>(Tok.range(), PrevName, NextName);
49 // Only update "PrevName" if "Next" is not nullptr.
50 // More information could be obtained by those dots.
51 if (NextName)
52 PrevName = NextName;
53 Dots.emplace_back(std::move(Do));
54 if (!Next) {
55 // extra ".", consider remove it.
56 Diagnostic &D =
57 Diags.emplace_back(Diagnostic::DK_AttrPathExtraDot, Tok.range());
58 D.fix("remove extra .").edit(TextEdit::mkRemoval(Tok.range()));
59 D.fix("insert dummy attrname")
60 .edit(TextEdit::mkInsertion(Tok.rCur(), R"("dummy")"));
61 continue;
62 }
63 AttrNames.emplace_back(std::move(Next));
64 continue;
65 }
66 break;
67 }
68 return std::make_shared<AttrPath>(LexerCursorRange{Begin, LastToken->rCur()},
69 std::move(AttrNames), std::move(Dots));
70}
71
72std::shared_ptr<Binding> Parser::parseBinding() {
73 auto Path = parseAttrPath();
74 if (!Path)
75 return nullptr;
76 assert(LastToken && "LastToken should be set after valid attrpath");
77 auto SyncEq = withSync(tok_eq);
78 auto SyncSemi = withSync(tok_semi_colon);
79 if (ExpectResult ER = expect(tok_eq); !ER.ok())
80 return std::make_shared<Binding>(
81 LexerCursorRange{Path->lCur(), LastToken->rCur()}, std::move(Path),
82 nullptr);
83 consume();
84 auto Expr = parseExpr();
85 if (!Expr)
86 diagNullExpr(Diags, LastToken->rCur(), "binding");
87 if (Token Tok = peek(); Tok.kind() == tok_semi_colon) {
88 consume();
89 } else {
90 // TODO: reset the cursor for error recovery.
91 // (https://github.com/nix-community/nixd/blob/2b0ca8cef0d13823132a52b6cd6f6d7372482664/libnixf/lib/Parse/Parser.cpp#L337)
92 // expected ";" for binding
93 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_Expected,
94 LexerCursorRange(LastToken->rCur()));
95 D << std::string(tok::spelling(tok_semi_colon));
96 D.fix("insert ;").edit(TextEdit::mkInsertion(LastToken->rCur(), ";"));
97 }
98 return std::make_shared<Binding>(
99 LexerCursorRange{Path->lCur(), LastToken->rCur()}, std::move(Path),
100 std::move(Expr));
101}
102
103std::shared_ptr<Inherit> Parser::parseInherit() {
104 Token TokInherit = peek();
105 if (TokInherit.kind() != tok_kw_inherit)
106 return nullptr;
107 consume();
108 auto SyncSemiColon = withSync(tok_semi_colon);
109
110 // These tokens might be consumed as "inherited_attrs"
111 auto SyncID = withSync(tok_id);
112 auto SyncQuote = withSync(tok_dquote);
113 auto SyncDollarCurly = withSync(tok_dollar_curly);
114
115 assert(LastToken && "LastToken should be set after consume()");
116 std::vector<std::shared_ptr<AttrName>> AttrNames;
117 std::shared_ptr<Expr> Expr = nullptr;
118 if (Token Tok = peek(); Tok.kind() == tok_l_paren) {
119 consume();
120 Expr = parseExpr();
121 if (!Expr)
122 diagNullExpr(Diags, LastToken->rCur(), "inherit");
123 if (ExpectResult ER = expect(tok_r_paren); ER.ok())
124 consume();
125 else
126 ER.diag().note(Note::NK_ToMachThis, Tok.range())
127 << std::string(tok::spelling(tok_l_paren));
128 }
129 while (true) {
130 if (auto AttrName = parseAttrName()) {
131 AttrNames.emplace_back(std::move(AttrName));
132 continue;
133 }
134 break;
135 }
136 ExpectResult ER = expect(tok_semi_colon);
137 if (ER.ok())
138 consume();
139 else
140 ER.diag().note(Note::NK_ToMachThis, TokInherit.range())
141 << std::string(tok::spelling(tok_kw_inherit));
142
143 // If attrnames are emtpy, this is an emtpy "inherit";
144 if (AttrNames.empty()) {
145 Diagnostic &D =
146 Diags.emplace_back(Diagnostic::DK_EmptyInherit, TokInherit.range());
148 Fix &F = D.fix("remove `inherit` keyword");
149 F.edit(TextEdit::mkRemoval(TokInherit.range()));
150 if (ER.ok()) {
151 // Remove ";" also.
152 F.edit(TextEdit::mkRemoval(ER.tok().range()));
153 }
154 }
155 return std::make_shared<Inherit>(
156 LexerCursorRange{TokInherit.lCur(), LastToken->rCur()},
157 std::move(AttrNames), std::move(Expr));
158}
159
160std::shared_ptr<Binds> Parser::parseBinds() {
161 // TODO: curently we don't support inherit
162 LexerCursor Begin = peek().lCur();
163 std::vector<std::shared_ptr<Node>> Bindings;
164 // attrpath
165 auto SyncID = withSync(tok_id);
166 auto SyncQuote = withSync(tok_dquote);
167 auto SyncDollarCurly = withSync(tok_dollar_curly);
168
169 // inherit
170 auto SyncInherit = withSync(tok_kw_inherit);
171 while (true) {
172 if (auto Binding = parseBinding()) {
173 Bindings.emplace_back(std::move(Binding));
174 continue;
175 }
176 if (auto Inherit = parseInherit()) {
177 Bindings.emplace_back(std::move(Inherit));
178 continue;
179 }
180 // If it is neither a binding, nor an inherit. Let's consume an "Unknown"
181 // For error recovery
182 if (removeUnexpected())
183 continue;
184 break;
185 }
186 if (Bindings.empty())
187 return nullptr;
188 assert(LastToken && "LastToken should be set after valid binding");
189 return std::make_shared<Binds>(LexerCursorRange{Begin, LastToken->rCur()},
190 std::move(Bindings));
191}
192
193std::shared_ptr<ExprAttrs> Parser::parseExprAttrs() {
194 std::shared_ptr<Misc> Rec;
195
196 auto Sync = withSync(tok_r_curly);
197
198 // "to match this ..."
199 // if "{" is missing, then use "rec", otherwise use "{"
200 Token Matcher = peek();
201 LexerCursor Begin = peek().lCur(); // rec or {
202 if (Token Tok = peek(); Tok.kind() == tok_kw_rec) {
203 consume();
204 Rec = std::make_shared<Misc>(Tok.range());
205 }
206 if (ExpectResult ER = expect(tok_l_curly); ER.ok()) {
207 consume();
208 Matcher = ER.tok();
209 }
210 assert(LastToken && "LastToken should be set after valid { or rec");
211 auto Binds = parseBinds();
212 if (ExpectResult ER = expect(tok_r_curly); ER.ok())
213 consume();
214 else
215 ER.diag().note(Note::NK_ToMachThis, Matcher.range())
216 << std::string(tok::spelling(Matcher.kind()));
217 return Act.onExprAttrs(LexerCursorRange{Begin, LastToken->rCur()},
218 std::move(Binds), std::move(Rec));
219}
Note & note(Note::NoteKind Kind, LexerCursorRange Range)
Definition Diagnostic.h:197
Fix & fix(std::string Message)
Definition Diagnostic.h:203
Fix & edit(TextEdit Edit)
Definition Diagnostic.h:65
A point in the source file.
Definition Range.h:57
std::shared_ptr< Interpolation > parseInterpolation()
Parse interpolations.
std::shared_ptr< Expr > parseExpr()
Definition ParseExpr.cpp:73
std::shared_ptr< Inherit > parseInherit()
std::shared_ptr< AttrName > parseAttrName()
Definition ParseAttrs.cpp:6
std::shared_ptr< ExprString > parseString(bool IsIndented)
std::shared_ptr< AttrPath > parseAttrPath()
std::shared_ptr< ExprAttrs > parseExprAttrs()
std::shared_ptr< Binds > parseBinds()
std::shared_ptr< Binding > parseBinding()
void tag(DiagnosticTag Tag)
Definition Diagnostic.h:96
std::shared_ptr< ExprAttrs > onExprAttrs(LexerCursorRange Range, std::shared_ptr< Binds > Binds, std::shared_ptr< Misc > Rec)
static TextEdit mkRemoval(LexerCursorRange RemovingRange)
Definition Diagnostic.h:39
static TextEdit mkInsertion(LexerCursor P, std::string NewText)
Definition Diagnostic.h:35
A token. With it's kind, and the range in source code.
Definition Token.h:55
LexerCursor lCur() const
Definition Token.h:63
tok::TokenKind kind() const
Definition Token.h:65
LexerCursorRange range() const
Definition Token.h:66
Diagnostic & diagNullExpr(std::vector< Diagnostic > &Diags, LexerCursor Loc, std::string As)
constexpr std::string_view spelling(TokenKind Kind)
Definition Token.h:13
Parser for the Nix expression language.