tree_house_bindings/
grammar.rs1use 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
9pub const MIN_COMPATIBLE_ABI_VERSION: u32 = 13;
13pub const ABI_VERSION: u32 = 15;
15
16enum 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 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:?}: {err}")]
93 DlOpen {
94 #[source]
95 err: libloading::Error,
96 path: PathBuf,
97 },
98 #[error("Failed to load symbol {symbol}: {err}")]
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#[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 pub fn ts_language_abi_version(grammar: Grammar) -> u32;
140
141 pub fn ts_language_symbol_type(grammar: Grammar, symbol: u16) -> u32;
146}