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 4 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 return parseExprLambda();
97 case tok_r_curly:
98 switch (peek(3).kind()) {
99 case tok_colon: // { a } :
100 case tok_at: // { a } @
101 return parseExprLambda();
102 default:
103 return parseExprAttrs();
104 }
105 default:
106 break;
107 }
108 break;
109 }
110 case tok_r_curly:
111 switch (peek(2).kind()) {
112 case tok_at: // { } @
113 case tok_colon: // { } :
114 return parseExprLambda();
115 default:
116 break;
117 }
118 break;
119 case tok_ellipsis: // { ...
120 return parseExprLambda();
121 default:
122 break;
123 }
124 break;
125 } // case tok_l_curly
126 case tok_kw_if:
127 return parseExprIf();
128 case tok_kw_assert:
129 return parseExprAssert();
130 case tok_kw_let:
131 if (peek(1).kind() != tok_l_curly)
132 return parseExprLet();
133 break;
134 case tok_kw_with:
135 return parseExprWith();
136 default:
137 break;
138 }
139
140 return parseExprOp();
141}
142
143std::shared_ptr<ExprIf> Parser::parseExprIf() {
144 LexerCursor LCur = lCur(); // if
145 Token TokIf = peek();
146 assert(TokIf.kind() == tok_kw_if && "parseExprIf should start with `if`");
147 consume(); // if
148 assert(LastToken && "LastToken should be set after consume()");
149
150 auto SyncThen = withSync(tok_kw_then);
151 auto SyncElse = withSync(tok_kw_else);
152
153 auto Cond = parseExpr();
154 if (!Cond) {
155 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "condition");
156 D.fix("remove `if` keyword").edit(TextEdit::mkRemoval(TokIf.range()));
157 D.fix("insert dummy condition")
158 .edit(TextEdit::mkInsertion(TokIf.rCur(), "true"));
159
160 if (peek().kind() != tok_kw_then)
161 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
162 std::move(Cond), /*Then=*/nullptr,
163 /*Else=*/nullptr);
164 }
165
166 ExpectResult ExpKwThen = expect(tok_kw_then);
167 if (!ExpKwThen.ok()) {
168 Diagnostic &D = ExpKwThen.diag();
169 Note &N = D.note(Note::NK_ToMachThis, TokIf.range());
170 N << std::string(tok::spelling(tok_kw_if));
171 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
172 std::move(Cond), /*Then=*/nullptr,
173 /*Else=*/nullptr);
174 }
175
176 consume(); // then
177
178 auto Then = parseExpr();
179 if (!Then) {
180 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "then");
181 Note &N = D.note(Note::NK_ToMachThis, TokIf.range());
182 N << std::string(tok::spelling(tok_kw_if));
183
184 if (peek().kind() != tok_kw_else)
185 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
186 std::move(Cond), std::move(Then),
187 /*Else=*/nullptr);
188 }
189
190 ExpectResult ExpKwElse = expect(tok_kw_else);
191 if (!ExpKwElse.ok())
192 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
193 std::move(Cond), std::move(Then),
194 /*Else=*/nullptr);
195
196 consume(); // else
197
198 auto Else = parseExpr();
199 if (!Else) {
200 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "else");
201 Note &N = D.note(Note::NK_ToMachThis, TokIf.range());
202 N << std::string(tok::spelling(tok_kw_if));
203 }
204
205 return std::make_shared<ExprIf>(LexerCursorRange{LCur, LastToken->rCur()},
206 std::move(Cond), std::move(Then),
207 std::move(Else));
208}
209
210std::shared_ptr<ExprAssert> Parser::parseExprAssert() {
211 LexerCursor LCur = lCur();
212 Token TokAssert = peek();
213 assert(TokAssert.kind() == tok_kw_assert && "should be tok_kw_assert");
214 consume(); // assert
215 assert(LastToken && "LastToken should be set after consume()");
216
217 auto SyncSemi = withSync(tok_semi_colon);
218
219 auto Cond = parseExpr();
220 if (!Cond) {
221 Diagnostic &D = diagNullExpr(Diags, LastToken->rCur(), "condition");
222 D.fix("remove `assert` keyword")
223 .edit(TextEdit::mkRemoval(TokAssert.range()));
224
225 if (peek().kind() != tok_colon)
226 return std::make_shared<ExprAssert>(
227 LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond),
228 /*Value=*/nullptr);
229 }
230
231 ExpectResult ExpSemi = expect(tok_semi_colon);
232 if (!ExpSemi.ok()) {
233 // missing ';'
234 Diagnostic &D = ExpSemi.diag();
235 Note &N = D.note(Note::NK_ToMachThis, TokAssert.range());
236 N << std::string(tok::spelling(tok_kw_assert));
237 return std::make_shared<ExprAssert>(
238 LexerCursorRange{LCur, LastToken->rCur()}, std::move(Cond),
239 /*Value=*/nullptr);
240 }
241
242 consume(); // ;
243
244 auto Value = parseExpr();
245
246 if (!Value)
247 diagNullExpr(Diags, LastToken->rCur(), "assert value");
248
249 return std::make_shared<ExprAssert>(LexerCursorRange{LCur, LastToken->rCur()},
250 std::move(Cond), std::move(Value));
251}
252
253std::shared_ptr<ExprLet> Parser::parseExprLet() {
254 LexerCursor LCur = lCur();
255 Token TokLet = peek();
256 assert(TokLet.kind() == tok_kw_let &&
257 "first token should be tok_kw_let in parseExprLet()");
258
259 auto Let = std::make_shared<Misc>(TokLet.range());
260
261 consume(); // 'let'
262
263 auto SyncIn = withSync(tok_kw_in);
264
265 assert(LastToken && "LastToken should be set after consume()");
266
267 auto Binds = parseBinds();
268 auto Attrs = Binds ? Act.onExprAttrs(Binds->range(), Binds, Let) : nullptr;
269
270 ExpectResult ExpKwIn = expect(tok_kw_in);
271
272 if (!ExpKwIn.ok()) {
273 // missing 'in'
274 return std::make_shared<ExprLet>(LexerCursorRange{LCur, LastToken->rCur()},
275 std::move(Let), /*KwIn=*/nullptr,
276 /*E=*/nullptr, std::move(Attrs));
277 }
278
279 auto In = std::make_shared<Misc>(ExpKwIn.tok().range());
280
281 consume(); // 'in'
282
283 auto E = parseExpr();
284 if (!E)
285 diagNullExpr(Diags, LastToken->rCur(), "let ... in");
286
287 return std::make_shared<ExprLet>(LexerCursorRange{LCur, LastToken->rCur()},
288 std::move(Let), std::move(In), std::move(E),
289 std::move(Attrs));
290}
291
292std::shared_ptr<ExprWith> Parser::parseExprWith() {
293 LexerCursor LCur = lCur();
294 Token TokWith = peek();
295 assert(TokWith.kind() == tok_kw_with && "token should be tok_kw_with");
296
297 consume(); // with
298
299 auto KwWith = std::make_shared<Misc>(TokWith.range());
300 assert(LastToken && "LastToken should be set after consume()");
301
302 auto SyncSemi = withSync(tok_semi_colon);
303
304 auto With = parseExpr();
305
306 if (!With)
307 diagNullExpr(Diags, LastToken->rCur(), "with expression");
308
309 ExpectResult ExpSemi = expect(tok_semi_colon);
310 if (!ExpSemi.ok()) {
311 ExpSemi.diag().note(Note::NK_ToMachThis, TokWith.range())
312 << std::string(tok::spelling(tok_kw_with));
313 return std::make_shared<ExprWith>(LexerCursorRange{LCur, LastToken->rCur()},
314 std::move(KwWith), /*TokSemi*/ nullptr,
315 std::move(With), /*E=*/nullptr);
316 }
317
318 auto TokSemi = std::make_shared<Misc>(ExpSemi.tok().range());
319 consume(); // ;
320
321 auto E = parseExpr();
322
323 if (!E)
324 diagNullExpr(Diags, LastToken->rCur(), "with body");
325
326 return std::make_shared<ExprWith>(LexerCursorRange{LCur, LastToken->rCur()},
327 std::move(KwWith), std::move(TokSemi),
328 std::move(With), std::move(E));
329}
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: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< ExprAttrs > parseExprAttrs()
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.