use std::fmt; use std::path::{Path, PathBuf}; use std::ptr::NonNull; use libloading::{Library, Symbol}; /// supported TS versions, WARNING: update when updating vendored c sources pub const MIN_COMPATIBLE_ABI_VERSION: u32 = 13; pub const ABI_VERSION: u32 = 14; // opaque pointer enum GrammarData {} #[repr(transparent)] #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Grammar { ptr: NonNull, } 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 { /// Loads a shared library containg a tree sitter grammar with name `name` // from `library_path`. /// /// # Safety /// /// `library_path` must be a valid tree sitter grammar pub unsafe fn new(name: &str, library_path: &Path) -> Result { 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 grammar = unsafe { let language_fn: Symbol NonNull> = library .get(language_fn_name.as_bytes()) .map_err(|err| Error::DlSym { err, symbol: name.to_owned(), })?; Grammar { ptr: language_fn() } }; let version = grammar.version(); if (MIN_COMPATIBLE_ABI_VERSION..=ABI_VERSION).contains(&version) { std::mem::forget(library); Ok(grammar) } else { Err(Error::IncompatibleVersion { version }) } } pub fn version(self) -> u32 { unsafe { ts_language_version(self) } } } #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Error opening dynamic library {path:?}")] DlOpen { #[source] err: libloading::Error, path: PathBuf, }, #[error("Failed to load symbol {symbol}")] DlSym { #[source] err: libloading::Error, symbol: String, }, #[error("Tried to load grammar with incompatible ABI {version}.")] IncompatibleVersion { version: u32 }, } /// An error that occurred when trying to assign an incompatible [`Grammar`] to /// a [`Parser`]. #[derive(Debug, PartialEq, Eq)] pub struct IncompatibleGrammarError { 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 {}.", self.version, ) } } impl std::error::Error for IncompatibleGrammarError {} extern "C" { /// Get the ABI version number for this language. This version number /// is used to ensure that languages were generated by a compatible version of /// Tree-sitter. See also [`ts_parser_set_language`]. pub fn ts_language_version(grammar: Grammar) -> u32; }