plotnik_langs/
lib.rs

1use std::num::NonZeroU16;
2use std::sync::Arc;
3
4use arborium_tree_sitter::Language;
5
6use plotnik_core::grammar::Grammar;
7use plotnik_core::{Cardinality, DynamicNodeTypes, NodeFieldId, NodeTypeId, NodeTypes, RawNode};
8
9pub mod builtin;
10pub mod dynamic;
11
12#[cfg(test)]
13mod lib_tests;
14
15pub use builtin::*;
16
17/// User-facing language type. Works with any language (static or dynamic).
18pub type Lang = Arc<dyn LangImpl>;
19
20/// Trait providing a unified facade for tree-sitter's Language API
21/// combined with our node type constraints.
22pub trait LangImpl: Send + Sync {
23    fn name(&self) -> &str;
24
25    /// Parse source code into a tree-sitter tree.
26    fn parse(&self, source: &str) -> arborium_tree_sitter::Tree;
27
28    fn resolve_named_node(&self, kind: &str) -> Option<NodeTypeId>;
29    fn resolve_anonymous_node(&self, kind: &str) -> Option<NodeTypeId>;
30    fn resolve_field(&self, name: &str) -> Option<NodeFieldId>;
31
32    // Enumeration methods for suggestions
33    fn all_named_node_kinds(&self) -> Vec<&'static str>;
34    fn all_field_names(&self) -> Vec<&'static str>;
35    fn node_type_name(&self, node_type_id: NodeTypeId) -> Option<&'static str>;
36    fn field_name(&self, field_id: NodeFieldId) -> Option<&'static str>;
37    fn fields_for_node_type(&self, node_type_id: NodeTypeId) -> Vec<&'static str>;
38
39    fn is_supertype(&self, node_type_id: NodeTypeId) -> bool;
40    fn subtypes(&self, supertype: NodeTypeId) -> &[u16];
41
42    fn root(&self) -> Option<NodeTypeId>;
43    fn is_extra(&self, node_type_id: NodeTypeId) -> bool;
44
45    fn has_field(&self, node_type_id: NodeTypeId, node_field_id: NodeFieldId) -> bool;
46    fn field_cardinality(
47        &self,
48        node_type_id: NodeTypeId,
49        node_field_id: NodeFieldId,
50    ) -> Option<Cardinality>;
51    fn valid_field_types(
52        &self,
53        node_type_id: NodeTypeId,
54        node_field_id: NodeFieldId,
55    ) -> &[NodeTypeId];
56    fn is_valid_field_type(
57        &self,
58        node_type_id: NodeTypeId,
59        node_field_id: NodeFieldId,
60        child: NodeTypeId,
61    ) -> bool;
62
63    fn children_cardinality(&self, node_type_id: NodeTypeId) -> Option<Cardinality>;
64    fn valid_child_types(&self, node_type_id: NodeTypeId) -> &[NodeTypeId];
65    fn is_valid_child_type(&self, node_type_id: NodeTypeId, child: NodeTypeId) -> bool;
66
67    /// Get the grammar for this language (for grammar-verify).
68    fn grammar(&self) -> &Grammar;
69}
70
71/// Language implementation with embedded grammar and node types.
72#[derive(Debug)]
73pub struct LangInner {
74    name: String,
75    ts_lang: Language,
76    node_types: DynamicNodeTypes,
77    grammar: Grammar,
78}
79
80impl LangInner {
81    /// Create a new language from raw node types and grammar.
82    pub fn new(name: &str, ts_lang: Language, raw_nodes: Vec<RawNode>, grammar: Grammar) -> Self {
83        let node_types = DynamicNodeTypes::build(
84            &raw_nodes,
85            |kind, named| {
86                let id = ts_lang.id_for_node_kind(kind, named);
87                NonZeroU16::new(id)
88            },
89            |field_name| ts_lang.field_id_for_name(field_name),
90        );
91
92        Self {
93            name: name.to_owned(),
94            ts_lang,
95            node_types,
96            grammar,
97        }
98    }
99
100    pub fn node_types(&self) -> &DynamicNodeTypes {
101        &self.node_types
102    }
103}
104
105impl LangImpl for LangInner {
106    fn name(&self) -> &str {
107        &self.name
108    }
109
110    fn parse(&self, source: &str) -> arborium_tree_sitter::Tree {
111        let mut parser = arborium_tree_sitter::Parser::new();
112        parser
113            .set_language(&self.ts_lang)
114            .expect("failed to set language");
115        parser.parse(source, None).expect("failed to parse source")
116    }
117
118    fn resolve_named_node(&self, kind: &str) -> Option<NodeTypeId> {
119        let id = self.ts_lang.id_for_node_kind(kind, true);
120        NonZeroU16::new(id)
121    }
122
123    fn resolve_anonymous_node(&self, kind: &str) -> Option<NodeTypeId> {
124        let id = self.ts_lang.id_for_node_kind(kind, false);
125        // Node ID 0 is tree-sitter internal; we never obtain it via cursor walk.
126        NonZeroU16::new(id)
127    }
128
129    fn resolve_field(&self, name: &str) -> Option<NodeFieldId> {
130        self.ts_lang.field_id_for_name(name)
131    }
132
133    fn all_named_node_kinds(&self) -> Vec<&'static str> {
134        let count = self.ts_lang.node_kind_count();
135        (0..count as u16)
136            .filter(|&id| self.ts_lang.node_kind_is_named(id))
137            .filter_map(|id| self.ts_lang.node_kind_for_id(id))
138            .collect()
139    }
140
141    fn all_field_names(&self) -> Vec<&'static str> {
142        let count = self.ts_lang.field_count();
143        (1..=count as u16)
144            .filter_map(|id| self.ts_lang.field_name_for_id(id))
145            .collect()
146    }
147
148    fn node_type_name(&self, node_type_id: NodeTypeId) -> Option<&'static str> {
149        self.ts_lang.node_kind_for_id(node_type_id.get())
150    }
151
152    fn field_name(&self, field_id: NodeFieldId) -> Option<&'static str> {
153        self.ts_lang.field_name_for_id(field_id.get())
154    }
155
156    fn fields_for_node_type(&self, node_type_id: NodeTypeId) -> Vec<&'static str> {
157        let count = self.ts_lang.field_count();
158        (1..=count as u16)
159            .filter_map(|id| {
160                let field_id = std::num::NonZeroU16::new(id)?;
161                if self.node_types.has_field(node_type_id, field_id) {
162                    self.ts_lang.field_name_for_id(id)
163                } else {
164                    None
165                }
166            })
167            .collect()
168    }
169
170    fn is_supertype(&self, node_type_id: NodeTypeId) -> bool {
171        self.ts_lang.node_kind_is_supertype(node_type_id.get())
172    }
173
174    fn subtypes(&self, supertype: NodeTypeId) -> &[u16] {
175        self.ts_lang.subtypes_for_supertype(supertype.get())
176    }
177
178    fn root(&self) -> Option<NodeTypeId> {
179        self.node_types.root()
180    }
181
182    fn is_extra(&self, node_type_id: NodeTypeId) -> bool {
183        self.node_types.is_extra(node_type_id)
184    }
185
186    fn has_field(&self, node_type_id: NodeTypeId, node_field_id: NodeFieldId) -> bool {
187        self.node_types.has_field(node_type_id, node_field_id)
188    }
189
190    fn field_cardinality(
191        &self,
192        node_type_id: NodeTypeId,
193        node_field_id: NodeFieldId,
194    ) -> Option<Cardinality> {
195        self.node_types
196            .field_cardinality(node_type_id, node_field_id)
197    }
198
199    fn valid_field_types(
200        &self,
201        node_type_id: NodeTypeId,
202        node_field_id: NodeFieldId,
203    ) -> &[NodeTypeId] {
204        self.node_types
205            .valid_field_types(node_type_id, node_field_id)
206    }
207
208    fn is_valid_field_type(
209        &self,
210        node_type_id: NodeTypeId,
211        node_field_id: NodeFieldId,
212        child: NodeTypeId,
213    ) -> bool {
214        self.node_types
215            .is_valid_field_type(node_type_id, node_field_id, child)
216    }
217
218    fn children_cardinality(&self, node_type_id: NodeTypeId) -> Option<Cardinality> {
219        self.node_types.children_cardinality(node_type_id)
220    }
221
222    fn valid_child_types(&self, node_type_id: NodeTypeId) -> &[NodeTypeId] {
223        self.node_types.valid_child_types(node_type_id)
224    }
225
226    fn is_valid_child_type(&self, node_type_id: NodeTypeId, child: NodeTypeId) -> bool {
227        self.node_types.is_valid_child_type(node_type_id, child)
228    }
229
230    fn grammar(&self) -> &Grammar {
231        &self.grammar
232    }
233}