1use crate::ast;
2use crate::ptr::P;
3use crate::token::{BinOp, Delim, Keyword, Kind as T, Token};
4use crate::Span;
5
6use super::{PResult, Parser};
7
8impl<'a, I> Parser<'a, I>
9where
10 I: Iterator<Item = Token>,
11{
12 pub fn parse_expr(&mut self) -> PResult<'a, ast::Expr> {
13 self.parse_expr_with_precedence(0)
14 }
15
16 pub fn parse_expr_with_precedence(&mut self, min_precedence: u32) -> PResult<'a, ast::Expr> {
17 let mut left = self.parse_unary_expr_or_higher()?;
18
19 while let Some(bin_op_kind) = self.peek_bin_op()? {
20 let precedence = binary_precedence(bin_op_kind);
21 if precedence <= min_precedence {
22 break;
23 }
24
25 let bin_op = ast::BinOp {
26 kind: bin_op_kind,
27 span: self.bump().span,
28 };
29
30 let right = self.parse_expr_with_precedence(precedence)?;
31
32 left = ast::Expr {
33 span: left.span.union(right.span),
34 kind: ast::ExprKind::Binary(bin_op, P(left), P(right)),
35 };
36 }
37
38 Ok(left)
39 }
40
41 fn peek_bin_op(&mut self) -> PResult<'a, Option<ast::BinOpKind>> {
42 use ast::BinOpKind as O;
43
44 let bin_op = match self.token.kind {
45 T::EqEq | T::Keyword(Keyword::Is) | T::Keyword(Keyword::Eq) => Some(O::Eq),
46 T::Neq | T::Keyword(Keyword::Neq) => Some(O::Neq),
47 T::Lt | T::Keyword(Keyword::Lt) => Some(O::Lt),
48 T::Lte | T::Keyword(Keyword::Lte) => Some(O::Lte),
49 T::Gt | T::Keyword(Keyword::Gt) => Some(O::Gt),
50 T::Gte | T::Keyword(Keyword::Gte) => Some(O::Gte),
51
52 T::AndAnd | T::Keyword(Keyword::And) => Some(O::And),
53 T::OrOr | T::Keyword(Keyword::Or) => Some(O::Or),
54 T::Xor | T::Keyword(Keyword::Xor) => Some(O::Xor),
55
56 T::BinOp(BinOp::Plus) => Some(O::Add),
57 T::BinOp(BinOp::Minus) => Some(O::Sub),
58 T::BinOp(BinOp::Star) => Some(O::Mul),
59 T::BinOp(BinOp::Slash) => Some(O::Div),
60 T::BinOp(BinOp::Percent) => Some(O::Mod),
61
62 T::Eq | T::Keyword(Keyword::To) => Some(O::Assign),
63 T::BinOpEq(BinOp::Plus) => Some(O::AddAssign),
64 T::BinOpEq(BinOp::Minus) => Some(O::SubAssign),
65 T::BinOpEq(BinOp::Star) => Some(O::MulAssign),
66 T::BinOpEq(BinOp::Slash) => Some(O::DivAssign),
67 T::BinOpEq(BinOp::Percent) => Some(O::ModAssign),
68
69 _ => None,
70 };
71
72 Ok(bin_op)
73 }
74
75 pub fn parse_unary_expr_or_higher(&mut self) -> PResult<'a, ast::Expr> {
76 let (kind, span) = match self.token.kind {
77 T::BinOp(BinOp::Minus) => (ast::UnOpKind::Neg, self.bump().span),
78 T::Not | T::Keyword(Keyword::Not) => (ast::UnOpKind::Not, self.bump().span),
79 _ => {
80 return {
81 if let Ok(expr) = self.parse_call_expr_or_atom() {
82 Ok(expr)
83 } else {
84 Err(self.expect_one_of(&[T::Not, T::BinOp(BinOp::Minus)]))
85 }
86 }
87 }
88 };
89
90 let op = ast::UnOp { kind, span };
91 let operand = self.parse_call_expr_or_atom()?;
92 let span = op.span.union(operand.span);
93
94 Ok(ast::Expr {
95 kind: ast::ExprKind::Unary(op, P(operand)),
96 span,
97 })
98 }
99
100 pub fn parse_call_expr_or_atom(&mut self) -> PResult<'a, ast::Expr> {
101 let receiver = self.parse_atom()?;
102
103 let (args, span) = match self.parse_call_arg_list() {
104 Some(tup) => tup,
105 None => return Ok(receiver),
106 };
107
108 Ok(ast::Expr {
109 span: receiver.span.union(span),
110 kind: ast::ExprKind::Call(P(receiver), args),
111 })
112 }
113
114 pub fn parse_call_arg_list(&mut self) -> Option<(Vec<ast::Expr>, Span)> {
115 self.eat(T::OpenDelim(Delim::Paren))?;
116
117 Some(self.parse_list_with(
118 true,
119 super::list::parse_list_sep_with_term(true, T::Comma, T::CloseDelim(Delim::Paren)),
120 |p, span| {
121 p.expect_one_of(&[T::Comma, T::CloseDelim(Delim::Paren)])
122 .span(span);
123 },
124 |p| p.parse_expr().ok(),
125 ))
126 }
127
128 pub fn parse_atom(&mut self) -> PResult<'a, ast::Expr> {
129 match self.token.kind {
130 T::OpenDelim(Delim::Paren) => {
131 let span = self.bump().span;
132 let mut expr = self.parse_expr()?;
133 if let Some(paren) = self.eat(T::CloseDelim(Delim::Paren)) {
134 expr.span = span.union(paren.span);
135 Ok(expr)
136 } else {
137 Err(self.expect_one_of(&[]))
138 }
139 }
140 T::Number
141 | T::Keyword(Keyword::True)
142 | T::Keyword(Keyword::False)
143 | T::Keyword(Keyword::Null)
144 | T::OpenDelim(Delim::DoubleQuote)
145 | T::OpenDelim(Delim::Backtick) => {
146 let lit = self.parse_lit()?;
147 Ok(ast::Expr {
148 span: lit.span,
149 kind: ast::ExprKind::Lit(lit),
150 })
151 }
152 T::Dollar | T::At | T::AtAt | T::Ident | T::Keyword(_) => {
153 let var = self.parse_var()?;
154 Ok(ast::Expr {
155 span: var.span,
156 kind: ast::ExprKind::Var(var),
157 })
158 }
159 _ => Err(self.expect_one_of(&[
160 T::OpenDelim(Delim::Paren),
161 T::Number,
162 T::Keyword(Keyword::True),
163 T::Keyword(Keyword::False),
164 T::Keyword(Keyword::Null),
165 T::OpenDelim(Delim::DoubleQuote),
166 T::OpenDelim(Delim::Backtick),
167 T::Dollar,
168 T::At,
169 T::AtAt,
170 T::Ident,
171 ])),
172 }
173 }
174
175 pub fn parse_var(&mut self) -> PResult<'a, ast::Var> {
176 let span = self.token.span;
177
178 let sigil = if self.eat(T::Dollar).is_some() {
179 ast::Sigil::Global
180 } else if self.eat(T::At).is_some() {
181 ast::Sigil::Node
182 } else if self.eat(T::AtAt).is_some() {
183 ast::Sigil::File
184 } else {
185 ast::Sigil::Local
186 };
187
188 let (symbol, keyword, ident_span) =
189 self.eat_symbol().ok_or_else(|| self.expect(T::Ident))?;
190
191 Ok(ast::Var {
192 sigil,
193 symbol,
194 keyword,
195 span: span.union(ident_span),
196 })
197 }
198
199 pub fn parse_lit(&mut self) -> PResult<'a, ast::Lit> {
200 let (kind, span) = match self.token.kind {
201 T::Number => (ast::LitKind::Number, self.bump().span),
202 T::Keyword(Keyword::True) => (ast::LitKind::True, self.bump().span),
203 T::Keyword(Keyword::False) => (ast::LitKind::False, self.bump().span),
204 T::Keyword(Keyword::Null) => (ast::LitKind::Null, self.bump().span),
205 T::OpenDelim(Delim::DoubleQuote) => {
206 self.bump();
207 let body =
208 self.parse_str_body_with_terminator(T::CloseDelim(Delim::DoubleQuote))?;
209 let span = body.span;
210 (ast::LitKind::Str(body), span)
211 }
212 T::OpenDelim(Delim::Backtick) => {
213 self.bump();
214 let body = self.parse_str_body_with_terminator(T::CloseDelim(Delim::Backtick))?;
215 let span = body.span;
216 (ast::LitKind::Str(body), span)
217 }
218 _ => {
219 return Err(self.expect_one_of(&[
220 T::Number,
221 T::Keyword(Keyword::True),
222 T::Keyword(Keyword::False),
223 T::Keyword(Keyword::Null),
224 T::OpenDelim(Delim::DoubleQuote),
225 T::OpenDelim(Delim::Backtick),
226 ]))
227 }
228 };
229
230 Ok(ast::Lit { kind, span })
231 }
232}
233
234fn binary_precedence(op: ast::BinOpKind) -> u32 {
235 use ast::BinOpKind as O;
236 match op {
237 O::Mul => 8,
238 O::Div => 8,
239 O::Mod => 8,
240 O::Add => 7,
241 O::Sub => 7,
242 O::Lt => 6,
243 O::Lte => 6,
244 O::Gt => 6,
245 O::Gte => 6,
246 O::Eq => 5,
247 O::Neq => 5,
248 O::MulAssign => 4,
249 O::DivAssign => 4,
250 O::ModAssign => 4,
251 O::AddAssign => 3,
252 O::SubAssign => 3,
253 O::And => 2,
254 O::Or => 2,
255 O::Xor => 2,
256 O::Assign => 1,
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 use crate::parse::test_utils::assert_parse;
265 use crate::parse::Parse;
266 use crate::Span;
267
268 #[test]
269 fn can_parse_lit() {
270 assert_parse("123", |_itn| ast::Lit {
271 kind: ast::LitKind::Number,
272 span: Span::new(0, 3),
273 });
274
275 assert_parse("true", |_itn| ast::Lit {
276 kind: ast::LitKind::True,
277 span: Span::new(0, 4),
278 });
279
280 assert_parse("false", |_itn| ast::Lit {
281 kind: ast::LitKind::False,
282 span: Span::new(0, 5),
283 });
284
285 assert_parse("null", |_itn| ast::Lit {
286 kind: ast::LitKind::Null,
287 span: Span::new(0, 4),
288 });
289 }
290
291 #[test]
292 fn can_parse_var() {
293 assert_parse("$foo", |itn| ast::Var {
294 sigil: ast::Sigil::Global,
295 symbol: itn.intern("foo"),
296 keyword: None,
297 span: Span::new(0, 4),
298 });
299
300 assert_parse("if", |itn| ast::Var {
301 sigil: ast::Sigil::Local,
302 symbol: itn.intern("if"),
303 keyword: Some(Keyword::If),
304 span: Span::new(0, 2),
305 });
306
307 assert_parse("@@bar", |itn| ast::Var {
308 sigil: ast::Sigil::File,
309 symbol: itn.intern("bar"),
310 keyword: None,
311 span: Span::new(0, 5),
312 });
313
314 assert_parse("@baz", |itn| ast::Var {
315 sigil: ast::Sigil::Node,
316 symbol: itn.intern("baz"),
317 keyword: None,
318 span: Span::new(0, 4),
319 });
320 }
321
322 #[test]
323 fn can_parse_expr() {
324 use ast::BinOpKind as O;
325
326 assert_parse("1 + 2 * 3", |itn| ast::Expr {
327 kind: ast::ExprKind::Binary(
328 ast::BinOp {
329 kind: O::Add,
330 span: Span::new(2, 1),
331 },
332 P(ast::Expr {
333 kind: ast::ExprKind::Lit(ast::Lit::parse_with_interner("1", 0, itn).unwrap()),
334 span: Span::new(0, 1),
335 }),
336 P(ast::Expr {
337 kind: ast::ExprKind::Binary(
338 ast::BinOp {
339 kind: O::Mul,
340 span: Span::new(6, 1),
341 },
342 P(ast::Expr {
343 kind: ast::ExprKind::Lit(
344 ast::Lit::parse_with_interner("2", 4, itn).unwrap(),
345 ),
346 span: Span::new(4, 1),
347 }),
348 P(ast::Expr {
349 kind: ast::ExprKind::Lit(
350 ast::Lit::parse_with_interner("3", 8, itn).unwrap(),
351 ),
352 span: Span::new(8, 1),
353 }),
354 ),
355 span: Span::new(4, 5),
356 }),
357 ),
358 span: Span::new(0, 9),
359 });
360
361 assert_parse("(1 + 2) * 3", |itn| ast::Expr {
362 kind: ast::ExprKind::Binary(
363 ast::BinOp {
364 kind: O::Mul,
365 span: Span::new(8, 1),
366 },
367 P(ast::Expr {
368 kind: ast::ExprKind::Binary(
369 ast::BinOp {
370 kind: O::Add,
371 span: Span::new(3, 1),
372 },
373 P(ast::Expr {
374 kind: ast::ExprKind::Lit(
375 ast::Lit::parse_with_interner("1", 1, itn).unwrap(),
376 ),
377 span: Span::new(1, 1),
378 }),
379 P(ast::Expr {
380 kind: ast::ExprKind::Lit(
381 ast::Lit::parse_with_interner("2", 5, itn).unwrap(),
382 ),
383 span: Span::new(5, 1),
384 }),
385 ),
386 span: Span::new(0, 7),
387 }),
388 P(ast::Expr {
389 kind: ast::ExprKind::Lit(ast::Lit::parse_with_interner("3", 10, itn).unwrap()),
390 span: Span::new(10, 1),
391 }),
392 ),
393 span: Span::new(0, 11),
394 });
395
396 assert_parse("bar()", |itn| ast::Expr {
397 kind: ast::ExprKind::Call(
398 P(ast::Expr {
399 kind: ast::ExprKind::Var(ast::Var::parse_with_interner("bar", 0, itn).unwrap()),
400 span: Span::new(0, 3),
401 }),
402 Vec::new(),
403 ),
404 span: Span::new(0, 5),
405 });
406
407 assert_parse("$foo = bar(42, 1 + 2 + @@baz(quux, )) or false", |itn| {
408 ast::Expr {
409 kind: ast::ExprKind::Binary(
410 ast::BinOp {
411 kind: O::Assign,
412 span: Span::new(5, 1),
413 },
414 P(ast::Expr {
415 kind: ast::ExprKind::Var(
416 ast::Var::parse_with_interner("$foo", 0, itn).unwrap(),
417 ),
418 span: Span::new(0, 4),
419 }),
420 P(ast::Expr {
421 kind: ast::ExprKind::Binary(
422 ast::BinOp {
423 kind: O::Or,
424 span: Span::new(38, 2),
425 },
426 P(ast::Expr {
427 kind: ast::ExprKind::Call(
428 P(ast::Expr {
429 kind: ast::ExprKind::Var(
430 ast::Var::parse_with_interner("bar", 7, itn).unwrap(),
431 ),
432 span: Span::new(7, 3),
433 }),
434 vec![
435 ast::Expr::parse_with_interner("42", 11, itn).unwrap(),
436 ast::Expr::parse_with_interner(
437 "1 + 2 + @@baz(quux, )",
438 15,
439 itn,
440 )
441 .unwrap(),
442 ],
443 ),
444 span: Span::new(7, 31),
445 }),
446 P(ast::Expr {
447 kind: ast::ExprKind::Lit(
448 ast::Lit::parse_with_interner("false", 41, itn).unwrap(),
449 ),
450 span: Span::new(41, 5),
451 }),
452 ),
453 span: Span::new(7, 39),
454 }),
455 ),
456 span: Span::new(0, 46),
457 }
458 });
459 }
460}