diff --git a/src/syntax/scanner/number.rs b/src/syntax/scanner/number.rs index 716049e..6b77cae 100644 --- a/src/syntax/scanner/number.rs +++ b/src/syntax/scanner/number.rs @@ -1,22 +1,63 @@ use crate::syntax::{token::{Token, self}, utils}; pub fn scan(chars: &Vec, start_pos: usize) -> (Token, usize) { + let next_char = chars.get(start_pos); + + // Try to scan a HEX value + if let Some(c) = next_char { + if *c == '0' { + // May be an 'x' or 'X' + let next_next_char = chars.get(start_pos + 1); + if let Some(c2) = next_next_char { + if *c2 == 'x' || *c2 == 'X' { + return scan_hex(chars, start_pos + 2, String::from("0x")); + } + } + } + } + + scan_decimal(chars, start_pos, String::from("")) } +/// Scans an integer. fn scan_decimal(chars: &Vec, start_pos: usize, current: String) -> (Token, usize) { let next_char = chars.get(start_pos); + // If a char is found if let Some(c) = next_char { - if utils::is_digit(*c) { - let new_value = format!("{}{}", current, *c); - scan_decimal(chars, start_pos + 1, new_value) - } else { - (token::new_number(current, start_pos as i32), start_pos) + let c = *c; + + // Scan a decimal number + if utils::is_digit(c) { + let new_value = format!("{}{}", current, c); + return scan_decimal(chars, start_pos + 1, new_value) } - } else { - (token::new_number(current, start_pos as i32), start_pos) + } + + // Return the current value + (token::new_number(current, start_pos as i32), start_pos) +} + +/// Scans a hex number. If successful, always returns '0x...', never '0X...' +/// +/// `current == ""` +/// +/// `start_pos` indicates the start of the hex value +fn scan_hex(chars: &Vec, start_pos: usize, current: String) -> (Token, usize) { + let next_char = chars.get(start_pos); + + if let Some(c) = next_char { + let c = *c; + + if utils::is_hex_digit(c) { + return scan_hex(chars, start_pos + 1, utils::str_append(current, c)) + } + } + + // Return current value + (token::new_number(current, start_pos as i32), start_pos) } @@ -59,4 +100,36 @@ mod tests { assert_eq!(TokenType::Number, token.token_type); assert_eq!("123456", token.value); } + + #[test] + fn test_hex() { + let input = str_to_vec("0x20"); + let start_pos = 0; + + let (token, next) = scan(&input, start_pos); + assert_eq!(4, next); + assert_eq!(TokenType::Number, token.token_type); + assert_eq!("0x20", token.value); + + + let input = str_to_vec(" 0Xff23DA"); + let start_pos = 4; + + let (token, next) = scan(&input, start_pos); + assert_eq!(12, next); + assert_eq!(TokenType::Number, token.token_type); + assert_eq!("0xff23DA", token.value); + } + + // Should not scan an incomplete hex value + #[test] + fn test_hex_2() { + let input = str_to_vec("0x20"); + let start_pos = 0; + + let (token, next) = scan(&input, start_pos); + assert_eq!(4, next); + assert_eq!(TokenType::Number, token.token_type); + assert_eq!("0x20", token.value); + } } diff --git a/src/syntax/utils.rs b/src/syntax/utils.rs index 9f1b3fd..c8e5abb 100644 --- a/src/syntax/utils.rs +++ b/src/syntax/utils.rs @@ -3,4 +3,10 @@ pub fn is_digit(c: char) -> bool { '0' <= c && c <= '9' } +pub fn is_hex_digit(c: char) -> bool { + is_digit(c) || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' +} +pub fn str_append(current: String, c: char) -> String { + format!("{}{}", current, c) +}