diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index 59bd78b7..af9d94c7 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -575,6 +575,7 @@ pub enum Pattern { /// The constructor for a custom type. Starts with an uppercase letter. Constructor { + is_record: bool, location: Span, name: String, arguments: Vec>, diff --git a/crates/lang/src/format.rs b/crates/lang/src/format.rs index fb8aad0e..993796fd 100644 --- a/crates/lang/src/format.rs +++ b/crates/lang/src/format.rs @@ -737,6 +737,7 @@ impl<'comments> Formatter<'comments> { args: &'a [CallArg], module: &'a Option, with_spread: bool, + is_record: bool, ) -> Document<'a> { fn is_breakable(expr: &UntypedPattern) -> bool { match expr { @@ -754,25 +755,32 @@ impl<'comments> Formatter<'comments> { }; if args.is_empty() && with_spread { - name.append("(..)") + if is_record { + name.append("{..}") + } else { + name.append("(..)") + } } else if args.is_empty() { name } else if with_spread { - name.append(wrap_args_with_spread( - args.iter().map(|a| self.pattern_call_arg(a)), - )) + let wrapped_args = if is_record { + wrap_fields_with_spread(args.iter().map(|a| self.pattern_call_arg(a))) + } else { + wrap_args_with_spread(args.iter().map(|a| self.pattern_call_arg(a))) + }; + + name.append(wrapped_args) } else { match args { [arg] if is_breakable(&arg.value) => name - .append(if arg.label.is_some() { "{" } else { "(" }) + .append(if is_record { "{" } else { "(" }) .append(self.pattern_call_arg(arg)) - .append(if arg.label.is_some() { "}" } else { ")" }) + .append(if is_record { "}" } else { ")" }) .group(), _ => name .append(wrap_args( - args.iter() - .map(|a| (self.pattern_call_arg(a), a.label.is_some())), + args.iter().map(|a| (self.pattern_call_arg(a), is_record)), )) .group(), } @@ -1254,8 +1262,9 @@ impl<'comments> Formatter<'comments> { arguments: args, module, with_spread, + is_record, .. - } => self.pattern_constructor(name, args, module, *with_spread), + } => self.pattern_constructor(name, args, module, *with_spread, *is_record), }; commented(doc, comments) } @@ -1396,10 +1405,10 @@ where let args = args.map(|a| a.0); - let ((open_broken, open_unbroken), close) = if curly { - ((" {", " { "), "}") + let (open_broken, open_unbroken, close) = if curly { + (" {", " { ", "}") } else { - (("(", "("), ")") + ("(", "(", ")") }; break_(open_broken, open_unbroken) @@ -1444,6 +1453,25 @@ where .group() } +pub fn wrap_fields_with_spread<'a, I>(args: I) -> Document<'a> +where + I: IntoIterator>, +{ + let mut args = args.into_iter().peekable(); + if args.peek().is_none() { + return "()".to_doc(); + } + + break_(" {", " { ") + .append(join(args, break_(",", ", "))) + .append(break_(",", ", ")) + .append("..") + .nest(INDENT) + .append(break_("", " ")) + .append("}") + .group() +} + fn list<'a>(elements: Document<'a>, length: usize, tail: Option>) -> Document<'a> { if length == 0 { return match tail { diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index e53f082a..64c336ec 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -913,7 +913,13 @@ pub fn pub_parser() -> impl Parser { pub fn pattern_parser() -> impl Parser { recursive(|r| { - let constructor_pattern_arg_parser = choice(( + let no_label = r.clone().map_with_span(|pattern, span| ast::CallArg { + location: span, + value: pattern, + label: None, + }); + + let record_constructor_pattern_arg_parser = choice(( select! {Token::Name {name} => name} .then_ignore(just(Token::Colon)) .then(r.clone()) @@ -922,14 +928,19 @@ pub fn pattern_parser() -> impl Parser impl Parser name}.then(constructor_pattern_args_parser); @@ -957,8 +973,9 @@ pub fn pattern_parser() -> impl Parser impl Parser name}.map_with_span(|name, span| { ast::UntypedPattern::Discard { name, diff --git a/crates/lang/src/tipo/pattern.rs b/crates/lang/src/tipo/pattern.rs index 83cba23c..148b3c2e 100644 --- a/crates/lang/src/tipo/pattern.rs +++ b/crates/lang/src/tipo/pattern.rs @@ -409,6 +409,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { name, arguments: mut pattern_args, with_spread, + is_record, .. } => { // Register the value as seen for detection of unused values @@ -525,6 +526,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { constructor, with_spread, tipo: instantiated_constructor_type, + is_record, }) } else { Err(Error::IncorrectArity { @@ -552,6 +554,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { constructor, with_spread, tipo: instantiated_constructor_type, + is_record, }) } else { Err(Error::IncorrectArity { diff --git a/examples/sample/validators/swap.ak b/examples/sample/validators/swap.ak index 2183f2eb..6ff304cb 100644 --- a/examples/sample/validators/swap.ak +++ b/examples/sample/validators/swap.ak @@ -33,9 +33,10 @@ pub fn spend( rdmr: Redeemer, ctx: spend.ScriptContext, ) -> Bool { - when datum.rdmr is { - sample.Buy(tipo1, fin) -> fin > 0 - sample.Sell(twice, find: fin) -> fin > 0 + let x = datum.rdmr + when x is { + sample.Buy { fin: fin, .. } -> fin > 0 + sample.Sell { find: fin, .. } -> fin > 0 sample.Hold(some) -> some > 0 } }