1use bumpalo::collections::Vec as BumpVec;
6use nash_region::{Located, Position, Region};
7use nash_source::{BinOpOperand, Expr};
8
9use crate::Parser;
10use crate::error;
11
12mod accessor;
13mod case;
14mod if_;
15mod lambda;
16mod let_;
17mod list;
18mod number;
19mod record;
20mod string;
21mod tuple;
22mod variable;
23
24impl<'a> Parser<'a> {
25 pub fn expression(&mut self) -> Result<(&'a Located<Expr<'a>>, Position), error::Expr<'a>> {
44 let start = self.get_position();
45
46 self.one_of(
47 error::Expr::Start,
48 vec![
49 Box::new(|p: &mut Parser<'a>| p.let_(start)),
51 Box::new(|p: &mut Parser<'a>| p.case_(start)),
53 Box::new(|p: &mut Parser<'a>| p.if_(start)),
55 Box::new(|p: &mut Parser<'a>| p.lambda(start)),
57 Box::new(|p| {
59 let expr = p.possibly_negative_term(start)?;
60 let end = p.get_position();
61 p.chomp(error::Expr::Space)?;
62 p.chomp_expr_end(start, expr, vec![], end)
63 }),
64 ],
65 )
66 }
67
68 pub(crate) fn chomp_expr_end(
86 &mut self,
87 start: Position,
88 expr: &'a Located<Expr<'a>>,
89 args: Vec<&'a Located<Expr<'a>>>,
90 end: Position,
91 ) -> Result<(&'a Located<Expr<'a>>, Position), error::Expr<'a>> {
92 let mut ops: BumpVec<'a, &'a BinOpOperand<'a>> = BumpVec::new_in(self.bump);
94 let mut current_expr = expr;
95 let mut current_args = args;
96 let mut current_end = end;
97
98 loop {
99 let state_for_fallback = (ops.clone(), current_expr, current_args.clone(), current_end);
100
101 let result = self.one_of_with_fallback(
102 vec![
103 Box::new(|p: &mut Parser<'a>| {
105 let (row, col) = p.position();
106 p.check_indent(row, col, error::Expr::Start)?;
107 let arg = p.term()?;
108 let new_end = p.get_position();
109 p.chomp(error::Expr::Space)?;
110
111 let mut new_args = current_args.clone();
112 new_args.push(arg);
113
114 Ok(ExprEndState::MoreArgs(new_args, new_end))
115 }),
116 Box::new(|p: &mut Parser<'a>| {
118 let (row, col) = p.position();
119 p.check_indent(row, col, error::Expr::Start)?;
120
121 let op_start = p.get_position();
123
124 let op = p.add_location_operator(
125 error::Expr::Start,
126 error::Expr::OperatorReserved,
127 )?;
128 let op_name = op.value;
129 let op_end = p.get_position();
130
131 p.chomp_and_check_indent(error::Expr::Space, |row, col| {
132 error::Expr::IndentOperatorRight(op_name, row, col)
133 })?;
134
135 let new_start = p.get_position();
136
137 if op_name == "-"
140 && current_end != op_start && op_end == new_start
142 {
144 let negated_expr = p.term()?;
146 let neg_end = p.get_position();
147 let neg_region = Region::new(op_start, neg_end);
148 let neg = p.alloc(Located::at(neg_region, Expr::Negate(negated_expr)));
149 p.chomp(error::Expr::Space)?;
150
151 let mut new_args = current_args.clone();
152 new_args.push(neg);
153
154 Ok(ExprEndState::MoreArgs(new_args, neg_end))
155 } else {
156 p.one_of(
158 |row, col| error::Expr::OperatorRight(op_name, row, col),
159 vec![
160 Box::new(|p: &mut Parser<'a>| {
162 let new_expr = p.possibly_negative_term(new_start)?;
163 let new_end = p.get_position();
164 p.chomp(error::Expr::Space)?;
165
166 Ok(ExprEndState::MoreOps(op, new_expr, new_end))
167 }),
168 Box::new(|p: &mut Parser<'a>| {
170 let (final_expr, final_end) = p.one_of(
171 |row, col| {
172 error::Expr::OperatorRight(op_name, row, col)
173 },
174 vec![
175 Box::new(|p: &mut Parser<'a>| p.let_(new_start)),
176 Box::new(|p| p.case_(new_start)),
177 Box::new(|p| p.if_(new_start)),
178 Box::new(|p| p.lambda(new_start)),
179 ],
180 )?;
181
182 Ok(ExprEndState::Final(op, final_expr, final_end))
183 }),
184 ],
185 )
186 }
187 }),
188 ],
189 ExprEndState::Done,
190 )?;
191
192 match result {
193 ExprEndState::MoreArgs(new_args, new_end) => {
194 current_args = new_args;
195 current_end = new_end;
196 }
197 ExprEndState::MoreOps(op, new_expr, new_end) => {
198 let call_expr = to_call(self, start, current_expr, current_args.clone());
200 let operand = self.alloc(BinOpOperand {
201 expr: call_expr,
202 op,
203 });
204 ops.push(operand);
205 current_expr = new_expr;
206 current_args = Vec::new();
207 current_end = new_end;
208 }
209 ExprEndState::Final(op, final_expr, final_end) => {
210 let call_expr = to_call(self, start, current_expr, current_args);
212 let operand = self.alloc(BinOpOperand {
213 expr: call_expr,
214 op,
215 });
216 ops.push(operand);
217
218 let ops_slice = ops.into_bump_slice();
219 let binops = Expr::BinOps {
220 operands: ops_slice,
221 last: final_expr,
222 };
223 let result = self.alloc(Located::at(Region::new(start, final_end), binops));
224 return Ok((result, final_end));
225 }
226 ExprEndState::Done => {
227 let (saved_ops, saved_expr, saved_args, saved_end) = state_for_fallback;
229 let final_call = to_call(self, start, saved_expr, saved_args);
230
231 if saved_ops.is_empty() {
232 return Ok((final_call, saved_end));
233 } else {
234 let ops_slice = saved_ops.into_bump_slice();
235 let binops = Expr::BinOps {
236 operands: ops_slice,
237 last: final_call,
238 };
239 let result = self.alloc(Located::at(Region::new(start, saved_end), binops));
240 return Ok((result, saved_end));
241 }
242 }
243 }
244 }
245 }
246
247 fn possibly_negative_term(
260 &mut self,
261 start: Position,
262 ) -> Result<&'a Located<Expr<'a>>, error::Expr<'a>> {
263 self.one_of(
264 error::Expr::Start,
265 vec![
266 Box::new(|p: &mut Parser<'a>| {
267 p.word1(b'-', error::Expr::Start)?;
268 let expr = p.term()?;
269 Ok(p.add_end(start, Expr::Negate(expr)))
270 }),
271 Box::new(|p| p.term()),
272 ],
273 )
274 }
275
276 pub fn term(&mut self) -> Result<&'a Located<Expr<'a>>, error::Expr<'a>> {
290 let start = self.get_position();
291
292 self.one_of(
293 error::Expr::Start,
294 vec![
295 Box::new(|p: &mut Parser<'a>| {
296 let expr = p.variable(start)?;
297 p.accessible(start, expr)
298 }),
299 Box::new(|p| p.string(start)),
300 Box::new(|p| p.number(start)),
301 Box::new(|p| p.list(start)),
302 Box::new(|p| {
303 let expr = p.record(start)?;
304 p.accessible(start, expr)
305 }),
306 Box::new(|p| {
307 let expr = p.tuple(start)?;
308 p.accessible(start, expr)
309 }),
310 Box::new(|p| p.accessor(start)),
311 ],
312 )
313 }
314}
315
316fn to_call<'a>(
326 parser: &Parser<'a>,
327 _start: Position,
328 func: &'a Located<Expr<'a>>,
329 args: Vec<&'a Located<Expr<'a>>>,
330) -> &'a Located<Expr<'a>> {
331 if args.is_empty() {
332 func
333 } else {
334 let last_arg = args.last().unwrap();
335 let region = Region::span_across(&func.region, &last_arg.region);
336 let args_slice = parser.alloc_slice_copy(&args);
337 parser.alloc(Located::at(
338 region,
339 Expr::Call {
340 function: func,
341 arguments: args_slice,
342 },
343 ))
344 }
345}
346
347enum ExprEndState<'a> {
349 MoreArgs(Vec<&'a Located<Expr<'a>>>, Position),
351 MoreOps(&'a Located<&'a str>, &'a Located<Expr<'a>>, Position),
353 Final(&'a Located<&'a str>, &'a Located<Expr<'a>>, Position),
355 Done,
357}
358
359#[cfg(test)]
361macro_rules! assert_expr_snapshot {
362 ($code:expr) => {{
363 let bump = bumpalo::Bump::new();
364 let src = bump.alloc_str(indoc::indoc!($code));
365 let mut parser = $crate::Parser::new(&bump, src.as_bytes());
366 let result = parser.term().expect("expected successful parse");
367
368 insta::with_settings!({
369 description => format!("Code:\n\n{}", indoc::indoc!($code)),
370 omit_expression => true,
371 }, {
372 insta::assert_debug_snapshot!(result);
373 });
374 }};
375}
376
377#[cfg(test)]
379macro_rules! assert_expr_error_snapshot {
380 ($code:expr) => {{
381 let bump = bumpalo::Bump::new();
382 let src = bump.alloc_str(indoc::indoc!($code));
383 let mut parser = $crate::Parser::new(&bump, src.as_bytes());
384 let result = parser.term().expect_err("expected parse error");
385
386 insta::with_settings!({
387 description => format!("Code:\n\n{}", indoc::indoc!($code)),
388 omit_expression => true,
389 }, {
390 insta::assert_debug_snapshot!(result);
391 });
392 }};
393}
394
395#[cfg(test)]
397macro_rules! assert_expression_snapshot {
398 ($code:expr) => {{
399 let bump = bumpalo::Bump::new();
400 let src = bump.alloc_str(indoc::indoc!($code));
401 let mut parser = $crate::Parser::new(&bump, src.as_bytes());
402 let (result, _end) = parser.expression().expect("expected successful parse");
403
404 insta::with_settings!({
405 description => format!("Code:\n\n{}", indoc::indoc!($code)),
406 omit_expression => true,
407 }, {
408 insta::assert_debug_snapshot!(result);
409 });
410 }};
411}
412
413#[cfg(test)]
414pub(crate) use assert_expr_error_snapshot;
415#[cfg(test)]
416pub(crate) use assert_expr_snapshot;
417#[cfg(test)]
418pub(crate) use assert_expression_snapshot;
419
420#[cfg(test)]
421mod tests {
422 #[test]
423 fn negate_var() {
424 assert_expression_snapshot!("-x");
425 }
426
427 #[test]
428 fn negate_number() {
429 assert_expression_snapshot!("-42");
430 }
431
432 #[test]
433 fn negate_parens() {
434 assert_expression_snapshot!("-(a)");
435 }
436
437 #[test]
438 fn expr_simple_var() {
439 assert_expression_snapshot!("foo");
440 }
441
442 #[test]
443 fn expr_simple_number() {
444 assert_expression_snapshot!("123");
445 }
446
447 #[test]
448 fn application_single() {
449 assert_expression_snapshot!("f x");
450 }
451
452 #[test]
453 fn application_multiple() {
454 assert_expression_snapshot!("f x y z");
455 }
456
457 #[test]
458 fn application_nested() {
459 assert_expression_snapshot!("f (g x)");
460 }
461
462 #[test]
463 fn application_with_record() {
464 assert_expression_snapshot!("f { x = 1 }");
465 }
466
467 #[test]
469 fn binop_simple_add() {
470 assert_expression_snapshot!("a + b");
471 }
472
473 #[test]
474 fn binop_simple_subtract() {
475 assert_expression_snapshot!("a - b");
476 }
477
478 #[test]
479 fn binop_simple_multiply() {
480 assert_expression_snapshot!("a * b");
481 }
482
483 #[test]
484 fn binop_simple_divide() {
485 assert_expression_snapshot!("a / b");
486 }
487
488 #[test]
489 fn binop_chained() {
490 assert_expression_snapshot!("a + b + c");
491 }
492
493 #[test]
494 fn binop_mixed() {
495 assert_expression_snapshot!("a + b * c");
496 }
497
498 #[test]
499 fn binop_with_parens() {
500 assert_expression_snapshot!("(a + b) * c");
501 }
502
503 #[test]
504 fn binop_pipe_right() {
505 assert_expression_snapshot!("a |> b |> c");
506 }
507
508 #[test]
509 fn binop_pipe_left() {
510 assert_expression_snapshot!("c <| b <| a");
511 }
512
513 #[test]
514 fn binop_comparison() {
515 assert_expression_snapshot!("a == b");
516 }
517
518 #[test]
519 fn binop_less_than() {
520 assert_expression_snapshot!("a < b");
521 }
522
523 #[test]
524 fn binop_greater_than() {
525 assert_expression_snapshot!("a > b");
526 }
527
528 #[test]
529 fn binop_append() {
530 assert_expression_snapshot!("a ++ b");
531 }
532
533 #[test]
534 fn binop_cons() {
535 assert_expression_snapshot!("a :: b");
536 }
537
538 #[test]
539 fn binop_logical_and() {
540 assert_expression_snapshot!("a && b");
541 }
542
543 #[test]
544 fn binop_logical_or() {
545 assert_expression_snapshot!("a || b");
546 }
547
548 #[test]
549 fn binop_less_than_or_equal() {
550 assert_expression_snapshot!("a <= b");
551 }
552
553 #[test]
554 fn binop_greater_than_or_equal() {
555 assert_expression_snapshot!("a >= b");
556 }
557
558 #[test]
559 fn binop_not_equal() {
560 assert_expression_snapshot!("a /= b");
561 }
562
563 #[test]
564 fn binop_compose_right() {
565 assert_expression_snapshot!("f >> g");
566 }
567
568 #[test]
569 fn binop_compose_left() {
570 assert_expression_snapshot!("f << g");
571 }
572
573 #[test]
574 fn binop_power() {
575 assert_expression_snapshot!("a ^ b");
576 }
577
578 #[test]
579 fn binop_with_function_application() {
580 assert_expression_snapshot!("f x + g y");
581 }
582
583 #[test]
584 fn binop_with_negation() {
585 assert_expression_snapshot!("a + -b");
586 }
587
588 #[test]
589 fn binop_negative_first() {
590 assert_expression_snapshot!("-a + b");
591 }
592
593 #[test]
594 fn binop_with_let() {
595 assert_expression_snapshot!("a + let x = 1 in x");
596 }
597
598 #[test]
599 fn binop_with_if() {
600 assert_expression_snapshot!("a + if b then c else d");
601 }
602
603 #[test]
604 fn binop_with_case() {
605 assert_expression_snapshot!(
606 r#"
607 a + case x of
608 1 -> y
609 _ -> z
610 "#
611 );
612 }
613
614 #[test]
615 fn binop_with_lambda() {
616 assert_expression_snapshot!("a + \\x -> x");
617 }
618
619 #[test]
621 fn op_section_plus() {
622 assert_expression_snapshot!("(+)");
623 }
624
625 #[test]
626 fn op_section_minus() {
627 assert_expression_snapshot!("(-)");
628 }
629
630 #[test]
631 fn op_section_multiply() {
632 assert_expression_snapshot!("(*)");
633 }
634
635 #[test]
636 fn op_section_append() {
637 assert_expression_snapshot!("(++)");
638 }
639
640 #[test]
641 fn op_section_pipe_right() {
642 assert_expression_snapshot!("(|>)");
643 }
644
645 #[test]
646 fn op_section_cons() {
647 assert_expression_snapshot!("(::)");
648 }
649
650 #[test]
652 fn application_with_negative_arg() {
653 assert_expression_snapshot!("f -x");
655 }
656
657 #[test]
658 fn negation_in_parens() {
659 assert_expression_snapshot!("(-42)");
660 }
661
662 #[test]
663 fn negation_in_tuple() {
664 assert_expression_snapshot!("(-a, b)");
665 }
666}