Emit parse error when finding a ByteArray literal instead of String literal.
This commit is contained in:
parent
5132110d4b
commit
d72e13c7c8
|
@ -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(
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
Loading…
Reference in New Issue