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