1use std::sync::Arc;
9
10use chumsky::input::{Input as _, Stream, ValueInput};
11use chumsky::pratt::{infix, left, prefix, right};
12use chumsky::prelude::*;
13use chumsky::span::SimpleSpan;
14
15use panproto_expr::{BuiltinOp, Expr, Literal, Pattern};
16
17use crate::token::Token;
18
19pub type ParseError = Rich<'static, Token, SimpleSpan>;
21
22pub fn parse(tokens: &[crate::Spanned]) -> Result<Expr, Vec<ParseError>> {
30 let mapped: Vec<(Token, SimpleSpan)> = tokens
31 .iter()
32 .filter(|s| s.token != Token::Eof)
33 .map(|s| (s.token.clone(), SimpleSpan::new(s.span.start, s.span.end)))
34 .collect();
35 let eoi = tokens.last().map_or_else(
36 || SimpleSpan::new(0, 0),
37 |s| SimpleSpan::new(s.span.start, s.span.end),
38 );
39 let stream = Stream::from_iter(mapped).map(eoi, |(tok, span)| (tok, span));
40 expr_parser().parse(stream).into_result().map_err(|errs| {
41 errs.into_iter()
42 .map(chumsky::error::Rich::into_owned)
43 .collect()
44 })
45}
46
47fn ident<'t, 'src: 't, I>()
51-> impl Parser<'t, I, Arc<str>, extra::Err<Rich<'t, Token, SimpleSpan>>> + Clone
52where
53 I: ValueInput<'t, Token = Token, Span = SimpleSpan>,
54{
55 select! { Token::Ident(s) => Arc::from(s.as_str()) }.labelled("identifier")
56}
57
58fn upper_ident<'t, 'src: 't, I>()
60-> impl Parser<'t, I, Arc<str>, extra::Err<Rich<'t, Token, SimpleSpan>>> + Clone
61where
62 I: ValueInput<'t, Token = Token, Span = SimpleSpan>,
63{
64 select! { Token::UpperIdent(s) => Arc::from(s.as_str()) }.labelled("constructor")
65}
66
67fn layout_block<'t, 'src: 't, I, T: 't>(
72 item: impl Parser<'t, I, T, extra::Err<Rich<'t, Token, SimpleSpan>>> + Clone,
73) -> impl Parser<'t, I, Vec<T>, extra::Err<Rich<'t, Token, SimpleSpan>>> + Clone
74where
75 I: ValueInput<'t, Token = Token, Span = SimpleSpan>,
76{
77 let explicit = item
78 .clone()
79 .separated_by(just(Token::Newline).or(just(Token::Comma)))
80 .allow_trailing()
81 .collect::<Vec<_>>()
82 .delimited_by(just(Token::LBrace), just(Token::RBrace));
83
84 let implicit = item
85 .separated_by(just(Token::Newline))
86 .allow_trailing()
87 .collect::<Vec<_>>()
88 .delimited_by(just(Token::Indent), just(Token::Dedent));
89
90 explicit.or(implicit)
91}
92
93fn pattern_parser<'t, 'src: 't, I>()
97-> impl Parser<'t, I, Pattern, extra::Err<Rich<'t, Token, SimpleSpan>>> + Clone
98where
99 I: ValueInput<'t, Token = Token, Span = SimpleSpan>,
100{
101 recursive(|pat| {
102 let wildcard = select! { Token::Ident(s) if s == "_" => Pattern::Wildcard };
103
104 let var = ident().map(Pattern::Var);
105
106 let literal_pat = literal_parser().map(Pattern::Lit);
107
108 let paren = pat
109 .clone()
110 .delimited_by(just(Token::LParen), just(Token::RParen));
111
112 let list_pat = pat
113 .clone()
114 .separated_by(just(Token::Comma))
115 .collect::<Vec<_>>()
116 .delimited_by(just(Token::LBracket), just(Token::RBracket))
117 .map(Pattern::List);
118
119 let field_pat = ident()
120 .then(just(Token::Eq).ignore_then(pat.clone()).or_not())
121 .map(|(name, maybe_pat): (Arc<str>, Option<Pattern>)| {
122 let p = maybe_pat.unwrap_or_else(|| Pattern::Var(name.clone()));
123 (name, p)
124 });
125
126 let record_pat = field_pat
127 .separated_by(just(Token::Comma))
128 .collect::<Vec<_>>()
129 .delimited_by(just(Token::LBrace), just(Token::RBrace))
130 .map(Pattern::Record);
131
132 let constructor = upper_ident()
133 .then(pat.clone().repeated().collect::<Vec<_>>())
134 .map(|(name, args): (Arc<str>, Vec<Pattern>)| Pattern::Constructor(name, args));
135
136 choice((
137 wildcard,
138 literal_pat,
139 paren,
140 list_pat,
141 record_pat,
142 constructor,
143 var,
144 ))
145 })
146}
147
148fn literal_parser<'t, 'src: 't, I>()
152-> impl Parser<'t, I, Literal, extra::Err<Rich<'t, Token, SimpleSpan>>> + Clone
153where
154 I: ValueInput<'t, Token = Token, Span = SimpleSpan>,
155{
156 select! {
157 Token::Int(n) => Literal::Int(n),
158 Token::Float(f) => Literal::Float(f),
159 Token::Str(s) => Literal::Str(s),
160 Token::True => Literal::Bool(true),
161 Token::False => Literal::Bool(false),
162 Token::Nothing => Literal::Null,
163 }
164 .labelled("literal")
165}
166
167fn resolve_builtin(name: &str) -> Option<BuiltinOp> {
171 match name {
172 "add" => Some(BuiltinOp::Add),
173 "sub" => Some(BuiltinOp::Sub),
174 "mul" => Some(BuiltinOp::Mul),
175 "abs" => Some(BuiltinOp::Abs),
176 "floor" => Some(BuiltinOp::Floor),
177 "ceil" => Some(BuiltinOp::Ceil),
178 "round" => Some(BuiltinOp::Round),
179 "concat" => Some(BuiltinOp::Concat),
180 "len" => Some(BuiltinOp::Len),
181 "slice" => Some(BuiltinOp::Slice),
182 "upper" => Some(BuiltinOp::Upper),
183 "lower" => Some(BuiltinOp::Lower),
184 "trim" => Some(BuiltinOp::Trim),
185 "split" => Some(BuiltinOp::Split),
186 "join" => Some(BuiltinOp::Join),
187 "replace" => Some(BuiltinOp::Replace),
188 "contains" => Some(BuiltinOp::Contains),
189 "map" => Some(BuiltinOp::Map),
190 "filter" => Some(BuiltinOp::Filter),
191 "fold" => Some(BuiltinOp::Fold),
192 "append" => Some(BuiltinOp::Append),
193 "head" => Some(BuiltinOp::Head),
194 "tail" => Some(BuiltinOp::Tail),
195 "reverse" => Some(BuiltinOp::Reverse),
196 "flat_map" | "flatMap" => Some(BuiltinOp::FlatMap),
197 "length" => Some(BuiltinOp::Length),
198 "merge" | "merge_records" => Some(BuiltinOp::MergeRecords),
199 "keys" => Some(BuiltinOp::Keys),
200 "values" => Some(BuiltinOp::Values),
201 "has_field" | "hasField" => Some(BuiltinOp::HasField),
202 "default" | "default_val" | "defaultVal" => Some(BuiltinOp::DefaultVal),
203 "clamp" => Some(BuiltinOp::Clamp),
204 "truncate_str" | "truncateStr" => Some(BuiltinOp::TruncateStr),
205 "int_to_float" | "intToFloat" => Some(BuiltinOp::IntToFloat),
206 "float_to_int" | "floatToInt" => Some(BuiltinOp::FloatToInt),
207 "int_to_str" | "intToStr" => Some(BuiltinOp::IntToStr),
208 "float_to_str" | "floatToStr" => Some(BuiltinOp::FloatToStr),
209 "str_to_int" | "strToInt" => Some(BuiltinOp::StrToInt),
210 "str_to_float" | "strToFloat" => Some(BuiltinOp::StrToFloat),
211 "type_of" | "typeOf" => Some(BuiltinOp::TypeOf),
212 "is_null" | "isNull" => Some(BuiltinOp::IsNull),
213 "is_list" | "isList" => Some(BuiltinOp::IsList),
214 "edge" => Some(BuiltinOp::Edge),
215 "children" => Some(BuiltinOp::Children),
216 "has_edge" | "hasEdge" => Some(BuiltinOp::HasEdge),
217 "edge_count" | "edgeCount" => Some(BuiltinOp::EdgeCount),
218 "anchor" => Some(BuiltinOp::Anchor),
219 _ => None,
220 }
221}
222
223#[allow(clippy::too_many_lines)]
236fn expr_parser<'t, 'src: 't, I>()
237-> impl Parser<'t, I, Expr, extra::Err<Rich<'t, Token, SimpleSpan>>> + Clone
238where
239 I: ValueInput<'t, Token = Token, Span = SimpleSpan>,
240{
241 recursive(|expr| {
242 let pattern = pattern_parser();
243
244 let lit = literal_parser().map(Expr::Lit);
247
248 let var_or_builtin = ident().map(Expr::Var);
249
250 let constructor = upper_ident().map(Expr::Var);
251
252 let paren_expr = expr
253 .clone()
254 .delimited_by(just(Token::LParen), just(Token::RParen));
255
256 let list_expr = {
258 let plain_list = expr
259 .clone()
260 .separated_by(just(Token::Comma))
261 .collect::<Vec<_>>()
262 .map(Expr::List);
263
264 let comprehension = expr
266 .clone()
267 .then_ignore(just(Token::Pipe))
268 .then(
269 ident()
270 .then_ignore(just(Token::LeftArrow))
271 .then(expr.clone())
272 .map(|(n, e): (Arc<str>, Expr)| Qual::Generator(n, e))
273 .or(expr.clone().map(Qual::Guard))
274 .separated_by(just(Token::Comma))
275 .at_least(1)
276 .collect::<Vec<Qual>>(),
277 )
278 .map(|(body, quals): (Expr, Vec<Qual>)| desugar_comprehension(body, &quals));
279
280 let range = expr
282 .clone()
283 .then_ignore(just(Token::DotDot))
284 .then(expr.clone().or_not())
285 .map(|(start, end): (Expr, Option<Expr>)| match end {
286 Some(stop) => Expr::Builtin(
287 BuiltinOp::Map,
288 vec![
289 Expr::Lam(
290 Arc::from("_i"),
291 Box::new(Expr::Builtin(
292 BuiltinOp::Add,
293 vec![start.clone(), Expr::Var(Arc::from("_i"))],
294 )),
295 ),
296 Expr::Builtin(
297 BuiltinOp::Sub,
298 vec![
299 Expr::Builtin(
300 BuiltinOp::Add,
301 vec![stop, Expr::Lit(Literal::Int(1))],
302 ),
303 start,
304 ],
305 ),
306 ],
307 ),
308 None => Expr::List(vec![start]),
309 });
310
311 choice((comprehension, range, plain_list))
312 .delimited_by(just(Token::LBracket), just(Token::RBracket))
313 };
314
315 let record_expr = {
317 let field_bind = ident()
318 .then(just(Token::Eq).ignore_then(expr.clone()).or_not())
319 .map(|(name, val): (Arc<str>, Option<Expr>)| {
320 let v = val.unwrap_or_else(|| Expr::Var(name.clone()));
321 (name, v)
322 });
323
324 field_bind
325 .separated_by(just(Token::Comma))
326 .allow_trailing()
327 .collect::<Vec<_>>()
328 .delimited_by(just(Token::LBrace), just(Token::RBrace))
329 .map(Expr::Record)
330 };
331
332 let atom = choice((
333 lit,
334 paren_expr,
335 list_expr,
336 record_expr,
337 constructor,
338 var_or_builtin,
339 ));
340
341 let postfix_chain = atom.foldl(
344 choice((
345 just(Token::Dot).ignore_then(ident()).map(PostfixOp::Field),
346 just(Token::Arrow).ignore_then(ident()).map(PostfixOp::Edge),
347 ))
348 .repeated(),
349 |expr, postfix| match postfix {
350 PostfixOp::Field(name) => Expr::Field(Box::new(expr), name),
351 PostfixOp::Edge(edge) => Expr::Builtin(
352 BuiltinOp::Edge,
353 vec![expr, Expr::Lit(Literal::Str(edge.to_string()))],
354 ),
355 },
356 );
357
358 let app = postfix_chain
361 .clone()
362 .foldl(postfix_chain.repeated(), resolve_application);
363
364 let pratt = app.pratt((
367 infix(left(1), just(Token::Ampersand), |l, _, r, _| {
369 Expr::App(Box::new(r), Box::new(l))
370 }),
371 infix(left(3), just(Token::OrOr), |l, _, r, _| {
373 Expr::Builtin(BuiltinOp::Or, vec![l, r])
374 }),
375 infix(left(4), just(Token::AndAnd), |l, _, r, _| {
377 Expr::Builtin(BuiltinOp::And, vec![l, r])
378 }),
379 infix(right(5), just(Token::EqEq), |l, _, r, _| {
381 Expr::Builtin(BuiltinOp::Eq, vec![l, r])
382 }),
383 infix(right(5), just(Token::Neq), |l, _, r, _| {
384 Expr::Builtin(BuiltinOp::Neq, vec![l, r])
385 }),
386 infix(right(5), just(Token::Lt), |l, _, r, _| {
387 Expr::Builtin(BuiltinOp::Lt, vec![l, r])
388 }),
389 infix(right(5), just(Token::Lte), |l, _, r, _| {
390 Expr::Builtin(BuiltinOp::Lte, vec![l, r])
391 }),
392 infix(right(5), just(Token::Gt), |l, _, r, _| {
393 Expr::Builtin(BuiltinOp::Gt, vec![l, r])
394 }),
395 infix(right(5), just(Token::Gte), |l, _, r, _| {
396 Expr::Builtin(BuiltinOp::Gte, vec![l, r])
397 }),
398 infix(right(6), just(Token::PlusPlus), |l, _, r, _| {
400 Expr::Builtin(BuiltinOp::Concat, vec![l, r])
401 }),
402 infix(left(7), just(Token::Plus), |l, _, r, _| {
404 Expr::Builtin(BuiltinOp::Add, vec![l, r])
405 }),
406 infix(left(7), just(Token::Minus), |l, _, r, _| {
407 Expr::Builtin(BuiltinOp::Sub, vec![l, r])
408 }),
409 infix(left(8), just(Token::Star), |l, _, r, _| {
411 Expr::Builtin(BuiltinOp::Mul, vec![l, r])
412 }),
413 infix(left(8), just(Token::Slash), |l, _, r, _| {
414 Expr::Builtin(BuiltinOp::Div, vec![l, r])
415 }),
416 infix(left(8), just(Token::Percent), |l, _, r, _| {
417 Expr::Builtin(BuiltinOp::Mod, vec![l, r])
418 }),
419 infix(left(8), just(Token::ModKw), |l, _, r, _| {
420 Expr::Builtin(BuiltinOp::Mod, vec![l, r])
421 }),
422 infix(left(8), just(Token::DivKw), |l, _, r, _| {
423 Expr::Builtin(BuiltinOp::Div, vec![l, r])
424 }),
425 prefix(9, just(Token::Minus), |_, rhs, _| {
427 Expr::Builtin(BuiltinOp::Neg, vec![rhs])
428 }),
429 prefix(9, just(Token::Not), |_, rhs, _| {
430 Expr::Builtin(BuiltinOp::Not, vec![rhs])
431 }),
432 ));
433
434 let lambda = just(Token::Backslash)
438 .ignore_then(
439 pattern
440 .clone()
441 .repeated()
442 .at_least(1)
443 .collect::<Vec<Pattern>>(),
444 )
445 .then_ignore(just(Token::Arrow))
446 .then(expr.clone())
447 .map(|(params, body): (Vec<Pattern>, Expr)| desugar_lambda(¶ms, body));
448
449 let let_bind = ident()
451 .then(pattern.clone().repeated().collect::<Vec<Pattern>>())
452 .then_ignore(just(Token::Eq))
453 .then(expr.clone())
454 .map(|((name, params), val): ((Arc<str>, Vec<Pattern>), Expr)| {
455 if params.is_empty() {
456 (name, val)
457 } else {
458 (name, desugar_lambda(¶ms, val))
459 }
460 });
461
462 let let_expr = just(Token::Let)
463 .ignore_then(layout_block(let_bind.clone()).or(let_bind.clone().map(|b| vec![b])))
464 .then_ignore(just(Token::In))
465 .then(expr.clone())
466 .map(|(binds, body)| desugar_let_binds(binds, body));
467
468 let if_expr = just(Token::If)
470 .ignore_then(expr.clone())
471 .then_ignore(just(Token::Then))
472 .then(expr.clone())
473 .then_ignore(just(Token::Else))
474 .then(expr.clone())
475 .map(|((cond, then_branch), else_branch)| Expr::Match {
476 scrutinee: Box::new(cond),
477 arms: vec![
478 (Pattern::Lit(Literal::Bool(true)), then_branch),
479 (Pattern::Wildcard, else_branch),
480 ],
481 });
482
483 let case_arm = pattern
485 .clone()
486 .then_ignore(just(Token::Arrow))
487 .then(expr.clone());
488
489 let case_expr = just(Token::Case)
490 .ignore_then(expr.clone())
491 .then_ignore(just(Token::Of))
492 .then(layout_block(case_arm))
493 .map(|(scrutinee, arms)| Expr::Match {
494 scrutinee: Box::new(scrutinee),
495 arms,
496 });
497
498 let do_stmt = choice((
500 ident()
501 .then_ignore(just(Token::LeftArrow))
502 .then(expr.clone())
503 .map(|(name, e): (Arc<str>, Expr)| DoStmt::Bind(name, e)),
504 just(Token::Let)
505 .ignore_then(let_bind.clone())
506 .map(|(name, val)| DoStmt::Let(name, val)),
507 expr.clone().map(DoStmt::Expr),
508 ));
509
510 let do_expr = just(Token::Do)
511 .ignore_then(layout_block(do_stmt))
512 .map(desugar_do);
513
514 let full_expr = choice((do_expr, let_expr, if_expr, case_expr, lambda, pratt));
517
518 let where_bind = ident()
520 .then(pattern.repeated().collect::<Vec<Pattern>>())
521 .then_ignore(just(Token::Eq))
522 .then(expr.clone())
523 .map(|((name, params), val): ((Arc<str>, Vec<Pattern>), Expr)| {
524 if params.is_empty() {
525 (name, val)
526 } else {
527 (name, desugar_lambda(¶ms, val))
528 }
529 });
530
531 let where_clause = just(Token::Where)
532 .ignore_then(layout_block(where_bind.clone()).or(where_bind.map(|b| vec![b])));
533
534 full_expr
535 .then(where_clause.or_not())
536 .map(|(body, where_binds)| match where_binds {
537 Some(binds) => desugar_let_binds(binds, body),
538 None => body,
539 })
540 })
541}
542
543#[derive(Debug, Clone)]
547enum PostfixOp {
548 Field(Arc<str>),
550 Edge(Arc<str>),
552}
553
554#[derive(Debug, Clone)]
556enum Qual {
557 Generator(Arc<str>, Expr),
559 Guard(Expr),
561}
562
563#[derive(Debug, Clone)]
565enum DoStmt {
566 Bind(Arc<str>, Expr),
568 Let(Arc<str>, Expr),
570 Expr(Expr),
572}
573
574fn desugar_lambda(params: &[Pattern], body: Expr) -> Expr {
578 params.iter().rev().fold(body, |acc, pat| match pat {
579 Pattern::Var(name) => Expr::Lam(name.clone(), Box::new(acc)),
580 Pattern::Wildcard => Expr::Lam(Arc::from("_"), Box::new(acc)),
581 other => {
582 let fresh: Arc<str> = Arc::from("_arg");
583 Expr::Lam(
584 fresh.clone(),
585 Box::new(Expr::Match {
586 scrutinee: Box::new(Expr::Var(fresh)),
587 arms: vec![(other.clone(), acc)],
588 }),
589 )
590 }
591 })
592}
593
594fn desugar_let_binds(binds: Vec<(Arc<str>, Expr)>, body: Expr) -> Expr {
596 binds
597 .into_iter()
598 .rev()
599 .fold(body, |acc, (name, val)| Expr::Let {
600 name,
601 value: Box::new(val),
602 body: Box::new(acc),
603 })
604}
605
606fn desugar_comprehension(body: Expr, quals: &[Qual]) -> Expr {
608 quals
609 .iter()
610 .rev()
611 .fold(Expr::List(vec![body]), |acc, qual| match qual {
612 Qual::Generator(name, source) => Expr::Builtin(
613 BuiltinOp::FlatMap,
614 vec![source.clone(), Expr::Lam(name.clone(), Box::new(acc))],
615 ),
616 Qual::Guard(pred) => Expr::Match {
617 scrutinee: Box::new(pred.clone()),
618 arms: vec![
619 (Pattern::Lit(Literal::Bool(true)), acc),
620 (Pattern::Wildcard, Expr::List(vec![])),
621 ],
622 },
623 })
624}
625
626fn desugar_do(stmts: Vec<DoStmt>) -> Expr {
628 if stmts.is_empty() {
629 return Expr::List(vec![]);
630 }
631 let mut iter = stmts.into_iter().rev();
632 let Some(last) = iter.next() else {
634 return Expr::List(vec![]);
635 };
636 let init = match last {
637 DoStmt::Expr(e) | DoStmt::Bind(_, e) => e,
638 DoStmt::Let(name, val) => Expr::Let {
639 name,
640 value: Box::new(val),
641 body: Box::new(Expr::List(vec![])),
642 },
643 };
644 iter.fold(init, |acc, stmt| match stmt {
645 DoStmt::Bind(name, source) => Expr::Builtin(
646 BuiltinOp::FlatMap,
647 vec![source, Expr::Lam(name, Box::new(acc))],
648 ),
649 DoStmt::Let(name, val) => Expr::Let {
650 name,
651 value: Box::new(val),
652 body: Box::new(acc),
653 },
654 DoStmt::Expr(e) => Expr::Builtin(
655 BuiltinOp::FlatMap,
656 vec![e, Expr::Lam(Arc::from("_"), Box::new(acc))],
657 ),
658 })
659}
660
661fn resolve_application(func: Expr, arg: Expr) -> Expr {
663 match &func {
664 Expr::Var(name) => {
665 if let Some(op) = resolve_builtin(name) {
666 Expr::Builtin(op, vec![arg])
667 } else {
668 Expr::App(Box::new(func), Box::new(arg))
669 }
670 }
671 Expr::Builtin(op, args) if args.len() < op.arity() => {
672 let mut new_args = args.clone();
673 new_args.push(arg);
674 Expr::Builtin(*op, new_args)
675 }
676 _ => Expr::App(Box::new(func), Box::new(arg)),
677 }
678}
679
680#[cfg(test)]
681mod tests {
682 use super::*;
683 use crate::tokenize;
684
685 fn parse_ok(input: &str) -> Expr {
686 let tokens = tokenize(input).unwrap_or_else(|e| panic!("lex failed: {e}"));
687 parse(&tokens).unwrap_or_else(|e| panic!("parse failed: {e:?}"))
688 }
689
690 #[test]
691 fn parse_literal_int() {
692 assert_eq!(parse_ok("42"), Expr::Lit(Literal::Int(42)));
693 }
694
695 #[test]
696 fn parse_literal_string() {
697 assert_eq!(
698 parse_ok(r#""hello""#),
699 Expr::Lit(Literal::Str("hello".into()))
700 );
701 }
702
703 #[test]
704 fn parse_literal_bool() {
705 assert_eq!(parse_ok("True"), Expr::Lit(Literal::Bool(true)));
706 assert_eq!(parse_ok("False"), Expr::Lit(Literal::Bool(false)));
707 }
708
709 #[test]
710 fn parse_nothing() {
711 assert_eq!(parse_ok("Nothing"), Expr::Lit(Literal::Null));
712 }
713
714 #[test]
715 fn parse_variable() {
716 assert_eq!(parse_ok("x"), Expr::Var(Arc::from("x")));
717 }
718
719 #[test]
720 fn parse_arithmetic() {
721 assert_eq!(
722 parse_ok("1 + 2"),
723 Expr::Builtin(
724 BuiltinOp::Add,
725 vec![Expr::Lit(Literal::Int(1)), Expr::Lit(Literal::Int(2))]
726 )
727 );
728 }
729
730 #[test]
731 fn parse_precedence() {
732 assert_eq!(
733 parse_ok("1 + 2 * 3"),
734 Expr::Builtin(
735 BuiltinOp::Add,
736 vec![
737 Expr::Lit(Literal::Int(1)),
738 Expr::Builtin(
739 BuiltinOp::Mul,
740 vec![Expr::Lit(Literal::Int(2)), Expr::Lit(Literal::Int(3))]
741 ),
742 ]
743 )
744 );
745 }
746
747 #[test]
748 fn parse_comparison() {
749 assert_eq!(
750 parse_ok("x == 1"),
751 Expr::Builtin(
752 BuiltinOp::Eq,
753 vec![Expr::Var(Arc::from("x")), Expr::Lit(Literal::Int(1))]
754 )
755 );
756 }
757
758 #[test]
759 fn parse_logical() {
760 assert_eq!(
761 parse_ok("a && b || c"),
762 Expr::Builtin(
763 BuiltinOp::Or,
764 vec![
765 Expr::Builtin(
766 BuiltinOp::And,
767 vec![Expr::Var(Arc::from("a")), Expr::Var(Arc::from("b"))]
768 ),
769 Expr::Var(Arc::from("c")),
770 ]
771 )
772 );
773 }
774
775 #[test]
776 fn parse_negation() {
777 assert_eq!(
778 parse_ok("-x"),
779 Expr::Builtin(BuiltinOp::Neg, vec![Expr::Var(Arc::from("x"))])
780 );
781 }
782
783 #[test]
784 fn parse_not() {
785 assert_eq!(
786 parse_ok("not True"),
787 Expr::Builtin(BuiltinOp::Not, vec![Expr::Lit(Literal::Bool(true))])
788 );
789 }
790
791 #[test]
792 fn parse_field_access() {
793 assert_eq!(
794 parse_ok("x.name"),
795 Expr::Field(Box::new(Expr::Var(Arc::from("x"))), Arc::from("name"))
796 );
797 }
798
799 #[test]
800 fn parse_edge_traversal() {
801 assert_eq!(
802 parse_ok("doc -> layers"),
803 Expr::Builtin(
804 BuiltinOp::Edge,
805 vec![
806 Expr::Var(Arc::from("doc")),
807 Expr::Lit(Literal::Str("layers".into())),
808 ]
809 )
810 );
811 }
812
813 #[test]
814 fn parse_lambda() {
815 assert_eq!(
816 parse_ok("\\x -> x + 1"),
817 Expr::Lam(
818 Arc::from("x"),
819 Box::new(Expr::Builtin(
820 BuiltinOp::Add,
821 vec![Expr::Var(Arc::from("x")), Expr::Lit(Literal::Int(1))]
822 ))
823 )
824 );
825 }
826
827 #[test]
828 fn parse_multi_param_lambda() {
829 let e = parse_ok("\\x y -> x + y");
830 match &e {
831 Expr::Lam(x, inner) => {
832 assert_eq!(&**x, "x");
833 assert!(matches!(&**inner, Expr::Lam(y, _) if &**y == "y"));
834 }
835 _ => panic!("expected nested Lam, got {e:?}"),
836 }
837 }
838
839 #[test]
840 fn parse_let_in() {
841 assert_eq!(
842 parse_ok("let x = 1 in x + 1"),
843 Expr::Let {
844 name: Arc::from("x"),
845 value: Box::new(Expr::Lit(Literal::Int(1))),
846 body: Box::new(Expr::Builtin(
847 BuiltinOp::Add,
848 vec![Expr::Var(Arc::from("x")), Expr::Lit(Literal::Int(1))]
849 )),
850 }
851 );
852 }
853
854 #[test]
855 fn parse_if_then_else() {
856 let e = parse_ok("if True then 1 else 0");
857 assert!(matches!(e, Expr::Match { .. }));
858 }
859
860 #[test]
861 fn parse_case_of() {
862 let e = parse_ok("case x of\n True -> 1\n False -> 0");
863 match e {
864 Expr::Match { arms, .. } => assert_eq!(arms.len(), 2),
865 _ => panic!("expected Match"),
866 }
867 }
868
869 #[test]
870 fn parse_list() {
871 assert_eq!(
872 parse_ok("[1, 2, 3]"),
873 Expr::List(vec![
874 Expr::Lit(Literal::Int(1)),
875 Expr::Lit(Literal::Int(2)),
876 Expr::Lit(Literal::Int(3)),
877 ])
878 );
879 }
880
881 #[test]
882 fn parse_empty_list() {
883 assert_eq!(parse_ok("[]"), Expr::List(vec![]));
884 }
885
886 #[test]
887 fn parse_record() {
888 assert_eq!(
889 parse_ok("{ name = x, age = 30 }"),
890 Expr::Record(vec![
891 (Arc::from("name"), Expr::Var(Arc::from("x"))),
892 (Arc::from("age"), Expr::Lit(Literal::Int(30))),
893 ])
894 );
895 }
896
897 #[test]
898 fn parse_record_punning() {
899 assert_eq!(
900 parse_ok("{ name, age }"),
901 Expr::Record(vec![
902 (Arc::from("name"), Expr::Var(Arc::from("name"))),
903 (Arc::from("age"), Expr::Var(Arc::from("age"))),
904 ])
905 );
906 }
907
908 #[test]
909 fn parse_builtin_application() {
910 assert_eq!(
911 parse_ok("map f xs"),
912 Expr::Builtin(
913 BuiltinOp::Map,
914 vec![Expr::Var(Arc::from("f")), Expr::Var(Arc::from("xs"))]
915 )
916 );
917 }
918
919 #[test]
920 fn parse_string_concat() {
921 assert_eq!(
922 parse_ok(r#""hello" ++ " world""#),
923 Expr::Builtin(
924 BuiltinOp::Concat,
925 vec![
926 Expr::Lit(Literal::Str("hello".into())),
927 Expr::Lit(Literal::Str(" world".into())),
928 ]
929 )
930 );
931 }
932
933 #[test]
934 fn parse_pipe() {
935 assert_eq!(
936 parse_ok("x & f"),
937 Expr::App(
938 Box::new(Expr::Var(Arc::from("f"))),
939 Box::new(Expr::Var(Arc::from("x"))),
940 )
941 );
942 }
943
944 #[test]
945 fn parse_chained_field_access() {
946 assert_eq!(
947 parse_ok("x.a.b"),
948 Expr::Field(
949 Box::new(Expr::Field(
950 Box::new(Expr::Var(Arc::from("x"))),
951 Arc::from("a"),
952 )),
953 Arc::from("b"),
954 )
955 );
956 }
957
958 #[test]
959 fn parse_comprehension() {
960 let e = parse_ok("[ x + 1 | x <- xs ]");
961 assert!(matches!(e, Expr::Builtin(BuiltinOp::FlatMap, _)));
962 }
963}