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