From 76add65235c734cd7151770de641302b70e6738d Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Thu, 1 Jan 2026 03:12:25 -0700 Subject: [PATCH] Add support for binary, hex, and octal literals --- rust_compiler/libs/compiler/src/v1.rs | 58 +++++++++++++++++++++-- rust_compiler/libs/tokenizer/src/token.rs | 32 ++++++++++++- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index 6c5dd43..d153a69 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -2162,14 +2162,36 @@ impl<'a> Compiler<'a> { scope: &mut VariableScope<'a, '_>, ) -> Result, Error<'a>> { fn fold_binary_expression<'a>(expr: &BinaryExpression<'a>) -> Option { + fn number_to_i64(n: Number) -> Option { + match n { + Number::Integer(i, _) => i64::try_from(i).ok(), + Number::Decimal(d, _) => { + // Convert decimal to i64 by truncating + let int_part = d.trunc(); + i64::try_from(int_part.mantissa() / 10_i128.pow(int_part.scale())).ok() + } + } + } + + fn i64_to_number(i: i64) -> Number { + Number::Integer(i as i128, Unit::None) + } + let (lhs, rhs) = match &expr { BinaryExpression::Add(l, r) | BinaryExpression::Subtract(l, r) | BinaryExpression::Multiply(l, r) | BinaryExpression::Divide(l, r) | BinaryExpression::Exponent(l, r) - | BinaryExpression::Modulo(l, r) => (fold_expression(l)?, fold_expression(r)?), - _ => return None, + | BinaryExpression::Modulo(l, r) + | BinaryExpression::BitwiseAnd(l, r) + | BinaryExpression::BitwiseOr(l, r) + | BinaryExpression::BitwiseXor(l, r) + | BinaryExpression::LeftShift(l, r) + | BinaryExpression::RightShiftArithmetic(l, r) + | BinaryExpression::RightShiftLogical(l, r) => { + (fold_expression(l)?, fold_expression(r)?) + } }; match expr { @@ -2178,7 +2200,37 @@ impl<'a> Compiler<'a> { BinaryExpression::Multiply(..) => Some(lhs * rhs), BinaryExpression::Divide(..) => Some(lhs / rhs), // Watch out for div by zero panics! BinaryExpression::Modulo(..) => Some(lhs % rhs), - _ => None, // Handle Exponent separately or implement pow + BinaryExpression::BitwiseAnd(..) => { + let lhs_int = number_to_i64(lhs)?; + let rhs_int = number_to_i64(rhs)?; + Some(i64_to_number(lhs_int & rhs_int)) + } + BinaryExpression::BitwiseOr(..) => { + let lhs_int = number_to_i64(lhs)?; + let rhs_int = number_to_i64(rhs)?; + Some(i64_to_number(lhs_int | rhs_int)) + } + BinaryExpression::BitwiseXor(..) => { + let lhs_int = number_to_i64(lhs)?; + let rhs_int = number_to_i64(rhs)?; + Some(i64_to_number(lhs_int ^ rhs_int)) + } + BinaryExpression::LeftShift(..) => { + let lhs_int = number_to_i64(lhs)?; + let rhs_int = number_to_i64(rhs)?; + Some(i64_to_number(lhs_int << rhs_int)) + } + BinaryExpression::RightShiftArithmetic(..) => { + let lhs_int = number_to_i64(lhs)?; + let rhs_int = number_to_i64(rhs)?; + Some(i64_to_number(lhs_int >> rhs_int)) + } + BinaryExpression::RightShiftLogical(..) => { + let lhs_int = number_to_i64(lhs)?; + let rhs_int = number_to_i64(rhs)?; + Some(i64_to_number(lhs_int >> rhs_int)) + } + _ => None, // Exponent not handled in compile-time folding } } diff --git a/rust_compiler/libs/tokenizer/src/token.rs b/rust_compiler/libs/tokenizer/src/token.rs index 2290b97..0e4b273 100644 --- a/rust_compiler/libs/tokenizer/src/token.rs +++ b/rust_compiler/libs/tokenizer/src/token.rs @@ -135,6 +135,9 @@ pub enum TokenType<'a> { /// Represents a string token String(Cow<'a, str>), + #[regex(r"0[xX][0-9a-fA-F][0-9a-fA-F_]*([cfk])?", parse_number)] + #[regex(r"0[oO][0-7][0-7_]*([cfk])?", parse_number)] + #[regex(r"0[bB][01][01_]*([cfk])?", parse_number)] #[regex(r"[0-9][0-9_]*(\.[0-9][0-9_]*)?([cfk])?", parse_number)] /// Represents a number token Number(Number), @@ -254,7 +257,33 @@ fn parse_number<'a>(lexer: &mut Lexer<'a, TokenType<'a>>) -> Result Unit::None, }; - if clean_str.contains('.') { + // Determine the base and parse accordingly + if clean_str.starts_with("0x") || clean_str.starts_with("0X") { + // Hexadecimal + let hex_part = &clean_str[2..]; + Ok(Number::Integer( + i128::from_str_radix(hex_part, 16) + .map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?, + unit, + )) + } else if clean_str.starts_with("0o") || clean_str.starts_with("0O") { + // Octal + let octal_part = &clean_str[2..]; + Ok(Number::Integer( + i128::from_str_radix(octal_part, 8) + .map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?, + unit, + )) + } else if clean_str.starts_with("0b") || clean_str.starts_with("0B") { + // Binary + let binary_part = &clean_str[2..]; + Ok(Number::Integer( + i128::from_str_radix(binary_part, 2) + .map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?, + unit, + )) + } else if clean_str.contains('.') { + // Decimal floating point Ok(Number::Decimal( clean_str .parse::() @@ -262,6 +291,7 @@ fn parse_number<'a>(lexer: &mut Lexer<'a, TokenType<'a>>) -> Result()