diff --git a/crates/aiken-lang/src/parser/error.rs b/crates/aiken-lang/src/parser/error.rs index 0212e042..2877c7ba 100644 --- a/crates/aiken-lang/src/parser/error.rs +++ b/crates/aiken-lang/src/parser/error.rs @@ -9,6 +9,30 @@ use std::collections::HashSet; #[derive(Debug, Clone, Diagnostic, thiserror::Error)] #[error("{kind}\n")] +#[diagnostic( + help( + "{}", + match kind { + ErrorKind::Unexpected(..) if !expected.is_empty() => { + format!( + "I am looking for one of the following patterns:\n{}", + expected + .iter() + .map(|x| format!( + "→ {}", + x.to_aiken() + .if_supports_color(Stdout, |s| s.purple()) + )) + .collect::>() + .join("\n") + ) + }, + _ => { + kind.help().map(|x| x.to_string()).unwrap_or_default() + } + } + ) +)] pub struct ParseError { pub kind: ErrorKind, #[label] @@ -28,6 +52,16 @@ impl ParseError { self } + pub fn expected_but_got(expected: Pattern, got: Pattern, span: Span) -> Self { + Self { + kind: ErrorKind::Unexpected(got), + expected: HashSet::from_iter([expected]), + span, + while_parsing: None, + label: None, + } + } + pub fn invalid_assignment_right_hand_side(span: Span) -> Self { Self { kind: ErrorKind::UnfinishedAssignmentRightHandSide, @@ -163,7 +197,7 @@ pub enum ErrorKind { UnexpectedEnd, #[error("{0}")] - #[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))] + #[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new("")))) ] Unexpected(Pattern), #[error("I discovered an invalid tuple index.")] @@ -221,7 +255,9 @@ pub enum ErrorKind { HybridNotationInByteArray, #[error("I failed to understand a when clause guard.")] - #[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#checking-equality-and-ordering-in-patterns"))] + #[diagnostic(url( + "https://aiken-lang.org/language-tour/control-flow#checking-equality-and-ordering-in-patterns" + ))] #[diagnostic(help("{}", formatdoc! { r#"Clause guards are not as capable as standard expressions. While you can combine multiple clauses using '{operator_or}' and '{operator_and}', you can't do any arithmetic in there. They are mainly meant to compare pattern variables to some known constants using simple binary operators. @@ -278,15 +314,6 @@ pub enum Pattern { #[error("I found an unexpected token '{0}'.")] #[diagnostic(help("Try removing it!"))] Token(Token), - #[error("I found an unexpected literal value.")] - #[diagnostic(help("Try removing it!"))] - Literal, - #[error("I found an unexpected type name.")] - #[diagnostic(help("Try removing it!"))] - TypeIdent, - #[error("I found an unexpected identifier.")] - #[diagnostic(help("Try removing it!"))] - TermIdent, #[error("I found an unexpected end of input.")] End, #[error("I found a malformed list spread pattern.")] @@ -295,11 +322,6 @@ pub enum Pattern { #[error("I found an out-of-bound byte literal.")] #[diagnostic(help("Bytes must be between 0-255."))] Byte, - #[error("I found an unexpected pattern.")] - #[diagnostic(help( - "If no label is provided then only variables\nmatching a field name are allowed." - ))] - RecordPunning, #[error("I found an unexpected label.")] #[diagnostic(help("You can only use labels surrounded by curly braces"))] Label, @@ -308,11 +330,27 @@ pub enum Pattern { Discard, } +impl Pattern { + fn to_aiken(&self) -> String { + use Pattern::*; + match self { + Token(tok) => tok.to_string(), + Char(c) => c.to_string(), + End => "".to_string(), + Match => "A pattern (a discard, a var, etc...)".to_string(), + Byte => "A byte between [0; 255]".to_string(), + Label => "A label".to_string(), + Discard => "_".to_string(), + } + } +} + impl From for Pattern { fn from(c: char) -> Self { Self::Char(c) } } + impl From for Pattern { fn from(tok: Token) -> Self { Self::Token(tok) diff --git a/crates/aiken-lang/src/parser/expr/fail_todo_trace.rs b/crates/aiken-lang/src/parser/expr/fail_todo_trace.rs index be7c7d34..2cde45c8 100644 --- a/crates/aiken-lang/src/parser/expr/fail_todo_trace.rs +++ b/crates/aiken-lang/src/parser/expr/fail_todo_trace.rs @@ -2,7 +2,7 @@ use crate::{ ast::TraceKind, expr::UntypedExpr, parser::{ - error::ParseError, + error::{ParseError, Pattern}, expr::{string, when::clause}, token::Token, }, diff --git a/crates/aiken-lang/src/parser/token.rs b/crates/aiken-lang/src/parser/token.rs index 8f1fdbfa..78c754ae 100644 --- a/crates/aiken-lang/src/parser/token.rs +++ b/crates/aiken-lang/src/parser/token.rs @@ -183,6 +183,6 @@ impl fmt::Display for Token { Token::Validator => "validator", Token::Via => "via", }; - write!(f, "\"{s}\"") + write!(f, "{s}") } } diff --git a/crates/aiken-project/src/error.rs b/crates/aiken-project/src/error.rs index b262ed11..b5398018 100644 --- a/crates/aiken-project/src/error.rs +++ b/crates/aiken-project/src/error.rs @@ -324,7 +324,7 @@ impl Diagnostic for Error { "Try moving the shared code to a separate module that the others can depend on\n- {}", modules.join("\n- ") ))), - Error::Parse { error, .. } => error.kind.help(), + Error::Parse { error, .. } => error.help(), Error::Type { error, .. } => error.help(), Error::StandardIo(_) => None, Error::MissingManifest { .. } => Some(Box::new(