Fix bytearray literals parsing and formatting.
Weirdly enough, we got the parsing wrong for byte literals in expressions (but did okay in constants). But got the formatting wrong in constants (yet did okay for formatting expressions). I've factored out the code in both cases to avoid the duplication that led to this in the first place. Plus added test coverage to make sure this doesn't happen in the future.
This commit is contained in:
parent
0e65fecf69
commit
a50b51e086
|
@ -318,16 +318,7 @@ impl<'comments> Formatter<'comments> {
|
|||
|
||||
fn const_expr<'a, A, B>(&mut self, value: &'a Constant<A, B>) -> Document<'a> {
|
||||
match value {
|
||||
Constant::ByteArray { bytes, .. } => "#"
|
||||
.to_doc()
|
||||
.append(
|
||||
flex_break("[", "[")
|
||||
.append(join(bytes.iter().map(|b| b.to_doc()), break_(",", ", ")))
|
||||
.nest(INDENT)
|
||||
.append(break_(",", ""))
|
||||
.append("]"),
|
||||
)
|
||||
.group(),
|
||||
Constant::ByteArray { bytes, .. } => self.bytearray(bytes),
|
||||
Constant::Int { value, .. } => value.to_doc(),
|
||||
|
||||
Constant::String { value, .. } => self.string(value),
|
||||
|
@ -647,20 +638,18 @@ impl<'comments> Formatter<'comments> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn bytearray<'a>(&mut self, bytes: &'a [u8]) -> Document<'a> {
|
||||
"#".to_doc()
|
||||
.append("\"")
|
||||
.append(Document::String(hex::encode(bytes)))
|
||||
.append("\"")
|
||||
}
|
||||
|
||||
pub fn expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
|
||||
let comments = self.pop_comments(expr.start_byte_index());
|
||||
|
||||
let document = match expr {
|
||||
UntypedExpr::ByteArray { bytes, .. } => "#"
|
||||
.to_doc()
|
||||
.append(
|
||||
flex_break("[", "[")
|
||||
.append(join(bytes.iter().map(|b| b.to_doc()), break_(",", ", ")))
|
||||
.nest(INDENT)
|
||||
.append(break_(",", ""))
|
||||
.append("]"),
|
||||
)
|
||||
.group(),
|
||||
UntypedExpr::ByteArray { bytes, .. } => self.bytearray(bytes),
|
||||
UntypedExpr::If {
|
||||
branches,
|
||||
final_else,
|
||||
|
|
|
@ -360,55 +360,12 @@ fn constant_value_parser() -> impl Parser<Token, ast::UntypedConstant, Error = P
|
|||
elements,
|
||||
});
|
||||
|
||||
let constant_bytearray_list_parser = just(Token::Hash)
|
||||
.ignore_then(
|
||||
select! {Token::Int {value} => value}
|
||||
.validate(|value, span, emit| {
|
||||
let byte: u8 = match value.parse() {
|
||||
Ok(b) => b,
|
||||
Err(_) => {
|
||||
emit(ParseError::expected_input_found(
|
||||
span,
|
||||
None,
|
||||
Some(error::Pattern::Byte),
|
||||
));
|
||||
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
byte
|
||||
})
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftSquare), just(Token::RightSquare)),
|
||||
)
|
||||
.map_with_span(|bytes, span| ast::UntypedConstant::ByteArray {
|
||||
let constant_bytearray_parser =
|
||||
bytearray_parser().map_with_span(|bytes, span| ast::UntypedConstant::ByteArray {
|
||||
location: span,
|
||||
bytes,
|
||||
});
|
||||
|
||||
let constant_bytearray_hexstring_parser =
|
||||
just(Token::Hash)
|
||||
.ignore_then(select! {Token::String {value} => value}.validate(
|
||||
|value, span, emit| match hex::decode(value) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(_) => {
|
||||
emit(ParseError::malformed_base16_string_literal(span));
|
||||
vec![]
|
||||
}
|
||||
},
|
||||
))
|
||||
.map_with_span(|bytes, span| ast::UntypedConstant::ByteArray {
|
||||
location: span,
|
||||
bytes,
|
||||
});
|
||||
|
||||
let constant_bytearray_parser = choice((
|
||||
constant_bytearray_list_parser,
|
||||
constant_bytearray_hexstring_parser,
|
||||
));
|
||||
|
||||
let constant_list_parser = r
|
||||
.clone()
|
||||
.separated_by(just(Token::Comma))
|
||||
|
@ -529,6 +486,44 @@ fn constant_value_parser() -> impl Parser<Token, ast::UntypedConstant, Error = P
|
|||
})
|
||||
}
|
||||
|
||||
pub fn bytearray_parser() -> impl Parser<Token, Vec<u8>, Error = ParseError> {
|
||||
let bytearray_list_parser = just(Token::Hash).ignore_then(
|
||||
select! {Token::Int {value} => value}
|
||||
.validate(|value, span, emit| {
|
||||
let byte: u8 = match value.parse() {
|
||||
Ok(b) => b,
|
||||
Err(_) => {
|
||||
emit(ParseError::expected_input_found(
|
||||
span,
|
||||
None,
|
||||
Some(error::Pattern::Byte),
|
||||
));
|
||||
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
byte
|
||||
})
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftSquare), just(Token::RightSquare)),
|
||||
);
|
||||
|
||||
let bytearray_hexstring_parser =
|
||||
just(Token::Hash).ignore_then(select! {Token::String {value} => value}.validate(
|
||||
|value, span, emit| match hex::decode(value) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(_) => {
|
||||
emit(ParseError::malformed_base16_string_literal(span));
|
||||
vec![]
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
choice((bytearray_list_parser, bytearray_hexstring_parser))
|
||||
}
|
||||
|
||||
pub fn fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
|
||||
choice((
|
||||
select! {Token::Name {name} => name}
|
||||
|
@ -913,30 +908,8 @@ pub fn expr_parser(
|
|||
elems,
|
||||
});
|
||||
|
||||
let bytearray = just(Token::Hash)
|
||||
.ignore_then(
|
||||
select! {Token::Int {value} => value}
|
||||
.validate(|value, span, emit| {
|
||||
let byte: u8 = match value.parse() {
|
||||
Ok(b) => b,
|
||||
Err(_) => {
|
||||
emit(ParseError::expected_input_found(
|
||||
span,
|
||||
None,
|
||||
Some(error::Pattern::Byte),
|
||||
));
|
||||
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
byte
|
||||
})
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftSquare), just(Token::RightSquare)),
|
||||
)
|
||||
.map_with_span(|bytes, span| expr::UntypedExpr::ByteArray {
|
||||
let bytearray =
|
||||
bytearray_parser().map_with_span(|bytes, span| expr::UntypedExpr::ByteArray {
|
||||
location: span,
|
||||
bytes,
|
||||
});
|
||||
|
|
|
@ -256,3 +256,19 @@ fn test_block_expr() {
|
|||
|
||||
assert_fmt(src, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_bytearray_literals() {
|
||||
let src = indoc! {r#"
|
||||
const foo = #"ff00"
|
||||
const bar = #[0, 255]
|
||||
"#};
|
||||
|
||||
let expected = indoc! { r#"
|
||||
const foo = #"ff00"
|
||||
|
||||
const bar = #"00ff"
|
||||
"#};
|
||||
|
||||
assert_fmt(src, expected);
|
||||
}
|
||||
|
|
|
@ -1652,22 +1652,50 @@ fn plain_bytearray_literals() {
|
|||
fn base16_bytearray_literals() {
|
||||
let code = indoc! {r#"
|
||||
pub const my_policy_id = #"00aaff"
|
||||
|
||||
pub fn foo() {
|
||||
my_policy_id == #"00aaff"
|
||||
}
|
||||
"#};
|
||||
|
||||
assert_definitions(
|
||||
code,
|
||||
vec![ast::UntypedDefinition::ModuleConstant(ModuleConstant {
|
||||
doc: None,
|
||||
location: Span::new((), 0..34),
|
||||
public: true,
|
||||
name: "my_policy_id".to_string(),
|
||||
annotation: None,
|
||||
value: Box::new(Constant::ByteArray {
|
||||
location: Span::new((), 25..34),
|
||||
bytes: vec![0, 170, 255],
|
||||
vec![
|
||||
ast::UntypedDefinition::ModuleConstant(ModuleConstant {
|
||||
doc: None,
|
||||
location: Span::new((), 0..34),
|
||||
public: true,
|
||||
name: "my_policy_id".to_string(),
|
||||
annotation: None,
|
||||
value: Box::new(Constant::ByteArray {
|
||||
location: Span::new((), 25..34),
|
||||
bytes: vec![0, 170, 255],
|
||||
}),
|
||||
tipo: (),
|
||||
}),
|
||||
tipo: (),
|
||||
})],
|
||||
ast::UntypedDefinition::Fn(Function {
|
||||
arguments: vec![],
|
||||
body: expr::UntypedExpr::BinOp {
|
||||
location: Span::new((), 55..80),
|
||||
name: ast::BinOp::Eq,
|
||||
left: Box::new(expr::UntypedExpr::Var {
|
||||
location: Span::new((), 55..67),
|
||||
name: "my_policy_id".to_string(),
|
||||
}),
|
||||
right: Box::new(expr::UntypedExpr::ByteArray {
|
||||
location: Span::new((), 71..80),
|
||||
bytes: vec![0, 170, 255],
|
||||
}),
|
||||
},
|
||||
doc: None,
|
||||
location: Span::new((), 36..48),
|
||||
name: "foo".to_string(),
|
||||
public: true,
|
||||
return_annotation: None,
|
||||
return_type: (),
|
||||
end_position: 81,
|
||||
}),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue