Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-syntax/src/tree_sitter/grammar.rs')
| -rw-r--r-- | helix-syntax/src/tree_sitter/grammar.rs | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/helix-syntax/src/tree_sitter/grammar.rs b/helix-syntax/src/tree_sitter/grammar.rs new file mode 100644 index 00000000..fe164e75 --- /dev/null +++ b/helix-syntax/src/tree_sitter/grammar.rs @@ -0,0 +1,107 @@ +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<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 { + /// 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<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 grammar = unsafe { + 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(), + })?; + 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; +} |