aiken_lang/parser/expr/
record_update.rs1use chumsky::prelude::*;
2
3use crate::{
4 ast,
5 expr::UntypedExpr,
6 parser::{error::ParseError, token::Token},
7};
8
9pub fn parser(
10 r: Recursive<'_, Token, UntypedExpr, ParseError>,
11) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
12 select! {Token::Name { name } => name}
13 .map_with_span(|module, span: ast::Span| (module, span))
14 .then_ignore(just(Token::Dot))
15 .or_not()
16 .then(select! {Token::UpName { name } => name}.map_with_span(|name, span| (name, span)))
17 .then(
18 just(Token::DotDot)
19 .ignore_then(r.clone())
20 .then(
21 just(Token::Comma)
22 .ignore_then(
23 choice((
24 select! { Token::Name {name} => name }
25 .then_ignore(just(Token::Colon))
26 .then(r.clone())
27 .map_with_span(|(label, value), span| {
28 ast::UntypedRecordUpdateArg {
29 label,
30 value,
31 location: span,
32 }
33 }),
34 select! {Token::Name {name} => name}.map_with_span(|name, span| {
35 ast::UntypedRecordUpdateArg {
36 location: span,
37 value: UntypedExpr::Var {
38 name: name.clone(),
39 location: span,
40 },
41 label: name,
42 }
43 }),
44 ))
45 .separated_by(just(Token::Comma))
46 .allow_trailing(),
47 )
48 .or_not(),
49 )
50 .delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
51 .map_with_span(|a, span: ast::Span| (a, span)),
52 )
53 .map(|((module, (name, n_span)), ((spread, opt_args), span))| {
54 let constructor = if let Some((module, m_span)) = module {
55 UntypedExpr::FieldAccess {
56 location: m_span.union(n_span),
57 label: name,
58 container: Box::new(UntypedExpr::Var {
59 location: m_span,
60 name: module,
61 }),
62 }
63 } else {
64 UntypedExpr::Var {
65 location: n_span,
66 name,
67 }
68 };
69
70 let spread_span = spread.location();
71
72 let location = ast::Span::new((), spread_span.start - 2..spread_span.end);
73
74 let spread = ast::RecordUpdateSpread {
75 base: Box::new(spread),
76 location,
77 };
78
79 UntypedExpr::RecordUpdate {
80 location: constructor.location().union(span),
81 constructor: Box::new(constructor),
82 spread,
83 arguments: opt_args.unwrap_or_default(),
84 }
85 })
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::assert_expr;
91
92 #[test]
93 fn record_update_basic() {
94 assert_expr!(r#"User { ..user, name: "Aiken", age }"#);
95 }
96}