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> {
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 {
[arg] if is_breakable_expr(&arg.value) => self
.expr(fun)
.append("(")
.append(self.call_arg(arg))
.append(")")
.append(if needs_curly { "{" } else { "(" })
.append(self.call_arg(arg, needs_curly))
.append(if needs_curly { "}" } else { ")" })
.group(),
_ => self
.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(),
}
}
@ -978,12 +995,18 @@ impl<'comments> Formatter<'comments> {
self.expr(fun)
} else if hole_in_first_position {
// x |> fun(_, 2, 3)
self.expr(fun)
.append(wrap_args(args.iter().skip(1).map(|a| (self.call_arg(a), false))).group())
self.expr(fun).append(
wrap_args(
args.iter()
.skip(1)
.map(|a| (self.call_arg(a, false), false)),
)
.group(),
)
} else {
// x |> fun(1, _, 3)
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() => {
self.expr(fun)
.append("(_, ")
.append(self.call_arg(second))
.append(self.call_arg(second, false))
.append(")")
.group()
}
_ => self
.expr(fun)
.append(wrap_args(args.iter().map(|a| (self.call_arg(a), false))).group()),
_ => self.expr(fun).append(
wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group(),
),
},
// 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 {
Some(s) => commented(
s.to_doc().append(": "),
self.pop_comments(arg.location.start),
),
Some(s) => {
if can_pun && matches!(&arg.value, UntypedExpr::Var { name, .. } if name == s) {
nil()
} else {
commented(
s.to_doc().append(": "),
self.pop_comments(arg.location.start),
)
}
}
None => nil(),
}
.append(self.wrap_expr(&arg.value))

View File

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

View File

@ -46,4 +46,4 @@ pub fn incrementor(counter: Int, target: Int) -> Int {
}
}
pub const big_a = 5
pub const big_a = 5

View File

@ -50,7 +50,8 @@ pub type Datum {
}
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 {
Buy1 { signer, .. } -> signer == #[3, 3, 255]
Stuff(signer, _) -> signer == #[3, 3, 255]