diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 00a677d8..5fb377c3 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -1,8 +1,3 @@ -use itertools::Itertools; -use ordinal::Ordinal; -use std::sync::Arc; -use vec1::Vec1; - use crate::{ ast::{ Annotation, Arg, ArgName, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg, @@ -23,6 +18,11 @@ use crate::{ }, tipo::{self, Type}, }; +use itertools::Itertools; +use num_bigint::BigInt; +use ordinal::Ordinal; +use std::sync::Arc; +use vec1::Vec1; const INDENT: isize = 2; const DOCS_MAX_COLUMNS: isize = 80; @@ -665,7 +665,7 @@ impl<'comments> Formatter<'comments> { .append("\"") .append(Document::String(hex::encode(bytes))) .append("\""), - ByteArrayFormatPreference::ArrayOfBytes(base) => "#" + ByteArrayFormatPreference::ArrayOfBytes(Base::Decimal { .. }) => "#" .to_doc() .append( flex_break("[", "[") @@ -675,6 +675,25 @@ impl<'comments> Formatter<'comments> { .append("]"), ) .group(), + ByteArrayFormatPreference::ArrayOfBytes(Base::Hexadecimal) => "#" + .to_doc() + .append( + flex_break("[", "[") + .append(join( + bytes.iter().map(|b| { + Document::String(if *b < 16 { + format!("0x0{b:x}") + } else { + format!("{b:#x}") + }) + }), + break_(",", ", "), + )) + .nest(INDENT) + .append(break_(",", "")) + .append("]"), + ) + .group(), ByteArrayFormatPreference::Utf8String => nil() .append("\"") .append(Document::String(String::from_utf8(bytes.to_vec()).unwrap())) @@ -682,8 +701,37 @@ impl<'comments> Formatter<'comments> { } } - pub fn int<'a>(&mut self, i: &'a str, base: &Base) -> Document<'a> { - i.to_doc() + pub fn int<'a>(&mut self, s: &'a str, base: &Base) -> Document<'a> { + match base { + Base::Decimal { numeric_underscore } if *numeric_underscore => { + let s = s + .chars() + .rev() + .enumerate() + .flat_map(|(i, c)| { + if i != 0 && i % 3 == 0 { + Some('_') + } else { + None + } + .into_iter() + .chain(std::iter::once(c)) + }) + .collect::() + .chars() + .rev() + .collect::(); + + Document::String(s) + } + Base::Decimal { .. } => s.to_doc(), + Base::Hexadecimal => Document::String(format!( + "0x{}", + BigInt::parse_bytes(s.as_bytes(), 10) + .expect("Invalid parsed hexadecimal digits ?!") + .to_str_radix(16), + )), + } } pub fn expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> { diff --git a/crates/aiken-lang/src/parser/lexer.rs b/crates/aiken-lang/src/parser/lexer.rs index 9d31c301..110d0335 100644 --- a/crates/aiken-lang/src/parser/lexer.rs +++ b/crates/aiken-lang/src/parser/lexer.rs @@ -20,7 +20,7 @@ pub fn lexer() -> impl Parser, Error = ParseError> { .at_least(1) .at_most(3) .separated_by(just("_")) - .at_least(1) + .at_least(2) .flatten() .collect::() .map(|value| Token::Int { diff --git a/crates/aiken-lang/src/tests/format.rs b/crates/aiken-lang/src/tests/format.rs index 165ec0a4..8a0ca4d4 100644 --- a/crates/aiken-lang/src/tests/format.rs +++ b/crates/aiken-lang/src/tests/format.rs @@ -838,9 +838,10 @@ fn pipes_and_expressions() { fn hex_and_numeric_underscore() { let src = indoc! {r#" fn foo() { - let a = 1_000_000 - let b = 0xA4 - let c = #[ 0xFD, 0x12, 0x00, 0x1B ] + let a = 1_000_000 + 1_423 + 10393841 + let b = 0xa4 - 0xcd + let c = #[0xfd, 0x12, 0x00, 0x1b, 0x0a, 0x90] + let d = -100_000 } "#}; diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 4a17db7e..19479451 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -3592,56 +3592,77 @@ fn int_parsing_numeric_underscore() { fn foo() { let i = 1_234_567 let j = 1_000_000 - } + let k = -10_000 + } "#}; assert_definitions( code, vec![ast::Definition::Fn(Function { arguments: vec![], body: expr::UntypedExpr::Sequence { - location: Span::new((), 13..50), + location: Span::new((), 17..76), expressions: vec![ expr::UntypedExpr::Assignment { - location: Span::new((), 13..30), + location: Span::new((), 17..34), value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 21..30), + location: Span::new((), 25..34), value: "1234567".to_string(), base: Base::Decimal { numeric_underscore: true, }, }), pattern: ast::Pattern::Var { - location: Span::new((), 17..18), + location: Span::new((), 21..22), name: "i".to_string(), }, kind: ast::AssignmentKind::Let, annotation: None, }, expr::UntypedExpr::Assignment { - location: Span::new((), 33..50), + location: Span::new((), 39..56), value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 41..50), + location: Span::new((), 47..56), value: "1000000".to_string(), base: Base::Decimal { numeric_underscore: true, }, }), pattern: ast::Pattern::Var { - location: Span::new((), 37..38), + location: Span::new((), 43..44), name: "j".to_string(), }, kind: ast::AssignmentKind::Let, annotation: None, }, + expr::UntypedExpr::Assignment { + location: Span::new((), 61..76), + value: Box::new(expr::UntypedExpr::UnOp { + op: ast::UnOp::Negate, + location: Span::new((), 69..76), + value: Box::new(expr::UntypedExpr::Int { + location: Span::new((), 70..76), + value: "10000".to_string(), + base: Base::Decimal { + numeric_underscore: true, + }, + }), + }), + pattern: ast::Pattern::Var { + location: Span::new((), 65..66), + name: "k".to_string(), + }, + kind: ast::AssignmentKind::Let, + annotation: None, + }, ], }, doc: None, - location: Span::new((), 0..8), + location: Span::new((), 2..10), name: "foo".to_string(), public: false, return_annotation: None, return_type: (), - end_position: 51, + end_position: 77, can_error: true, })], )