diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 2fc87ab4..1792b087 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -318,16 +318,7 @@ impl<'comments> Formatter<'comments> { fn const_expr<'a, A, B>(&mut self, value: &'a Constant) -> 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, diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 201e61a7..c63a7a3f 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -360,55 +360,12 @@ fn constant_value_parser() -> impl Parser 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 impl Parser, 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 { 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, }); diff --git a/crates/aiken-lang/src/tests/format.rs b/crates/aiken-lang/src/tests/format.rs index 4290cf0c..5746b747 100644 --- a/crates/aiken-lang/src/tests/format.rs +++ b/crates/aiken-lang/src/tests/format.rs @@ -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); +} diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 6caa7e3f..845c9393 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -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, + }), + ], ) }