nixd
Loading...
Searching...
No Matches
ParseExpr.cpp
Go to the documentation of this file.
1#include "Parser.h"
2
3using namespace nixf;
4using namespace nixf::detail;
5
6std::shared_ptr<Expr> Parser::parseExprSelect() {
7 std::shared_ptr<Expr> Expr = parseExprSimple();
8 if (!Expr)
9 return nullptr;
10 assert(LastToken && "LastToken should be set after valid expr");
11 LexerCursor Begin = Expr->lCur();
12
13 Token Tok = peek();
14 if (Tok.kind() != tok_dot) {
15 // expr_select : expr_simple
16 return Expr;
17 }
18
19 // expr_select : expr_simple '.' attrpath
20 // | expr_simple '.' attrpath 'or' expr_select
21 consume(); // .
22 auto Path = parseAttrPath();
23 if (!Path) {
24 // extra ".", consider remove it.
25 Diagnostic &D =
26 Diags.emplace_back(Diagnostic::DK_SelectExtraDot, Tok.range());
27 D.fix("remove extra .").edit(TextEdit::mkRemoval(Tok.range()));
28 D.fix("insert dummy attrpath")
29 .edit(TextEdit::mkInsertion(Tok.rCur(), R"("dummy")"));
30 }
31
32 Token TokOr = peek();
33 if (TokOr.kind() != tok_kw_or) {
34 // expr_select : expr_simple '.' attrpath
35 return std::make_shared<ExprSelect>(
36 LexerCursorRange{Begin, LastToken->rCur()}, std::move(Expr),
37 std::move(Path), /*Default=*/nullptr);
38 }
39
40 // expr_select : expr_simple '.' attrpath 'or' expr_select
41 consume(); // `or`
42 auto Default = parseExprSelect();
43 if (!Default) {
44 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "default");
45 D.fix("remove `or` keyword").edit(TextEdit::mkRemoval(TokOr.range()));
46 }
47 return std::make_shared<ExprSelect>(
48 LexerCursorRange{Begin, LastToken->rCur()}, std::move(Expr),
49 std::move(Path), std::move(Default));
50}
51
52std::shared_ptr<Expr> Parser::parseExprApp(int Limit) {
53 std::shared_ptr<Expr> Fn = parseExprSelect();
54 // If fn cannot be evaluated to lambda, exit early.
55 if (!Fn || !Fn->maybeLambda())
56 return Fn;
57
58 std::vector<std::shared_ptr<Expr>> Args;
59 while (Limit--) {
60 std::shared_ptr<Expr> Arg = parseExprSelect();
61 if (!Arg)
62 break;
63 Args.emplace_back(std::move(Arg));
64 }
65
66 if (Args.empty())
67 return Fn;
68 return std::make_shared<ExprCall>(
69 LexerCursorRange{Fn->lCur(), Args.back()->rCur()}, std::move(Fn),
70 std::move(Args));
71}
72
73std::shared_ptr<Expr> Parser::parseExpr() {
74 // Look ahead 3 tokens.
75 switch (peek().kind()) {
76 case tok_id: {
77 switch (peek(1).kind()) {
78 case tok_at: // ID @
79 case tok_colon: // ID :
80 return parseExprLambda();
81 default:
82 break;
83 }
84 break;
85 }
86 case tok_l_curly: {
87 switch (peek(1).kind()) {
88 case tok_id: {
89 switch (peek(2).kind()) {
90 case tok_colon: // { a :
91 case tok_at: // { a @
92 case tok_question: // { a ?
93 case tok_comma: // { a ,
94 case tok_id: // { a b
95 case tok_ellipsis: // { a ...
96 case tok_r_curly:
97 return parseExprLambda();
98 default:
99 break;
100 }
101 break;
102 }
103 case tok_r_curly:
104 switch (peek(2).kind()) {
105 case tok_at: // { } @
106 case tok_colon: // { } :
107 return parseExprLambda();
108 default:
109 break;
110 }
111 break;
112 case tok_ellipsis: // { ...
113 return parseExprLambda();
114 default:
115 break;
116 }
117 break;
118 } // case tok_l_curly
119 case tok_kw_if:
120 return parseExprIf();
121 case tok_kw_assert:
122 return parseExprAssert();
123 case tok_kw_let:
124 if (peek(1).kind() != tok_l_curly)
125 return parseExprLet();
126 break;
127 case tok_kw_with:
128 return parseExprWith();
129 default:
130 break;
131 }
132
133 return parseExprOp();
134}
135
136std::shared_ptr<ExprIf> Parser::parseExprIf() {
137 LexerCursor LCur = lCur(); // if
138 Token TokIf = peek();
139 assert(TokIf.kind() == tok_kw_if && "parseExprIf should start with `if`");
140 consume(); // if
141 assert(LastToken && "LastToken should be set after consume()");
142
143 auto SyncThen = withSync(tok_kw_then);
144 auto SyncElse = withSync(tok_kw_else);
145
146 auto Cond = parseExpr();
147 if (!Cond) {
148 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "condition");
149 D.fix("remove `if` keyword").edit(TextEdit::mkRemoval(TokIf.range()));
150 D.fix("insert dummy condition")
151 .edit(TextEdit::mkInsertion(TokIf.rCur(), "true"));
152
153 if (peek().kind() != tok_kw_then)
154 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
155 std::move(Cond), /*Then=*/nullptr,
156 /*Else=*/nullptr);
157 }
158
159 ExpectResult ExpKwThen = expect(tok_kw_then);
160 if (!ExpKwThen.ok()) {
161 Diagnostic &D = ExpKwThen.diag();
162 Note &N = D.note(Note::NK_ToMachThis, TokIf.range());
163 N << std::string(tok::spelling(tok_kw_if));
164 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
165 std::move(Cond), /*Then=*/nullptr,
166 /*Else=*/nullptr);
167 }
168
169 consume(); // then
170
171 auto Then = parseExpr();
172 if (!Then) {
173 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "then");
174 Note &N = D.note(Note::NK_ToMachThis, TokIf.range());
175 N << std::string(tok::spelling(tok_kw_if));
176
177 if (peek().kind() != tok_kw_else)
178 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
179 std::move(Cond), std::move(Then),
180 /*Else=*/nullptr);
181 }
182
183 ExpectResult ExpKwElse = expect(tok_kw_else);
184 if (!ExpKwElse.ok())
185 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
186 std::move(Cond), std::move(Then),
187 /*Else=*/nullptr);
188
189 consume(); // else
190
191 auto Else = parseExpr();
192 if (!Else) {
193 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "else");
194 Note &N = D.note(Note::NK_ToMachThis, TokIf.range());
195 N << std::string(tok::spelling(tok_kw_if));
196 }
197
198 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
199 std::move(Cond), std::move(Then),
200 std::move(Else));
201}
202
203std::shared_ptr<ExprAssert> Parser::parseExprAssert() {
204 LexerCursor LCur = lCur();
205 Token TokAssert = peek();
206 assert(TokAssert.kind() == tok_kw_assert && "should be tok_kw_assert");
207 consume(); // assert
208 assert(LastToken && "LastToken should be set after consume()");
209
210 auto SyncSemi = withSync(tok_semi_colon);
211
212 auto Cond = parseExpr();
213 if (!Cond) {
214 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "condition");
215 D.fix("remove `assert` keyword")
216 .edit(TextEdit::mkRemoval(TokAssert.range()));
217
218 if (peek().kind() != tok_colon)
219 return std::make_shared<ExprAssert>(
220 LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond),
221 /*Value=*/nullptr);
222 }
223
224 ExpectResult ExpSemi = expect(tok_semi_colon);
225 if (!ExpSemi.ok()) {
226 // missing ';'
227 Diagnostic &D = ExpSemi.diag();
228 Note &N = D.note(Note::NK_ToMachThis, TokAssert.range());
229 N << std::string(tok::spelling(tok_kw_assert));
230 return std::make_shared<ExprAssert>(
231 LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond),
232 /*Value=*/nullptr);
233 }
234
235 consume(); // ;
236
237 auto Value = parseExpr();
238
239 if (!Value)
240 diagNullExpr(Diags, LastToken->rCur(), "assert value");
241
242 return std::make_shared<ExprAssert>(LexerCursorRange{LCur, LastToken->rCur()},
243 std::move(Cond), std::move(Value));
244}
245
246std::shared_ptr<ExprLet> Parser::parseExprLet() {
247 LexerCursor LCur = lCur();
248 Token TokLet = peek();
249 assert(TokLet.kind() == tok_kw_let &&
250 "first token should be tok_kw_let in parseExprLet()");
251
252 auto Let = std::make_shared<Misc>(TokLet.range());
253
254 consume(); // 'let'
255
256 auto SyncIn = withSync(tok_kw_in);
257
258 assert(LastToken && "LastToken should be set after consume()");
259
260 auto Binds = parseBinds();
261 auto Attrs = Binds ? Act.onExprAttrs(Binds->range(), Binds, Let) : nullptr;
262
263 ExpectResult ExpKwIn = expect(tok_kw_in);
264
265 if (!ExpKwIn.ok()) {
266 // missing 'in'
267 return std::make_shared<ExprLet>(LexerCursorRange{LCur, LastToken->rCur()},
268 std::move(Let), /*KwIn=*/nullptr,
269 /*E=*/nullptr, std::move(Attrs));
270 }
271
272 auto In = std::make_shared<Misc>(ExpKwIn.tok().range());
273
274 consume(); // 'in'
275
276 auto E = parseExpr();
277 if (!E)
278 diagNullExpr(Diags, LastToken->rCur(), "let ... in");
279
280 return std::make_shared<ExprLet>(LexerCursorRange{LCur, LastToken->rCur()},
281 std::move(Let), std::move(In), std::move(E),
282 std::move(Attrs));
283}
284
285std::shared_ptr<ExprWith> Parser::parseExprWith() {
286 LexerCursor LCur = lCur();
287 Token TokWith = peek();
288 assert(TokWith.kind() == tok_kw_with && "token should be tok_kw_with");
289
290 consume(); // with
291
292 auto KwWith = std::make_shared<Misc>(TokWith.range());
293 assert(LastToken && "LastToken should be set after consume()");
294
295 auto SyncSemi = withSync(tok_semi_colon);
296
297 auto With = parseExpr();
298
299 if (!With)
300 diagNullExpr(Diags, LastToken->rCur(), "with expression");
301
302 ExpectResult ExpSemi = expect(tok_semi_colon);
303 if (!ExpSemi.ok()) {
304 ExpSemi.diag().note(Note::NK_ToMachThis, TokWith.range())
305 << std::string(tok::spelling(tok_kw_with));
306 return std::make_shared<ExprWith>(LexerCursorRange{LCur, LastToken->rCur()},
307 std::move(KwWith), /*TokSemi*/ nullptr,
308 std::move(With), /*E=*/nullptr);
309 }
310
311 auto TokSemi = std::make_shared<Misc>(ExpSemi.tok().range());
312 consume(); // ;
313
314 auto E = parseExpr();
315
316 if (!E)
317 diagNullExpr(Diags, LastToken->rCur(), "with body");
318
319 return std::make_shared<ExprWith>(LexerCursorRange{LCur, LastToken->rCur()},
320 std::move(KwWith), std::move(TokSemi),
321 std::move(With), std::move(E));
322}
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
LexerCursor lCur() const
Definition Range.h:116
A point in the source file.
Definition Range.h:57
LexerCursor lCur() const
Definition Basic.h:37
LexerCursorRange range() const
Definition Basic.h:35
std::shared_ptr< ExprLet > parseExprLet()
std::shared_ptr< ExprAssert > parseExprAssert()
std::shared_ptr< Expr > parseExprOp()
Parse binary/unary operators.
std::shared_ptr< Expr > parseExpr()
Definition ParseExpr.cpp:73
std::shared_ptr< ExprLambda > parseExprLambda()
std::shared_ptr< Expr > parseExprApp(int Limit=INT_MAX)
Definition ParseExpr.cpp:52
std::shared_ptr< Expr > parseExprSelect()
Definition ParseExpr.cpp:6
std::shared_ptr< Expr > parseExprSimple()
std::shared_ptr< ExprWith > parseExprWith()
std::shared_ptr< ExprIf > parseExprIf()
std::shared_ptr< AttrPath > parseAttrPath()
std::shared_ptr< Binds > parseBinds()
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
tok::TokenKind kind() const
Definition Token.h:65
LexerCursorRange range() const
Definition Token.h:66
LexerCursor rCur() const
Definition Token.h:64
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.