use super::token_source::{Token, TokenSource};
use core::ffi::c_void;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct TSLexState(pub u32);
#[repr(C)]
pub struct TsLexer {
pub lookahead: unsafe extern "C" fn(*mut TsLexer) -> u32,
pub advance: unsafe extern "C" fn(*mut TsLexer, bool),
pub mark_end: unsafe extern "C" fn(*mut TsLexer),
pub result_symbol: u16,
pub data: *mut c_void,
}
struct Backing<'a> {
input: &'a [u8],
pos: usize,
mark: usize,
tok_len: usize,
}
unsafe extern "C" fn lookahead(lex: *mut TsLexer) -> u32 {
unsafe {
let st = &*((*lex).data as *const Backing);
if st.pos < st.input.len() {
st.input[st.pos] as u32
} else {
0
}
}
}
unsafe extern "C" fn advance(lex: *mut TsLexer, skip: bool) {
unsafe {
let st = &mut *((*lex).data as *mut Backing);
if !skip && st.pos < st.input.len() {
st.pos += 1;
} else if skip {
if st.pos < st.input.len() {
st.pos += 1;
}
}
}
}
unsafe extern "C" fn mark_end(lex: *mut TsLexer) {
unsafe {
let st = &mut *((*lex).data as *mut Backing);
st.mark = st.pos;
}
}
pub struct TsLexFnAdapter<'a> {
lang_lex: unsafe extern "C" fn(*mut c_void, TSLexState) -> bool,
backing: Backing<'a>,
ts: TsLexer,
state_tag: TSLexState,
look: Option<Token>,
}
impl<'a> TsLexFnAdapter<'a> {
pub fn new(
input: &'a [u8],
lang_lex: unsafe extern "C" fn(*mut c_void, TSLexState) -> bool,
) -> Self {
let backing = Backing {
input,
pos: 0,
mark: 0,
tok_len: 0,
};
let ts = TsLexer {
lookahead,
advance,
mark_end,
result_symbol: u16::MAX,
data: std::ptr::null_mut(), };
let mut adapter = Self {
lang_lex,
backing,
ts,
state_tag: TSLexState(0),
look: None,
};
adapter.ts.data = &mut adapter.backing as *mut _ as *mut c_void;
adapter
}
fn next_internal(&mut self) -> Option<Token> {
while self.backing.pos < self.backing.input.len() {
let c = self.backing.input[self.backing.pos];
if matches!(c, b' ' | b'\n' | b'\r' | b'\t') {
self.backing.pos += 1;
} else {
break;
}
}
if self.backing.pos >= self.backing.input.len() {
return None;
}
let token_start = self.backing.pos;
self.backing.mark = self.backing.pos;
self.backing.tok_len = 0;
self.ts.result_symbol = u16::MAX;
self.ts.data = &mut self.backing as *mut _ as *mut c_void;
let ok = unsafe { (self.lang_lex)(&mut self.ts as *mut _ as *mut c_void, self.state_tag) };
if ok && self.ts.result_symbol != u16::MAX {
let end = if self.backing.mark > token_start {
self.backing.mark
} else {
self.backing.pos
};
let len = end - token_start;
let tok = Token {
sym: self.ts.result_symbol,
start: token_start,
len,
};
self.backing.pos = end;
Some(tok)
} else {
let tok = Token {
sym: u16::MAX,
start: token_start,
len: 1,
};
self.backing.pos = token_start + 1;
Some(tok)
}
}
}
impl<'a> TokenSource for TsLexFnAdapter<'a> {
fn peek(&mut self) -> Option<Token> {
if self.look.is_none() {
self.look = self.next_internal();
}
self.look
}
fn bump(&mut self) {
self.look = None;
}
fn offset(&self) -> usize {
self.backing.pos
}
}