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.
Constructor {
is_record: bool,
location: Span,
name: String,
arguments: Vec<CallArg<Self>>,

View File

@ -737,6 +737,7 @@ impl<'comments> Formatter<'comments> {
args: &'a [CallArg<UntypedPattern>],
module: &'a Option<String>,
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 {
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<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> {
if length == 0 {
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> {
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,8 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
label: Some(name),
value: pattern,
}),
r.map_with_span(|pattern, span| ast::CallArg {
location: span,
value: pattern,
label: None,
}),
));
let constructor_pattern_args_parser = constructor_pattern_arg_parser
no_label.clone(),
))
.separated_by(just(Token::Comma))
.allow_trailing()
.then(
@ -938,12 +938,28 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
.ignored()
.or_not(),
)
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace));
let tuple_constructor_pattern_arg_parser = no_label
.separated_by(just(Token::Comma))
.allow_trailing()
.then(
just(Token::DotDot)
.then_ignore(just(Token::Comma).or_not())
.ignored()
.or_not(),
)
.delimited_by(just(Token::LeftParen), just(Token::RightParen));
let constructor_pattern_args_parser = choice((
record_constructor_pattern_arg_parser.map(|a| (a, true)),
tuple_constructor_pattern_arg_parser.map(|a| (a, false)),
))
.or_not()
.map(|opt_args| {
opt_args
.map(|(a, b)| (a, b.is_some()))
.unwrap_or_else(|| (vec![], false))
.map(|((a, b), c)| (a, b.is_some(), c))
.unwrap_or_else(|| (vec![], false, false))
});
let constructor_pattern_parser =
@ -957,8 +973,9 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
.or_not(),
)
.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 {
is_record,
location: span,
name: c_name,
arguments,
@ -974,8 +991,10 @@ 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(
|(name, (arguments, with_spread, is_record)), span| {
ast::UntypedPattern::Constructor {
is_record,
location: span,
name,
arguments,
@ -984,7 +1003,8 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
with_spread,
tipo: (),
}
}),
},
),
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::UntypedPattern::Discard {
name,

View File

@ -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 {

View File

@ -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
}
}