Emit parse error when finding a ByteArray literal instead of String literal.

This commit is contained in:
KtorZ 2023-02-18 10:55:39 +01:00
parent 5132110d4b
commit d72e13c7c8
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
2 changed files with 101 additions and 0 deletions

View File

@ -533,12 +533,70 @@ pub fn anon_fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = Par
}) })
} }
fn unexpected_bytearray_literal(
token: Token,
expr: &expr::UntypedExpr,
) -> Option<(ParseError, String)> {
match expr {
expr::UntypedExpr::ByteArray {
bytes,
preferred_format: ByteArrayFormatPreference::Utf8String,
location,
..
} => {
let literal = String::from_utf8(bytes.clone()).unwrap();
Some((
ParseError::unexpected_bytearray_literal(*location, token, literal.clone()),
literal,
))
}
_ => None,
}
}
fn validate_error_todo(
token: Token,
reason: Option<expr::UntypedExpr>,
emit: &mut dyn FnMut(ParseError),
) -> Option<expr::UntypedExpr> {
if let Some(reason) = reason {
match unexpected_bytearray_literal(token, &reason) {
None => Some(reason),
Some((err, value)) => {
emit(err);
Some(expr::UntypedExpr::String {
location: reason.location(),
value,
})
}
}
} else {
reason
}
}
pub fn expr_seq_parser() -> impl Parser<Token, expr::UntypedExpr, Error = ParseError> { pub fn expr_seq_parser() -> impl Parser<Token, expr::UntypedExpr, Error = ParseError> {
recursive(|r| { recursive(|r| {
choice(( choice((
just(Token::Trace) just(Token::Trace)
.ignore_then(expr_parser(r.clone())) .ignore_then(expr_parser(r.clone()))
.then(r.clone()) .then(r.clone())
.validate(|(text, then), _span, emit| {
match unexpected_bytearray_literal(Token::Trace, &text) {
None => (text, then),
Some((err, value)) => {
emit(err);
(
expr::UntypedExpr::String {
location: text.location(),
value,
},
then,
)
}
}
})
.map_with_span(|(text, then_), span| expr::UntypedExpr::Trace { .map_with_span(|(text, then_), span| expr::UntypedExpr::Trace {
kind: TraceKind::Trace, kind: TraceKind::Trace,
location: span, location: span,
@ -547,9 +605,11 @@ pub fn expr_seq_parser() -> impl Parser<Token, expr::UntypedExpr, Error = ParseE
}), }),
just(Token::ErrorTerm) just(Token::ErrorTerm)
.ignore_then(expr_parser(r.clone()).or_not()) .ignore_then(expr_parser(r.clone()).or_not())
.validate(|reason, _span, emit| validate_error_todo(Token::ErrorTerm, reason, emit))
.map_with_span(|reason, span| expr::UntypedExpr::error(span, reason)), .map_with_span(|reason, span| expr::UntypedExpr::error(span, reason)),
just(Token::Todo) just(Token::Todo)
.ignore_then(expr_parser(r.clone()).or_not()) .ignore_then(expr_parser(r.clone()).or_not())
.validate(|reason, _span, emit| validate_error_todo(Token::Todo, reason, emit))
.map_with_span(|reason, span| expr::UntypedExpr::todo(span, reason)), .map_with_span(|reason, span| expr::UntypedExpr::todo(span, reason)),
expr_parser(r.clone()) expr_parser(r.clone())
.then(r.repeated()) .then(r.repeated())
@ -924,6 +984,7 @@ pub fn expr_parser(
.then_ignore(one_of(Token::RArrow).not().rewind()) .then_ignore(one_of(Token::RArrow).not().rewind())
.or_not(), .or_not(),
) )
.validate(|reason, _span, emit| validate_error_todo(Token::Todo, reason, emit))
.map_with_span(|reason, span| expr::UntypedExpr::todo(span, reason)), .map_with_span(|reason, span| expr::UntypedExpr::todo(span, reason)),
just(Token::ErrorTerm) just(Token::ErrorTerm)
.ignore_then( .ignore_then(
@ -931,6 +992,9 @@ pub fn expr_parser(
.then_ignore(just(Token::RArrow).not().rewind()) .then_ignore(just(Token::RArrow).not().rewind())
.or_not(), .or_not(),
) )
.validate(|reason, _span, emit| {
validate_error_todo(Token::ErrorTerm, reason, emit)
})
.map_with_span(|reason, span| expr::UntypedExpr::error(span, reason)), .map_with_span(|reason, span| expr::UntypedExpr::error(span, reason)),
))) )))
.map_with_span( .map_with_span(

View File

@ -55,6 +55,16 @@ impl ParseError {
label: None, label: None,
} }
} }
pub fn unexpected_bytearray_literal(span: Span, keyword: Token, literal: String) -> Self {
Self {
kind: ErrorKind::UnexpectedByteArrayLiteral(keyword, literal),
span,
while_parsing: None,
expected: HashSet::new(),
label: Some("use @"),
}
}
} }
impl PartialEq for ParseError { impl PartialEq for ParseError {
@ -102,15 +112,18 @@ impl<T: Into<Pattern>> chumsky::Error<T> for ParseError {
pub enum ErrorKind { pub enum ErrorKind {
#[error("I arrived at the end of the file unexpectedly.")] #[error("I arrived at the end of the file unexpectedly.")]
UnexpectedEnd, UnexpectedEnd,
#[error("{0}")] #[error("{0}")]
#[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))] #[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))]
Unexpected(Pattern), Unexpected(Pattern),
#[error("I discovered an invalid tuple index.")] #[error("I discovered an invalid tuple index.")]
#[diagnostic()] #[diagnostic()]
InvalidTupleIndex { InvalidTupleIndex {
#[help] #[help]
hint: Option<String>, hint: Option<String>,
}, },
#[error("I tripped over a malformed base16-encoded string literal.")] #[error("I tripped over a malformed base16-encoded string literal.")]
#[diagnostic(help("{}", formatdoc! { #[diagnostic(help("{}", formatdoc! {
r#"You can declare literal bytearrays from base16-encoded (a.k.a. hexadecimal) string literals. r#"You can declare literal bytearrays from base16-encoded (a.k.a. hexadecimal) string literals.
@ -123,6 +136,7 @@ pub enum ErrorKind {
"# "#
, "pub const".bright_blue(), "=".yellow(), "\"f4c9f9c4252d86702c2f4c2e49e6648c7cffe3c8f2b6b7d779788f50\"".bright_purple()}))] , "pub const".bright_blue(), "=".yellow(), "\"f4c9f9c4252d86702c2f4c2e49e6648c7cffe3c8f2b6b7d779788f50\"".bright_purple()}))]
MalformedBase16StringLiteral, MalformedBase16StringLiteral,
#[error("I failed to understand a when clause guard.")] #[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! { #[diagnostic(help("{}", formatdoc! {
@ -145,6 +159,29 @@ pub enum ErrorKind {
, bad = "✖️".red() , bad = "✖️".red()
}))] }))]
InvalidWhenClause, InvalidWhenClause,
#[error(
"I noticed a {} literal where I would expect a {}.",
"ByteArray".bold().bright_blue(),
"String".bold().bright_blue(),
)]
#[diagnostic(url("https://aiken-lang.org/language-tour/primitive-types#string"))]
#[diagnostic(help("{}", formatdoc! {
r#"The keyword {keyword} accepts a {type_String} as argument. A {type_String} literal is denoted by {syntax}. Simple double-quotes are interpreted as {type_ByteArray} of UTF-8 encoded bytes. Juggling between {type_ByteArray} and {type_String} can be a little confusing.
In this instance, you probably meant to write the following:
{keyword} {symbol_at}{symbol_doublequotes}{literal}{symbol_doublequotes}
"#,
type_String = "String".bold().bright_blue(),
type_ByteArray = "ByteArray".bold().bright_blue(),
symbol_at = "@".purple(),
symbol_doublequotes = "\"".purple(),
syntax = format!("{}{}", "@".purple(), "\"...\"".purple()),
keyword = format!("{}", .0).replace('"', "").bold(),
literal = .1.purple(),
}))]
UnexpectedByteArrayLiteral(Token, String),
} }
#[derive(Debug, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)] #[derive(Debug, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)]