feat: better errors for incorrect contructor making

This commit is contained in:
rvcas 2022-12-05 18:02:00 -05:00 committed by Lucas
parent 7e6dc978a1
commit 7875af7d35
5 changed files with 134 additions and 32 deletions

View File

@ -839,17 +839,34 @@ impl<'comments> Formatter<'comments> {
} }
fn call<'a>(&mut self, fun: &'a UntypedExpr, args: &'a [CallArg<UntypedExpr>]) -> Document<'a> { fn call<'a>(&mut self, fun: &'a UntypedExpr, args: &'a [CallArg<UntypedExpr>]) -> Document<'a> {
let is_constr = match fun {
UntypedExpr::Var { name, .. } => name[0..1].chars().all(|c| c.is_uppercase()),
UntypedExpr::FieldAccess { container, .. } => {
matches!(&**container, UntypedExpr::Var { name, .. } if name[0..1].chars().all(|c| c.is_uppercase()))
}
_ => false,
};
let needs_curly = if is_constr {
args.iter().all(|arg| arg.label.is_some())
} else {
false
};
match args { match args {
[arg] if is_breakable_expr(&arg.value) => self [arg] if is_breakable_expr(&arg.value) => self
.expr(fun) .expr(fun)
.append("(") .append(if needs_curly { "{" } else { "(" })
.append(self.call_arg(arg)) .append(self.call_arg(arg, needs_curly))
.append(")") .append(if needs_curly { "}" } else { ")" })
.group(), .group(),
_ => self _ => self
.expr(fun) .expr(fun)
.append(wrap_args(args.iter().map(|a| (self.call_arg(a), false)))) .append(wrap_args(
args.iter()
.map(|a| (self.call_arg(a, needs_curly), needs_curly)),
))
.group(), .group(),
} }
} }
@ -978,12 +995,18 @@ impl<'comments> Formatter<'comments> {
self.expr(fun) self.expr(fun)
} else if hole_in_first_position { } else if hole_in_first_position {
// x |> fun(_, 2, 3) // x |> fun(_, 2, 3)
self.expr(fun) self.expr(fun).append(
.append(wrap_args(args.iter().skip(1).map(|a| (self.call_arg(a), false))).group()) wrap_args(
args.iter()
.skip(1)
.map(|a| (self.call_arg(a, false), false)),
)
.group(),
)
} else { } else {
// x |> fun(1, _, 3) // x |> fun(1, _, 3)
self.expr(fun) self.expr(fun)
.append(wrap_args(args.iter().map(|a| (self.call_arg(a), false))).group()) .append(wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group())
} }
} }
@ -997,14 +1020,14 @@ impl<'comments> Formatter<'comments> {
[first, second] if is_breakable_expr(&second.value) && first.is_capture_hole() => { [first, second] if is_breakable_expr(&second.value) && first.is_capture_hole() => {
self.expr(fun) self.expr(fun)
.append("(_, ") .append("(_, ")
.append(self.call_arg(second)) .append(self.call_arg(second, false))
.append(")") .append(")")
.group() .group()
} }
_ => self _ => self.expr(fun).append(
.expr(fun) wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group(),
.append(wrap_args(args.iter().map(|a| (self.call_arg(a), false))).group()), ),
}, },
// The body of a capture being not a fn shouldn't be possible... // The body of a capture being not a fn shouldn't be possible...
@ -1188,12 +1211,18 @@ impl<'comments> Formatter<'comments> {
} }
} }
fn call_arg<'a>(&mut self, arg: &'a CallArg<UntypedExpr>) -> Document<'a> { fn call_arg<'a>(&mut self, arg: &'a CallArg<UntypedExpr>, can_pun: bool) -> Document<'a> {
match &arg.label { match &arg.label {
Some(s) => commented( Some(s) => {
if can_pun && matches!(&arg.value, UntypedExpr::Var { name, .. } if name == s) {
nil()
} else {
commented(
s.to_doc().append(": "), s.to_doc().append(": "),
self.pop_comments(arg.location.start), self.pop_comments(arg.location.start),
), )
}
}
None => nil(), None => nil(),
} }
.append(self.wrap_expr(&arg.value)) .append(self.wrap_expr(&arg.value))

View File

@ -650,21 +650,60 @@ pub fn expr_parser(
choice(( 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(choice((
r.clone(),
select! {Token::DiscardName {name} => name }.validate(
|_name, span, emit| {
emit(ParseError::expected_input_found(
span,
None,
Some(error::Pattern::Discard),
));
expr::UntypedExpr::Var {
location: span,
name: CAPTURE_VARIABLE.to_string(),
}
},
),
)))
.map_with_span(|(label, value), span| ast::CallArg { .map_with_span(|(label, value), span| ast::CallArg {
location: span, location: span,
value, value,
label: Some(label), label: Some(label),
}), }),
choice((
select! {Token::Name {name} => name}.map_with_span(|name, span| { select! {Token::Name {name} => name}.map_with_span(|name, span| {
ast::CallArg { (
location: span, expr::UntypedExpr::Var {
value: expr::UntypedExpr::Var {
name: name.clone(), name: name.clone(),
location: span, location: span,
}, },
name,
)
}),
select! {Token::DiscardName {name} => name }.validate(
|name, span, emit| {
emit(ParseError::expected_input_found(
span,
None,
Some(error::Pattern::Discard),
));
(
expr::UntypedExpr::Var {
location: span,
name: CAPTURE_VARIABLE.to_string(),
},
name,
)
},
),
))
.map(|(value, name)| ast::CallArg {
location: value.location(),
value,
label: Some(name), label: Some(name),
}
}), }),
)) ))
.separated_by(just(Token::Comma)) .separated_by(just(Token::Comma))
@ -680,9 +719,36 @@ pub fn expr_parser(
.map_with_span(|name, span| (name, span)), .map_with_span(|name, span| (name, span)),
) )
.then( .then(
r.clone() select! {Token::Name {name} => name}
.map_with_span(|value, span| ast::CallArg { .ignored()
.then_ignore(just(Token::Colon))
.validate(|_label, span, emit| {
emit(ParseError::expected_input_found(
span,
None,
Some(error::Pattern::Label),
))
})
.or_not()
.then(choice((
r.clone(),
select! {Token::DiscardName {name} => name }.validate(
|_name, span, emit| {
emit(ParseError::expected_input_found(
span,
None,
Some(error::Pattern::Discard),
));
expr::UntypedExpr::Var {
location: span, location: span,
name: CAPTURE_VARIABLE.to_string(),
}
},
),
)))
.map(|(_label, value)| ast::CallArg {
location: value.location(),
value, value,
label: None, label: None,
}) })
@ -1340,8 +1406,8 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
let tuple_constructor_pattern_arg_parser = r let tuple_constructor_pattern_arg_parser = r
.clone() .clone()
.map_with_span(|pattern, span| ast::CallArg { .map(|pattern| ast::CallArg {
location: span, location: pattern.location(),
value: pattern, value: pattern,
label: None, label: None,
}) })

View File

@ -115,6 +115,12 @@ pub enum Pattern {
"If no label is provided then only variables\nmatching a field name are allowed" "If no label is provided then only variables\nmatching a field name are allowed"
))] ))]
RecordPunning, RecordPunning,
#[error("Unexpected label")]
#[diagnostic(help("You can only use labels with curly braces"))]
Label,
#[error("Unexpected hole")]
#[diagnostic(help("You can only use capture syntax with functions not constructors"))]
Discard,
} }
impl From<char> for Pattern { impl From<char> for Pattern {

View File

@ -50,7 +50,8 @@ pub type Datum {
} }
pub fn spend(datum: Datum, _rdmr: Nil, _ctx: Nil) -> Bool { pub fn spend(datum: Datum, _rdmr: Nil, _ctx: Nil) -> Bool {
let x = Buy1 { signer: #[4, 4, 255], amount: 1000 } let amount = 1000
let x = Buy1 { signer: #[4, 4, 255], amount }
when x is { when x is {
Buy1 { signer, .. } -> signer == #[3, 3, 255] Buy1 { signer, .. } -> signer == #[3, 3, 255]
Stuff(signer, _) -> signer == #[3, 3, 255] Stuff(signer, _) -> signer == #[3, 3, 255]