tectonic_engine_bibtex 0.2.0

The `bibtex` program as a reusable crate.
Documentation
use crate::c_api::{
    buffer::{with_buffers, with_buffers_mut, BufTy},
    char_info::{IdClass, LexClass},
    ASCIICode, BufPointer,
};

/// cbindgen:rename-all=ScreamingSnakeCase
#[repr(C)]
pub enum ScanRes {
    IdNull = 0,
    SpecifiedCharAdjacent = 1,
    OtherCharAdjacent = 2,
    WhitespaceAdjacent = 3,
}

pub struct Scan<'a> {
    chars: &'a [ASCIICode],
    not_class: Option<LexClass>,
    class: Option<LexClass>,
}

impl<'a> Scan<'a> {
    fn new(chars: &'a [ASCIICode]) -> Scan<'a> {
        Scan {
            chars,
            not_class: None,
            class: None,
        }
    }

    fn not_class(mut self, class: LexClass) -> Self {
        self.not_class = Some(class);
        self
    }

    fn class(mut self, class: LexClass) -> Self {
        self.class = Some(class);
        self
    }

    fn match_char(&self, char: ASCIICode) -> bool {
        self.not_class
            .map_or(false, |class| LexClass::of(char) != class)
            || self
                .class
                .map_or(false, |class| LexClass::of(char) == class)
            || self.chars.contains(&char)
    }

    fn scan_till(&self, last: BufPointer) -> bool {
        with_buffers_mut(|buffers| {
            buffers.set_offset(BufTy::Base, 1, buffers.offset(BufTy::Base, 2));

            let mut idx = buffers.offset(BufTy::Base, 2);
            while idx < last && !self.match_char(buffers.at(BufTy::Base, idx)) {
                idx += 1;
            }
            buffers.set_offset(BufTy::Base, 2, idx);

            idx < last
        })
    }

    fn scan_till_nonempty(&self, last: BufPointer) -> bool {
        with_buffers_mut(|buffers| {
            let start = buffers.offset(BufTy::Base, 2);
            buffers.set_offset(BufTy::Base, 1, start);

            let mut idx = start;
            while idx < last && !self.match_char(buffers.at(BufTy::Base, idx)) {
                idx += 1;
            }
            buffers.set_offset(BufTy::Base, 2, idx);

            idx - start != 0
        })
    }
}

#[no_mangle]
pub extern "C" fn scan1(char1: ASCIICode) -> bool {
    let last = with_buffers(|buffers| buffers.init(BufTy::Base));
    Scan::new(&[char1]).scan_till(last)
}

#[no_mangle]
pub extern "C" fn scan1_white(char1: ASCIICode) -> bool {
    let last = with_buffers(|buffers| buffers.init(BufTy::Base));
    Scan::new(&[char1])
        .class(LexClass::Whitespace)
        .scan_till(last)
}

#[no_mangle]
pub extern "C" fn scan2(char1: ASCIICode, char2: ASCIICode) -> bool {
    let last = with_buffers(|buffers| buffers.init(BufTy::Base));
    Scan::new(&[char1, char2]).scan_till(last)
}

#[no_mangle]
pub extern "C" fn scan2_white(char1: ASCIICode, char2: ASCIICode) -> bool {
    let last = with_buffers(|buffers| buffers.init(BufTy::Base));
    Scan::new(&[char1, char2])
        .class(LexClass::Whitespace)
        .scan_till(last)
}

#[no_mangle]
pub extern "C" fn scan3(char1: ASCIICode, char2: ASCIICode, char3: ASCIICode) -> bool {
    let last = with_buffers(|buffers| buffers.init(BufTy::Base));
    Scan::new(&[char1, char2, char3]).scan_till(last)
}

#[no_mangle]
pub extern "C" fn scan_alpha() -> bool {
    let last = with_buffers(|buffers| buffers.init(BufTy::Base));
    Scan::new(&[])
        .not_class(LexClass::Alpha)
        .scan_till_nonempty(last)
}

#[no_mangle]
pub extern "C" fn scan_white_space() -> bool {
    let last = with_buffers(|buffers| buffers.init(BufTy::Base));
    Scan::new(&[])
        .not_class(LexClass::Whitespace)
        .scan_till(last)
}

#[no_mangle]
pub extern "C" fn scan_identifier(char1: ASCIICode, char2: ASCIICode, char3: ASCIICode) -> ScanRes {
    with_buffers_mut(|buffers| {
        let last = buffers.init(BufTy::Base);
        let start = buffers.offset(BufTy::Base, 2);
        buffers.set_offset(BufTy::Base, 1, start);

        let mut idx = start;
        let char = buffers.at(BufTy::Base, idx);
        if LexClass::of(char) != LexClass::Numeric {
            while idx < last && IdClass::of(buffers.at(BufTy::Base, idx)) == IdClass::LegalIdChar {
                idx += 1;
            }
            buffers.set_offset(BufTy::Base, 2, idx);
        }

        let char = buffers.at(BufTy::Base, idx);
        if idx - start == 0 {
            ScanRes::IdNull
        } else if LexClass::of(char) == LexClass::Whitespace || idx == last {
            ScanRes::WhitespaceAdjacent
        } else if char == char1 || char == char2 || char == char3 {
            ScanRes::SpecifiedCharAdjacent
        } else {
            ScanRes::OtherCharAdjacent
        }
    })
}

#[no_mangle]
pub extern "C" fn scan_nonneg_integer() -> bool {
    let last = with_buffers(|buffers| buffers.init(BufTy::Base));
    Scan::new(&[])
        .not_class(LexClass::Numeric)
        .scan_till_nonempty(last)
}

#[no_mangle]
pub extern "C" fn scan_integer(token_value: &mut i32) -> bool {
    with_buffers_mut(|buffers| {
        let last = buffers.init(BufTy::Base);
        let start = buffers.offset(BufTy::Base, 2);
        buffers.set_offset(BufTy::Base, 1, start);

        let mut idx = start;
        let sign = if buffers.at(BufTy::Base, idx) == b'-' {
            idx += 1;
            true
        } else {
            false
        };

        *token_value = 0;
        let mut char = buffers.at(BufTy::Base, idx);
        while idx < last && LexClass::of(char) == LexClass::Numeric {
            *token_value = *token_value * 10 + (char - 48) as i32;
            idx += 1;
            char = buffers.at(BufTy::Base, idx);
        }
        buffers.set_offset(BufTy::Base, 2, idx);

        if sign {
            *token_value *= -1;
        }

        idx - start != if sign { 1 } else { 0 }
    })
}