use std::fmt;
use std::path::{Path, PathBuf};
use std::ptr::NonNull;
use libloading::{Library, Symbol};
#[cfg(feature = "tree-sitter-language")]
use tree_sitter_language::LanguageFn;
pub const MIN_COMPATIBLE_ABI_VERSION: u32 = 13;
pub const ABI_VERSION: u32 = 15;
enum GrammarData {}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Grammar {
ptr: NonNull<GrammarData>,
}
unsafe impl Send for Grammar {}
unsafe impl Sync for Grammar {}
impl std::fmt::Debug for Grammar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Grammar").finish_non_exhaustive()
}
}
impl Grammar {
pub unsafe fn new(name: &str, library_path: &Path) -> Result<Grammar, Error> {
let library = unsafe {
Library::new(library_path).map_err(|err| Error::DlOpen {
err,
path: library_path.to_owned(),
})?
};
let language_fn_name = format!("tree_sitter_{}", name.replace('-', "_"));
let language_fn: Symbol<unsafe extern "C" fn() -> NonNull<GrammarData>> = library
.get(language_fn_name.as_bytes())
.map_err(|err| Error::DlSym {
err,
symbol: name.to_owned(),
})?;
let grammar = Grammar::from_grammar_data(language_fn())?;
std::mem::forget(library);
Ok(grammar)
}
fn from_grammar_data(ptr: NonNull<GrammarData>) -> Result<Grammar, Error> {
let grammar = Grammar { ptr };
let version = grammar.abi_version();
if (MIN_COMPATIBLE_ABI_VERSION..=ABI_VERSION).contains(&version) {
Ok(grammar)
} else {
Err(Error::IncompatibleVersion { version })
}
}
pub fn abi_version(self) -> u32 {
unsafe { ts_language_abi_version(self) }
}
pub fn node_kind_is_visible(self, kind_id: u16) -> bool {
let symbol_type = unsafe { ts_language_symbol_type(self, kind_id) };
symbol_type <= (SymbolType::Anonymous as u32)
}
}
#[cfg(feature = "tree-sitter-language")]
impl TryFrom<LanguageFn> for Grammar {
type Error = Error;
fn try_from(builder: LanguageFn) -> Result<Self, Self::Error> {
let ptr = unsafe { NonNull::new_unchecked(builder.into_raw()().cast_mut().cast()) };
Self::from_grammar_data(ptr)
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Error opening dynamic library {path:?}: {err}")]
DlOpen {
#[source]
err: libloading::Error,
path: PathBuf,
},
#[error("Failed to load symbol {symbol}: {err}")]
DlSym {
#[source]
err: libloading::Error,
symbol: String,
},
#[error("Tried to load grammar with incompatible ABI {version}.")]
IncompatibleVersion { version: u32 },
}
#[derive(Debug, PartialEq, Eq)]
pub struct IncompatibleGrammarError {
pub abi_version: u32,
}
impl fmt::Display for IncompatibleGrammarError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Tried to load grammar with incompatible ABI version {}.",
self.abi_version,
)
}
}
impl std::error::Error for IncompatibleGrammarError {}
#[repr(u32)]
#[allow(dead_code)]
pub enum SymbolType {
Regular,
Anonymous,
Supertype,
Auxiliary,
}
extern "C" {
pub fn ts_language_abi_version(grammar: Grammar) -> u32;
pub fn ts_language_symbol_type(grammar: Grammar, symbol: u16) -> u32;
}