luau-syntax 0.732.0

Luau lexer, parser, AST, CST, and source utilities
Documentation
use super::{hex_value, is_digit, is_hex_digit, is_space, to_utf8, unescape};

pub(crate) fn fixup_string_bytes(source: &[u8]) -> Option<Vec<u8>> {
    if source.is_empty() || !source.contains(&b'\\') {
        return Some(source.to_vec());
    }

    let mut data = source.to_vec();
    let size = data.len();
    let mut write = 0usize;
    let mut i = 0usize;

    while i < size {
        if data[i] != b'\\' {
            data[write] = data[i];
            write += 1;
            i += 1;
            continue;
        }

        if i + 1 == size {
            return None;
        }

        let escape = data[i + 1];
        i += 2;

        match escape {
            b'\n' => {
                data[write] = b'\n';
                write += 1;
            }
            b'\r' => {
                data[write] = b'\n';
                write += 1;
                if i < size && data[i] == b'\n' {
                    i += 1;
                }
            }
            b'x' => {
                if i + 2 > size {
                    return None;
                }

                let mut code = 0u8;
                for _ in 0..2 {
                    let ch = data[i];
                    if !is_hex_digit(ch) {
                        return None;
                    }

                    code = code.wrapping_mul(16).wrapping_add(hex_value(ch)?);
                    i += 1;
                }

                data[write] = code;
                write += 1;
            }
            b'z' => {
                while i < size && is_space(data[i]) {
                    i += 1;
                }
            }
            b'u' => {
                if i + 3 > size || data[i] != b'{' {
                    return None;
                }
                i += 1;

                if i == size || data[i] == b'}' {
                    return None;
                }

                let mut code = 0u32;
                for _ in 0..16 {
                    if i == size {
                        return None;
                    }

                    let ch = data[i];
                    if ch == b'}' {
                        break;
                    }
                    if !is_hex_digit(ch) {
                        return None;
                    }

                    code = code
                        .checked_mul(16)?
                        .checked_add(u32::from(hex_value(ch)?))?;
                    i += 1;
                }

                if i == size || data[i] != b'}' {
                    return None;
                }
                i += 1;

                write += to_utf8(&mut data[write..], code)?;
            }
            byte if is_digit(byte) => {
                let mut code = u32::from(byte - b'0');

                for _ in 0..2 {
                    if i == size || !is_digit(data[i]) {
                        break;
                    }

                    code = code
                        .checked_mul(10)?
                        .checked_add(u32::from(data[i] - b'0'))?;
                    i += 1;
                }

                let code = u8::try_from(code).ok()?;
                data[write] = code;
                write += 1;
            }
            other => {
                data[write] = unescape(other);
                write += 1;
            }
        }
    }

    data.truncate(write);
    Some(data)
}

pub(crate) fn multiline_string_bytes(value: &[u8]) -> Vec<u8> {
    if value.is_empty() {
        return Vec::new();
    }

    let source = if value.starts_with(b"\r\n") {
        &value[2..]
    } else if value.starts_with(b"\n") {
        &value[1..]
    } else {
        value
    };

    let mut result = Vec::with_capacity(source.len());
    let mut index = 0usize;

    while index < source.len() {
        if source[index] == b'\r' && source.get(index + 1) == Some(&b'\n') {
            result.push(b'\n');
            index += 2;
        } else {
            result.push(source[index]);
            index += 1;
        }
    }

    result
}