fix: better constructor pattern parsing

This commit is contained in:
rvcas 2022-11-16 21:29:14 -05:00
parent ef9fd15e12
commit 72bf27d467
No known key found for this signature in database
GPG Key ID: C09B64E263F7D68C
5 changed files with 95 additions and 42 deletions

View File

@ -575,6 +575,7 @@ pub enum Pattern<Constructor, Type> {
/// The constructor for a custom type. Starts with an uppercase letter. /// The constructor for a custom type. Starts with an uppercase letter.
Constructor { Constructor {
is_record: bool,
location: Span, location: Span,
name: String, name: String,
arguments: Vec<CallArg<Self>>, arguments: Vec<CallArg<Self>>,

View File

@ -737,6 +737,7 @@ impl<'comments> Formatter<'comments> {
args: &'a [CallArg<UntypedPattern>], args: &'a [CallArg<UntypedPattern>],
module: &'a Option<String>, module: &'a Option<String>,
with_spread: bool, with_spread: bool,
is_record: bool,
) -> Document<'a> { ) -> Document<'a> {
fn is_breakable(expr: &UntypedPattern) -> bool { fn is_breakable(expr: &UntypedPattern) -> bool {
match expr { match expr {
@ -754,25 +755,32 @@ impl<'comments> Formatter<'comments> {
}; };
if args.is_empty() && with_spread { if args.is_empty() && with_spread {
name.append("(..)") if is_record {
name.append("{..}")
} else {
name.append("(..)")
}
} else if args.is_empty() { } else if args.is_empty() {
name name
} else if with_spread { } else if with_spread {
name.append(wrap_args_with_spread( let wrapped_args = if is_record {
args.iter().map(|a| self.pattern_call_arg(a)), 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 { } else {
match args { match args {
[arg] if is_breakable(&arg.value) => name [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(self.pattern_call_arg(arg))
.append(if arg.label.is_some() { "}" } else { ")" }) .append(if is_record { "}" } else { ")" })
.group(), .group(),
_ => name _ => name
.append(wrap_args( .append(wrap_args(
args.iter() args.iter().map(|a| (self.pattern_call_arg(a), is_record)),
.map(|a| (self.pattern_call_arg(a), a.label.is_some())),
)) ))
.group(), .group(),
} }
@ -1254,8 +1262,9 @@ impl<'comments> Formatter<'comments> {
arguments: args, arguments: args,
module, module,
with_spread, 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) commented(doc, comments)
} }
@ -1396,10 +1405,10 @@ where
let args = args.map(|a| a.0); let args = args.map(|a| a.0);
let ((open_broken, open_unbroken), close) = if curly { let (open_broken, open_unbroken, close) = if curly {
((" {", " { "), "}") (" {", " { ", "}")
} else { } else {
(("(", "("), ")") ("(", "(", ")")
}; };
break_(open_broken, open_unbroken) break_(open_broken, open_unbroken)
@ -1444,6 +1453,25 @@ where
.group() .group()
} }
pub fn wrap_fields_with_spread<'a, I>(args: I) -> Document<'a>
where
I: IntoIterator<Item = Document<'a>>,
{
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>>) -> Document<'a> { fn list<'a>(elements: Document<'a>, length: usize, tail: Option<Document<'a>>) -> Document<'a> {
if length == 0 { if length == 0 {
return match tail { return match tail {

View File

@ -913,7 +913,13 @@ pub fn pub_parser() -> impl Parser<Token, (), Error = ParseError> {
pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = ParseError> { pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = ParseError> {
recursive(|r| { 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} select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon)) .then_ignore(just(Token::Colon))
.then(r.clone()) .then(r.clone())
@ -922,14 +928,19 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
label: Some(name), label: Some(name),
value: pattern, value: pattern,
}), }),
r.map_with_span(|pattern, span| ast::CallArg { no_label.clone(),
location: span, ))
value: pattern, .separated_by(just(Token::Comma))
label: None, .allow_trailing()
}), .then(
)); just(Token::DotDot)
.then_ignore(just(Token::Comma).or_not())
.ignored()
.or_not(),
)
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace));
let constructor_pattern_args_parser = constructor_pattern_arg_parser let tuple_constructor_pattern_arg_parser = no_label
.separated_by(just(Token::Comma)) .separated_by(just(Token::Comma))
.allow_trailing() .allow_trailing()
.then( .then(
@ -938,13 +949,18 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
.ignored() .ignored()
.or_not(), .or_not(),
) )
.delimited_by(just(Token::LeftParen), just(Token::RightParen)) .delimited_by(just(Token::LeftParen), just(Token::RightParen));
.or_not()
.map(|opt_args| { let constructor_pattern_args_parser = choice((
opt_args record_constructor_pattern_arg_parser.map(|a| (a, true)),
.map(|(a, b)| (a, b.is_some())) tuple_constructor_pattern_arg_parser.map(|a| (a, false)),
.unwrap_or_else(|| (vec![], false)) ))
}); .or_not()
.map(|opt_args| {
opt_args
.map(|((a, b), c)| (a, b.is_some(), c))
.unwrap_or_else(|| (vec![], false, false))
});
let constructor_pattern_parser = let constructor_pattern_parser =
select! {Token::UpName { name } => name}.then(constructor_pattern_args_parser); select! {Token::UpName { name } => name}.then(constructor_pattern_args_parser);
@ -957,8 +973,9 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
.or_not(), .or_not(),
) )
.map_with_span(|(name, opt_pattern), span| { .map_with_span(|(name, opt_pattern), span| {
if let Some((c_name, (arguments, with_spread))) = opt_pattern { if let Some((c_name, (arguments, with_spread, is_record))) = opt_pattern {
ast::UntypedPattern::Constructor { ast::UntypedPattern::Constructor {
is_record,
location: span, location: span,
name: c_name, name: c_name,
arguments, arguments,
@ -974,17 +991,20 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
} }
} }
}), }),
constructor_pattern_parser.map_with_span(|(name, (arguments, with_spread)), span| { constructor_pattern_parser.map_with_span(
ast::UntypedPattern::Constructor { |(name, (arguments, with_spread, is_record)), span| {
location: span, ast::UntypedPattern::Constructor {
name, is_record,
arguments, location: span,
module: None, name,
constructor: (), arguments,
with_spread, module: None,
tipo: (), constructor: (),
} with_spread,
}), tipo: (),
}
},
),
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::UntypedPattern::Discard { ast::UntypedPattern::Discard {
name, name,

View File

@ -409,6 +409,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
name, name,
arguments: mut pattern_args, arguments: mut pattern_args,
with_spread, with_spread,
is_record,
.. ..
} => { } => {
// Register the value as seen for detection of unused values // Register the value as seen for detection of unused values
@ -525,6 +526,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
constructor, constructor,
with_spread, with_spread,
tipo: instantiated_constructor_type, tipo: instantiated_constructor_type,
is_record,
}) })
} else { } else {
Err(Error::IncorrectArity { Err(Error::IncorrectArity {
@ -552,6 +554,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
constructor, constructor,
with_spread, with_spread,
tipo: instantiated_constructor_type, tipo: instantiated_constructor_type,
is_record,
}) })
} else { } else {
Err(Error::IncorrectArity { Err(Error::IncorrectArity {

View File

@ -33,9 +33,10 @@ pub fn spend(
rdmr: Redeemer, rdmr: Redeemer,
ctx: spend.ScriptContext, ctx: spend.ScriptContext,
) -> Bool { ) -> Bool {
when datum.rdmr is { let x = datum.rdmr
sample.Buy(tipo1, fin) -> fin > 0 when x is {
sample.Sell(twice, find: fin) -> fin > 0 sample.Buy { fin: fin, .. } -> fin > 0
sample.Sell { find: fin, .. } -> fin > 0
sample.Hold(some) -> some > 0 sample.Hold(some) -> some > 0
} }
} }