vyre-libs 0.6.2

vyre Category A library ecosystem - pure-IR compositions over vyre-ops hardware primitives
Documentation
use super::*;

impl PreprocessorExprParser<'_, '_, '_> {
    pub(super) fn consume_integer(&mut self) -> Option<u64> {
        self.skip_ws_and_splices();
        let start = self.index;
        let radix = if self.bytes.get(self.index..self.index + 2) == Some(b"0x")
            || self.bytes.get(self.index..self.index + 2) == Some(b"0X")
        {
            self.index += 2;
            16
        } else if self.bytes.get(self.index..self.index + 2) == Some(b"0b")
            || self.bytes.get(self.index..self.index + 2) == Some(b"0B")
        {
            self.index += 2;
            2
        } else if self.bytes.get(self.index).copied() == Some(b'0') {
            8
        } else {
            10
        };
        let digits_start = self.index;
        let mut value = 0u64;
        while let Some(byte) = self.bytes.get(self.index).copied() {
            if byte == b'\'' {
                let Some(next) = self.bytes.get(self.index + 1).copied() else {
                    break;
                };
                if digit_value_for_radix(next, radix).is_none() {
                    break;
                }
                self.index += 1;
                continue;
            }
            let digit = match byte {
                b'0'..=b'9' => u64::from(byte - b'0'),
                b'a'..=b'f' if radix == 16 => u64::from(byte - b'a' + 10),
                b'A'..=b'F' if radix == 16 => u64::from(byte - b'A' + 10),
                _ => break,
            };
            if digit >= radix {
                break;
            }
            value = value.saturating_mul(radix).saturating_add(digit);
            self.index += 1;
        }
        if self.index == digits_start {
            self.index = start;
            return None;
        }
        loop {
            if matches!(
                self.bytes.get(self.index),
                Some(b'u' | b'U' | b'l' | b'L' | b'z' | b'Z')
            ) {
                self.index += 1;
            } else if matches!(
                self.bytes.get(self.index..self.index + 2),
                Some(b"wb" | b"WB")
            ) {
                self.index += 2;
            } else {
                break;
            }
        }
        Some(value)
    }

    pub(super) fn consume_char_constant(&mut self) -> Result<Option<u64>, CPreprocessorError> {
        self.skip_ws_and_splices();
        let prefix_start = self.index;
        if self.bytes.get(self.index..self.index + 2) == Some(b"u8") {
            self.index += 2;
        } else if matches!(self.bytes.get(self.index), Some(b'L' | b'u' | b'U')) {
            self.index += 1;
        }
        if !self.consume_byte(b'\'') {
            self.index = prefix_start;
            return Ok(None);
        }

        let mut value = 0u64;
        let mut saw_character = false;
        loop {
            let Some(byte) = self.bytes.get(self.index).copied() else {
                return Err(self.error("Fix: terminate #if character constant"));
            };
            if byte == b'\'' {
                break;
            }
            if matches!(byte, b'\n' | b'\r') {
                return Err(self.error("Fix: close #if character constant before newline"));
            }
            let next_value = if self.consume_byte(b'\\') {
                self.consume_escape_value()?
            } else {
                self.index += 1;
                u64::from(byte)
            };
            value = value.wrapping_shl(8) | (next_value & 0xff);
            saw_character = true;
        }

        if !saw_character {
            return Err(
                self.error("Fix: #if character constant must contain at least one character")
            );
        }

        if !self.consume_byte(b'\'') {
            return Err(self.error("Fix: close #if character constant with single quote"));
        }
        Ok(Some(value))
    }

    pub(super) fn consume_escape_value(&mut self) -> Result<u64, CPreprocessorError> {
        let Some(byte) = self.bytes.get(self.index).copied() else {
            return Err(self.error("Fix: complete #if character escape"));
        };
        self.index += 1;
        let value = match byte {
            b'\'' => b'\'',
            b'"' => b'"',
            b'?' => b'?',
            b'\\' => b'\\',
            b'a' => 7,
            b'b' => 8,
            b'f' => 12,
            b'n' => b'\n',
            b'r' => b'\r',
            b't' => b'\t',
            b'v' => 11,
            b'0'..=b'7' => {
                let mut value = u64::from(byte - b'0');
                let mut digits = 1u8;
                while digits < 3 {
                    let Some(next @ b'0'..=b'7') = self.bytes.get(self.index).copied() else {
                        break;
                    };
                    value = value * 8 + u64::from(next - b'0');
                    self.index += 1;
                    digits += 1;
                }
                return Ok(value);
            }
            b'x' => return self.consume_hex_escape(),
            b'u' => return self.consume_fixed_hex_escape(4),
            b'U' => return self.consume_fixed_hex_escape(8),
            other => other,
        };
        Ok(u64::from(value))
    }

    pub(super) fn consume_fixed_hex_escape(
        &mut self,
        digits: usize,
    ) -> Result<u64, CPreprocessorError> {
        let mut value = 0u64;
        for _ in 0..digits {
            let Some(byte) = self.bytes.get(self.index).copied() else {
                return Err(self.error("Fix: universal character escape is truncated"));
            };
            let digit = match byte {
                b'0'..=b'9' => u64::from(byte - b'0'),
                b'a'..=b'f' => u64::from(byte - b'a' + 10),
                b'A'..=b'F' => u64::from(byte - b'A' + 10),
                _ => return Err(self.error("Fix: universal character escape needs hex digits")),
            };
            value = value.saturating_mul(16).saturating_add(digit);
            self.index += 1;
        }
        Ok(value)
    }

    pub(super) fn consume_hex_escape(&mut self) -> Result<u64, CPreprocessorError> {
        let start = self.index;
        let mut value = 0u64;
        while let Some(byte) = self.bytes.get(self.index).copied() {
            let digit = match byte {
                b'0'..=b'9' => u64::from(byte - b'0'),
                b'a'..=b'f' => u64::from(byte - b'a' + 10),
                b'A'..=b'F' => u64::from(byte - b'A' + 10),
                _ => break,
            };
            value = value.saturating_mul(16).saturating_add(digit);
            self.index += 1;
        }
        if self.index == start {
            return Err(self.error("Fix: hex character escape needs at least one digit"));
        }
        Ok(value)
    }
}

fn digit_value_for_radix(byte: u8, radix: u64) -> Option<u64> {
    let digit = match byte {
        b'0'..=b'9' => u64::from(byte - b'0'),
        b'a'..=b'f' if radix == 16 => u64::from(byte - b'a' + 10),
        b'A'..=b'F' if radix == 16 => u64::from(byte - b'A' + 10),
        _ => return None,
    };
    (digit < radix).then_some(digit)
}