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 ExpectResult ExpTokEq = expect(tok_eq);
80 if (!ExpTokEq.ok())
81 return std::make_shared<Binding>(
82 LexerCursorRange{Path->lCur(), LastToken->rCur()}, std::move(Path),
83 nullptr, nullptr);
84 consume();
85 auto TokEq = std::make_shared<Misc>(ExpTokEq.tok().range());
86 auto Expr = parseExpr();
87 if (!Expr)
88 diagNullExpr(Diags, LastToken->rCur(), "binding");
89 if (Token Tok = peek(); Tok.kind() == tok_semi_colon) {
90 consume();
91 } else {
92 // TODO: reset the cursor for error recovery.
93 // (https://github.com/nix-community/nixd/blob/2b0ca8cef0d13823132a52b6cd6f6d7372482664/libnixf/lib/Parse/Parser.cpp#L337)
94 // expected ";" for binding
95 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_Expected,
96 LexerCursorRange(LastToken->rCur()));
97 D << std::string(tok::spelling(tok_semi_colon));
98 D.fix("insert ;").edit(TextEdit::mkInsertion(LastToken->rCur(), ";"));
99 }
100 return std::make_shared<Binding>(
101 LexerCursorRange{Path->lCur(), LastToken->rCur()}, std::move(Path),
102 std::move(Expr), std::move(TokEq));
103}
104
105std::shared_ptr<Inherit> Parser::parseInherit() {
106 Token TokInherit = peek();
107 if (TokInherit.kind() != tok_kw_inherit)
108 return nullptr;
109 consume();
110 auto SyncSemiColon = withSync(tok_semi_colon);
111
112 // These tokens might be consumed as "inherited_attrs"
113 auto SyncID = withSync(tok_id);
114 auto SyncQuote = withSync(tok_dquote);
115 auto SyncDollarCurly = withSync(tok_dollar_curly);
116
117 assert(LastToken && "LastToken should be set after consume()");
118 std::vector<std::shared_ptr<AttrName>> AttrNames;
119 std::shared_ptr<Expr> Expr = nullptr;
120 if (Token Tok = peek(); Tok.kind() == tok_l_paren) {
121 consume();
122 Expr = parseExpr();
123 if (!Expr)
124 diagNullExpr(Diags, LastToken->rCur(), "inherit");
125 if (ExpectResult ER = expect(tok_r_paren); ER.ok())
126 consume();
127 else
128 ER.diag().note(Note::NK_ToMachThis, Tok.range())
129 << std::string(tok::spelling(tok_l_paren));
130 }
131 while (true) {
132 if (auto AttrName = parseAttrName()) {
133 AttrNames.emplace_back(std::move(AttrName));
134 continue;
135 }
136 break;
137 }
138 ExpectResult ER = expect(tok_semi_colon);
139 if (ER.ok())
140 consume();
141 else
142 ER.diag().note(Note::NK_ToMachThis, TokInherit.range())
143 << std::string(tok::spelling(tok_kw_inherit));
144
145 // If attrnames are emtpy, this is an emtpy "inherit";
146 if (AttrNames.empty()) {
147 Diagnostic &D =
148 Diags.emplace_back(Diagnostic::DK_EmptyInherit, TokInherit.range());
150 Fix &F = D.fix("remove `inherit` keyword");
151 F.edit(TextEdit::mkRemoval(TokInherit.range()));
152 if (ER.ok()) {
153 // Remove ";" also.
154 F.edit(TextEdit::mkRemoval(ER.tok().range()));
155 }
156 }
157 return std::make_shared<Inherit>(
158 LexerCursorRange{TokInherit.lCur(), LastToken->rCur()},
159 std::move(AttrNames), std::move(Expr));
160}
161
162std::shared_ptr<Binds> Parser::parseBinds() {
163 // TODO: curently we don't support inherit
164 LexerCursor Begin = peek().lCur();
165 std::vector<std::shared_ptr<Node>> Bindings;
166 // attrpath
167 auto SyncID = withSync(tok_id);
168 auto SyncQuote = withSync(tok_dquote);
169 auto SyncDollarCurly = withSync(tok_dollar_curly);
170
171 // inherit
172 auto SyncInherit = withSync(tok_kw_inherit);
173 while (true) {
174 if (auto Binding = parseBinding()) {
175 Bindings.emplace_back(std::move(Binding));
176 continue;
177 }
178 if (auto Inherit = parseInherit()) {
179 Bindings.emplace_back(std::move(Inherit));
180 continue;
181 }
182 // If it is neither a binding, nor an inherit. Let's consume an "Unknown"
183 // For error recovery
184 if (removeUnexpected())
185 continue;
186 break;
187 }
188 if (Bindings.empty())
189 return nullptr;
190 assert(LastToken && "LastToken should be set after valid binding");
191 return std::make_shared<Binds>(LexerCursorRange{Begin, LastToken->rCur()},
192 std::move(Bindings));
193}
194
195std::shared_ptr<ExprAttrs> Parser::parseExprAttrs() {
196 std::shared_ptr<Misc> Rec;
197
198 auto Sync = withSync(tok_r_curly);
199
200 // "to match this ..."
201 // if "{" is missing, then use "rec", otherwise use "{"
202 Token Matcher = peek();
203 LexerCursor Begin = peek().lCur(); // rec or {
204 if (Token Tok = peek(); Tok.kind() == tok_kw_rec) {
205 consume();
206 Rec = std::make_shared<Misc>(Tok.range());
207 }
208 if (ExpectResult ER = expect(tok_l_curly); ER.ok()) {
209 consume();
210 Matcher = ER.tok();
211 }
212 assert(LastToken && "LastToken should be set after valid { or rec");
213 auto Binds = parseBinds();
214 if (ExpectResult ER = expect(tok_r_curly); ER.ok())
215 consume();
216 else
217 ER.diag().note(Note::NK_ToMachThis, Matcher.range())
218 << std::string(tok::spelling(Matcher.kind()));
219 return Act.onExprAttrs(LexerCursorRange{Begin, LastToken->rCur()},
220 std::move(Binds), std::move(Rec));
221}
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
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:14
LexerCursor lCur() const
Definition Token.h:22
tok::TokenKind kind() const
Definition Token.h:24
LexerCursorRange range() const
Definition Token.h:25
Diagnostic & diagNullExpr(std::vector< Diagnostic > &Diags, LexerCursor Loc, std::string As)
Parser for the Nix expression language.