verificar/grammar/
typescript.rs1use crate::Language;
7
8use super::Grammar;
9
10pub struct TypeScriptGrammar {
15 #[cfg(feature = "tree-sitter")]
16 parser: std::sync::Mutex<tree_sitter::Parser>,
17}
18
19impl std::fmt::Debug for TypeScriptGrammar {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 f.debug_struct("TypeScriptGrammar")
22 .field("language", &"typescript")
23 .finish()
24 }
25}
26
27impl Default for TypeScriptGrammar {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl TypeScriptGrammar {
34 #[must_use]
41 #[allow(clippy::expect_used)]
42 pub fn new() -> Self {
43 #[cfg(feature = "tree-sitter")]
44 {
45 let mut parser = tree_sitter::Parser::new();
46 parser
47 .set_language(&tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into())
48 .expect("Failed to load TypeScript grammar");
49 Self {
50 parser: std::sync::Mutex::new(parser),
51 }
52 }
53 #[cfg(not(feature = "tree-sitter"))]
54 {
55 Self {}
56 }
57 }
58
59 #[cfg(feature = "tree-sitter")]
63 pub fn parse(&self, code: &str) -> Option<tree_sitter::Tree> {
64 let mut parser = self.parser.lock().ok()?;
65 parser.parse(code, None)
66 }
67
68 #[cfg(feature = "tree-sitter")]
70 pub fn root_node(&self, code: &str) -> Option<String> {
71 self.parse(code)
72 .map(|tree| tree.root_node().kind().to_string())
73 }
74
75 #[cfg(feature = "tree-sitter")]
77 pub fn has_errors(&self, code: &str) -> bool {
78 self.parse(code)
79 .map_or(true, |tree| tree.root_node().has_error())
80 }
81
82 #[cfg(feature = "tree-sitter")]
84 pub fn ast_depth(&self, code: &str) -> usize {
85 fn max_depth(node: tree_sitter::Node<'_>) -> usize {
86 let child_depths = node
87 .children(&mut node.walk())
88 .map(max_depth)
89 .max()
90 .unwrap_or(0);
91 1 + child_depths
92 }
93
94 self.parse(code)
95 .map_or(0, |tree| max_depth(tree.root_node()))
96 }
97
98 #[cfg(feature = "tree-sitter")]
100 pub fn node_count(&self, code: &str) -> usize {
101 fn count_nodes(node: tree_sitter::Node<'_>) -> usize {
102 1 + node
103 .children(&mut node.walk())
104 .map(count_nodes)
105 .sum::<usize>()
106 }
107
108 self.parse(code)
109 .map_or(0, |tree| count_nodes(tree.root_node()))
110 }
111}
112
113impl Grammar for TypeScriptGrammar {
114 fn language(&self) -> Language {
115 Language::TypeScript
116 }
117
118 fn validate(&self, code: &str) -> bool {
119 if code.is_empty() {
120 return false;
121 }
122
123 #[cfg(feature = "tree-sitter")]
124 {
125 !self.has_errors(code)
126 }
127
128 #[cfg(not(feature = "tree-sitter"))]
129 {
130 let balanced_parens = code.chars().filter(|&c| c == '(').count()
133 == code.chars().filter(|&c| c == ')').count();
134 let balanced_brackets = code.chars().filter(|&c| c == '[').count()
135 == code.chars().filter(|&c| c == ']').count();
136 let balanced_braces = code.chars().filter(|&c| c == '{').count()
137 == code.chars().filter(|&c| c == '}').count();
138
139 balanced_parens && balanced_brackets && balanced_braces
140 }
141 }
142
143 fn max_enumeration_depth(&self) -> usize {
144 5 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
157 fn test_typescript_grammar_language() {
158 let grammar = TypeScriptGrammar::new();
159 assert_eq!(grammar.language(), Language::TypeScript);
160 }
161
162 #[test]
163 fn test_typescript_grammar_validate_basic() {
164 let grammar = TypeScriptGrammar::new();
165 assert!(grammar.validate("let x = 1;"));
166 assert!(grammar.validate("const y: number = 42;"));
167 assert!(!grammar.validate(""));
168 }
169
170 #[test]
171 fn test_typescript_grammar_validate_function() {
172 let grammar = TypeScriptGrammar::new();
173 assert!(grammar.validate("function foo(): void {}"));
174 assert!(grammar.validate("function add(a: number, b: number): number { return a + b; }"));
175 assert!(grammar.validate("const arrow = (x: number) => x * 2;"));
176 }
177
178 #[test]
179 fn test_typescript_grammar_validate_interface() {
180 let grammar = TypeScriptGrammar::new();
181 assert!(grammar.validate("interface Foo { x: number; }"));
182 assert!(grammar.validate("interface Bar { name: string; age: number; }"));
183 }
184
185 #[test]
186 fn test_typescript_grammar_validate_class() {
187 let grammar = TypeScriptGrammar::new();
188 assert!(grammar.validate("class Foo {}"));
189 assert!(grammar.validate("class Bar { constructor(public x: number) {} }"));
190 assert!(grammar.validate("class Baz extends Foo { private y: string = ''; }"));
191 }
192
193 #[test]
194 fn test_typescript_grammar_validate_type_annotations() {
195 let grammar = TypeScriptGrammar::new();
196 assert!(grammar.validate("let x: number = 1;"));
197 assert!(grammar.validate("let arr: number[] = [1, 2, 3];"));
198 assert!(grammar.validate("let tuple: [string, number] = ['a', 1];"));
199 assert!(grammar.validate("type MyType = string | number;"));
200 }
201
202 #[test]
203 fn test_typescript_grammar_validate_generics() {
204 let grammar = TypeScriptGrammar::new();
205 assert!(grammar.validate("function identity<T>(x: T): T { return x; }"));
206 assert!(grammar.validate("class Box<T> { value: T; }"));
207 assert!(grammar.validate("let map: Map<string, number> = new Map();"));
208 }
209
210 #[test]
211 fn test_typescript_grammar_validate_control_flow() {
212 let grammar = TypeScriptGrammar::new();
213 assert!(grammar.validate("if (x) { y = 1; }"));
214 assert!(grammar.validate("for (let i = 0; i < 10; i++) { console.log(i); }"));
215 assert!(grammar.validate("while (true) { break; }"));
216 assert!(grammar.validate("switch (x) { case 1: break; default: break; }"));
217 }
218
219 #[test]
220 fn test_typescript_grammar_validate_async() {
221 let grammar = TypeScriptGrammar::new();
222 assert!(grammar.validate("async function fetch(): Promise<void> {}"));
223 assert!(grammar.validate("const result = await fetch();"));
224 }
225
226 #[test]
227 fn test_typescript_grammar_validate_unbalanced() {
228 let grammar = TypeScriptGrammar::new();
229 assert!(!grammar.validate("let x = (1 + 2"));
231 assert!(!grammar.validate("let x = [1, 2"));
232 assert!(!grammar.validate("let x = {a: 1"));
233 }
234
235 #[test]
236 fn test_typescript_grammar_max_depth() {
237 let grammar = TypeScriptGrammar::new();
238 assert_eq!(grammar.max_enumeration_depth(), 5);
239 }
240
241 #[test]
242 fn test_typescript_grammar_debug() {
243 let grammar = TypeScriptGrammar::new();
244 let debug = format!("{:?}", grammar);
245 assert!(debug.contains("TypeScriptGrammar"));
246 assert!(debug.contains("typescript"));
247 }
248
249 #[test]
250 fn test_typescript_grammar_default() {
251 let grammar = TypeScriptGrammar::default();
252 assert_eq!(grammar.language(), Language::TypeScript);
253 }
254
255 #[cfg(feature = "tree-sitter")]
256 mod tree_sitter_tests {
257 use super::*;
258
259 #[test]
260 fn test_parse_simple() {
261 let grammar = TypeScriptGrammar::new();
262 let tree = grammar.parse("let x = 1;");
263 assert!(tree.is_some());
264 }
265
266 #[test]
267 fn test_root_node() {
268 let grammar = TypeScriptGrammar::new();
269 let root = grammar.root_node("let x = 1;");
270 assert_eq!(root, Some("program".to_string()));
271 }
272
273 #[test]
274 fn test_has_errors_valid() {
275 let grammar = TypeScriptGrammar::new();
276 assert!(!grammar.has_errors("let x = 1;"));
277 assert!(!grammar.has_errors("function foo(): void {}"));
278 }
279
280 #[test]
281 fn test_has_errors_invalid() {
282 let grammar = TypeScriptGrammar::new();
283 assert!(grammar.has_errors("function foo("));
284 assert!(grammar.has_errors("class {"));
285 }
286
287 #[test]
288 fn test_ast_depth() {
289 let grammar = TypeScriptGrammar::new();
290 let simple_depth = grammar.ast_depth("let x = 1;");
291 let complex_depth = grammar.ast_depth("function foo() { if (x) { return y + z; } }");
292 assert!(simple_depth > 0);
293 assert!(complex_depth > simple_depth);
294 }
295
296 #[test]
297 fn test_node_count() {
298 let grammar = TypeScriptGrammar::new();
299 let simple_count = grammar.node_count("let x = 1;");
300 let complex_count = grammar.node_count("let x = 1; let y = 2; let z = 3;");
301 assert!(simple_count > 0);
302 assert!(complex_count > simple_count);
303 }
304
305 #[test]
306 fn test_typescript_specific_syntax() {
307 let grammar = TypeScriptGrammar::new();
308 assert!(!grammar.has_errors("let x: number = 1;"));
310 assert!(
311 !grammar.has_errors("function add(a: number, b: number): number { return a + b; }")
312 );
313 assert!(!grammar.has_errors("interface Foo { bar: string; }"));
314 assert!(!grammar.has_errors("type MyType = string | number;"));
315 assert!(!grammar.has_errors("enum Color { Red, Green, Blue }"));
316 }
317 }
318}