ripex 0.3.0

Multi-language structural parsing and fact extraction.
Documentation
use crate::js::lexer::scanner::Scanner;
use crate::js::lexer::token::TokenKind;

pub fn scan_string(scanner: &mut Scanner, quote: char) -> String {
    let mut result = String::new();
    loop {
        match scanner.advance() {
            None | Some('\n') | Some('\r') => {
                return result;
            }
            Some(ch) if ch == quote => {
                return result;
            }
            Some('\\') => {
                if let Some(c) = scan_escape(scanner) {
                    result.push(c);
                }
            }
            Some(ch) => {
                result.push(ch);
            }
        }
    }
}

pub fn scan_template(scanner: &mut Scanner) -> (String, TokenKind) {
    let mut result = String::new();
    loop {
        match scanner.peek() {
            None => {
                return (result, TokenKind::TemplateTail);
            }
            Some('`') => {
                scanner.advance();
                return (result, TokenKind::TemplateTail);
            }
            Some('$') if scanner.peek_ahead(1) == Some('{') => {
                scanner.advance();
                scanner.advance();
                return (result, TokenKind::TemplateHead);
            }
            Some('\\') => {
                scanner.advance();
                if let Some(c) = scan_escape(scanner) {
                    result.push(c);
                }
            }
            Some(ch) => {
                scanner.advance();
                result.push(ch);
            }
        }
    }
}

fn scan_escape(scanner: &mut Scanner) -> Option<char> {
    Some(match scanner.advance()? {
        'n' => '\n',
        't' => '\t',
        'r' => '\r',
        'b' => '\u{0008}',
        'f' => '\u{000C}',
        'v' => '\u{000B}',
        '\\' => '\\',
        '\'' => '\'',
        '"' => '"',
        '`' => '`',
        '0' => {
            if matches!(scanner.peek(), Some('0'..='7')) {
                let mut code = 0u32;
                for _ in 0..3 {
                    match scanner.peek() {
                        Some(d @ '0'..='7') => {
                            scanner.advance();
                            code = code * 8 + (d as u32 - '0' as u32);
                        }
                        _ => break,
                    }
                }
                char::from_u32(code).unwrap_or('\u{FFFD}')
            } else {
                '\0'
            }
        }
        'x' => {
            let hi = scanner.advance().and_then(hex_val);
            let lo = scanner.advance().and_then(hex_val);
            match (hi, lo) {
                (Some(h), Some(l)) => ((h << 4) | l) as char,
                _ => '\u{FFFD}',
            }
        }
        'u' => {
            if scanner.peek() == Some('{') {
                scanner.advance();
                let mut code = 0u32;
                loop {
                    match scanner.peek() {
                        Some('}') => {
                            scanner.advance();
                            break;
                        }
                        Some(d) if d.is_ascii_hexdigit() => {
                            scanner.advance();
                            code = code * 16 + d.to_digit(16).unwrap();
                        }
                        _ => break,
                    }
                }
                char::from_u32(code).unwrap_or('\u{FFFD}')
            } else {
                let hi = scanner.advance().and_then(hex_val);
                let lo = scanner.advance().and_then(hex_val);
                match (hi, lo) {
                    (Some(h), Some(l)) => ((h << 4) | l) as char,
                    _ => '\u{FFFD}',
                }
            }
        }
        '\r' => {
            if scanner.peek() == Some('\n') {
                scanner.advance();
            }
            return None;
        }
        '\n' => {
            return None;
        }
        ch => ch,
    })
}

fn hex_val(c: char) -> Option<u8> {
    match c {
        '0'..='9' => Some(c as u8 - b'0'),
        'a'..='f' => Some(c as u8 - b'a' + 10),
        'A'..='F' => Some(c as u8 - b'A' + 10),
        _ => None,
    }
}