diff --git a/hblang/README.md b/hblang/README.md index 15275233..7ea58855 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -544,3 +544,18 @@ sqrt := fn(x: int): int { return g } ``` + +#### hex_octal_binary_literals +```hb +main := fn(): int { + hex := 0xFF + decimal := 255 + octal := 0o377 + binary := 0b11111111 + + if hex == decimal & octal == decimal & binary == decimal { + return 0 + } + return 1 +} +``` \ No newline at end of file diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 1eef2087..6b06785b 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -3340,5 +3340,6 @@ mod tests { struct_return_from_module_function => README; //comptime_pointers => README; sort_something_viredly => README; + hex_octal_binary_literals => README; } } diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index 6c7e671f..76a3345b 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -290,6 +290,24 @@ impl<'a> Lexer<'a> { let kind = match c { ..=b' ' => continue, + b'0' if self.advance_if(b'x') => { + while let Some(b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f') = self.peek() { + self.advance(); + } + T::Number + } + b'0' if self.advance_if(b'b') => { + while let Some(b'0' | b'1') = self.peek() { + self.advance(); + } + T::Number + } + b'0' if self.advance_if(b'o') => { + while let Some(b'0'..=b'7') = self.peek() { + self.advance(); + } + T::Number + } b'0'..=b'9' => { while let Some(b'0'..=b'9') = self.peek() { self.advance(); diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index 56110790..83550840 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -389,13 +389,23 @@ impl<'a, 'b> Parser<'a, 'b> { pos: token.start, stmts: self.collect_list(T::Semi, T::RBrace, Self::expr), }, - T::Number => E::Number { - pos: token.start, - value: match self.lexer.slice(token.range()).parse() { - Ok(value) => value, - Err(e) => self.report(format_args!("invalid number: {e}")), - }, - }, + T::Number => { + let slice = self.lexer.slice(token.range()); + let (slice, radix) = match &slice.get(0..2) { + Some("0x") => (slice.trim_start_matches("0x"), Radix::Hex), + Some("0b") => (slice.trim_start_matches("0b"), Radix::Binary), + Some("0o") => (slice.trim_start_matches("0o"), Radix::Octal), + _ => (slice, Radix::Decimal), + }; + E::Number { + pos: token.start, + value: match u64::from_str_radix(slice, radix as u32) { + Ok(value) => value, + Err(e) => self.report(format_args!("invalid number: {e}")), + }, + radix, + } + } T::LParen => { let expr = self.expr(); self.expect_advance(T::RParen); @@ -620,6 +630,15 @@ macro_rules! generate_expr { (@last ($($last:tt)*),) => { $($last)* }; } +#[repr(u32)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Radix { + Hex = 16, + Octal = 8, + Binary = 2, + Decimal = 10, +} + // it would be real nice if we could use relative pointers and still pattern match easily generate_expr! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -671,6 +690,7 @@ generate_expr! { Number { pos: Pos, value: u64, + radix: Radix, }, BinOp { left: &'a Self, @@ -992,7 +1012,12 @@ impl<'a> std::fmt::Display for Expr<'a> { write!(f, "}}")?; res } - Self::Number { value, .. } => write!(f, "{value}"), + Self::Number { value, radix, .. } => match radix { + Radix::Decimal => write!(f, "{value}"), + Radix::Hex => write!(f, "{value:#X}"), + Radix::Octal => write!(f, "{value:#o}"), + Radix::Binary => write!(f, "{value:#b}"), + }, Self::Bool { value, .. } => write!(f, "{value}"), Self::BinOp { left, diff --git a/hblang/tests/codegen_tests_hex_octal_binary_literals.txt b/hblang/tests/codegen_tests_hex_octal_binary_literals.txt new file mode 100644 index 00000000..e3aee3a7 --- /dev/null +++ b/hblang/tests/codegen_tests_hex_octal_binary_literals.txt @@ -0,0 +1,3 @@ +code size: 210 +ret: 0 +status: Ok(())