tree_house_bindings/
grammar.rs

1use std::fmt;
2use std::path::{Path, PathBuf};
3use std::ptr::NonNull;
4
5use libloading::{Library, Symbol};
6#[cfg(feature = "tree-sitter-language")]
7use tree_sitter_language::LanguageFn;
8
9/// Lowest supported ABI version of a grammar.
10// WARNING: update when updating vendored c sources
11// `TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION`
12pub const MIN_COMPATIBLE_ABI_VERSION: u32 = 13;
13// `TREE_SITTER_LANGUAGE_VERSION`
14pub const ABI_VERSION: u32 = 15;
15
16// opaque pointer
17enum GrammarData {}
18
19#[repr(transparent)]
20#[derive(Clone, Copy, PartialEq, Eq, Hash)]
21pub struct Grammar {
22    ptr: NonNull<GrammarData>,
23}
24
25unsafe impl Send for Grammar {}
26unsafe impl Sync for Grammar {}
27
28impl std::fmt::Debug for Grammar {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.debug_struct("Grammar").finish_non_exhaustive()
31    }
32}
33
34impl Grammar {
35    /// Loads a shared library containing a tree sitter grammar with name `name`
36    // from `library_path`.
37    ///
38    /// # Safety
39    ///
40    /// `library_path` must be a valid tree sitter grammar
41    pub unsafe fn new(name: &str, library_path: &Path) -> Result<Grammar, Error> {
42        let library = unsafe {
43            Library::new(library_path).map_err(|err| Error::DlOpen {
44                err,
45                path: library_path.to_owned(),
46            })?
47        };
48        let language_fn_name = format!("tree_sitter_{}", name.replace('-', "_"));
49        let language_fn: Symbol<unsafe extern "C" fn() -> NonNull<GrammarData>> = library
50            .get(language_fn_name.as_bytes())
51            .map_err(|err| Error::DlSym {
52                err,
53                symbol: name.to_owned(),
54            })?;
55        let grammar = Grammar::from_grammar_data(language_fn())?;
56        std::mem::forget(library);
57        Ok(grammar)
58    }
59
60    fn from_grammar_data(ptr: NonNull<GrammarData>) -> Result<Grammar, Error> {
61        let grammar = Grammar { ptr };
62        let version = grammar.abi_version();
63        if (MIN_COMPATIBLE_ABI_VERSION..=ABI_VERSION).contains(&version) {
64            Ok(grammar)
65        } else {
66            Err(Error::IncompatibleVersion { version })
67        }
68    }
69
70    pub fn abi_version(self) -> u32 {
71        unsafe { ts_language_abi_version(self) }
72    }
73
74    pub fn node_kind_is_visible(self, kind_id: u16) -> bool {
75        let symbol_type = unsafe { ts_language_symbol_type(self, kind_id) };
76        symbol_type <= (SymbolType::Anonymous as u32)
77    }
78}
79
80#[cfg(feature = "tree-sitter-language")]
81impl TryFrom<LanguageFn> for Grammar {
82    type Error = Error;
83
84    fn try_from(builder: LanguageFn) -> Result<Self, Self::Error> {
85        let ptr = unsafe { NonNull::new_unchecked(builder.into_raw()().cast_mut().cast()) };
86        Self::from_grammar_data(ptr)
87    }
88}
89
90#[derive(thiserror::Error, Debug)]
91pub enum Error {
92    #[error("Error opening dynamic library {path:?}")]
93    DlOpen {
94        #[source]
95        err: libloading::Error,
96        path: PathBuf,
97    },
98    #[error("Failed to load symbol {symbol}")]
99    DlSym {
100        #[source]
101        err: libloading::Error,
102        symbol: String,
103    },
104    #[error("Tried to load grammar with incompatible ABI {version}.")]
105    IncompatibleVersion { version: u32 },
106}
107
108/// An error that occurred when trying to assign an incompatible [`Grammar`] to
109/// a [`crate::parser::Parser`].
110#[derive(Debug, PartialEq, Eq)]
111pub struct IncompatibleGrammarError {
112    pub abi_version: u32,
113}
114
115impl fmt::Display for IncompatibleGrammarError {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        write!(
118            f,
119            "Tried to load grammar with incompatible ABI version {}.",
120            self.abi_version,
121        )
122    }
123}
124impl std::error::Error for IncompatibleGrammarError {}
125
126#[repr(u32)]
127#[allow(dead_code)]
128pub enum SymbolType {
129    Regular,
130    Anonymous,
131    Supertype,
132    Auxiliary,
133}
134
135extern "C" {
136    /// Get the ABI version number for this language. This version number
137    /// is used to ensure that languages were generated by a compatible version of
138    /// Tree-sitter. See also `ts_parser_set_language`.
139    pub fn ts_language_abi_version(grammar: Grammar) -> u32;
140
141    /// Checks whether the given node type belongs to named nodes, anonymous nodes, or hidden
142    /// nodes.
143    ///
144    /// See also `ts_node_is_named`. Hidden nodes are never returned from the API.
145    pub fn ts_language_symbol_type(grammar: Grammar, symbol: u16) -> u32;
146}