thread_ast_engine/language.rs
1// SPDX-FileCopyrightText: 2022 Herrington Darkholme <2883231+HerringtonDarkholme@users.noreply.github.com>
2// SPDX-FileCopyrightText: 2025 Knitli Inc. <knitli@knit.li>
3// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
4//
5// SPDX-License-Identifier: AGPL-3.0-or-later AND MIT
6//! # Language Abstraction for AST Parsing
7//!
8//! This module defines the [`Language`](crates/ast-engine/src/language.rs:16) trait, which abstracts over language-specific details for AST parsing and pattern matching.
9//!
10//! ## Purpose
11//!
12//! - **Meta-variable Handling:** Configure how meta-variables (e.g., `$A`) are recognized and processed for different languages.
13//! - **Pattern Preprocessing:** Normalize pattern code before matching, adapting to language-specific quirks.
14//! - **Tree-sitter Integration:** Map node kinds and fields to tree-sitter IDs for efficient AST traversal.
15//! - **Extensibility:** Support custom language implementations (see [`Tsx`](crates/ast-engine/src/language.rs:63) for TypeScript/TSX).
16//!
17//! ## Key Components
18//!
19//! - [`Language`](crates/ast-engine/src/language.rs:16): Core trait for language-specific AST operations.
20//! - [`Tsx`](crates/ast-engine/src/language.rs:63): Example implementation for TypeScript/TSX.
21//! - Meta-variable extraction and normalization utilities.
22//!
23//! ## Example
24//!
25//! ```rust,no_run
26//! use thread_ast_engine::language::Language;
27//!
28//! let lang = Tsx {};
29//! let pattern = lang.pre_process_pattern("var $A = $B");
30//! let meta_var = lang.extract_meta_var("$A");
31//! ```
32#[allow(unused_imports)]
33#[cfg(feature = "matching")]
34use super::{Pattern, PatternBuilder, PatternError};
35use crate::meta_var::{MetaVariable, extract_meta_var};
36use std::borrow::Cow;
37use std::path::Path;
38
39/// Trait to abstract ts-language usage in ast-grep, which includes:
40/// * which character is used for meta variable.
41/// * if we need to use other char in meta var for parser at runtime
42/// * pre process the Pattern code.
43pub trait Language: Clone + std::fmt::Debug + Send + Sync + 'static {
44 /// normalize pattern code before matching
45 /// e.g. remove `expression_statement`, or prefer parsing {} to object over block
46 fn pre_process_pattern<'q>(&self, query: &'q str) -> Cow<'q, str> {
47 Cow::Borrowed(query)
48 }
49
50 /// Configure meta variable special character
51 /// By default $ is the metavar char, but in PHP it can be #
52 #[inline]
53 fn meta_var_char(&self) -> char {
54 '$'
55 }
56
57 /// Some language does not accept $ as the leading char for identifiers.
58 /// We need to change $ to other char at run-time to make parser happy, thus the name expando.
59 /// By default this is the same as `meta_var` char so replacement is done at runtime.
60 #[inline]
61 fn expando_char(&self) -> char {
62 self.meta_var_char()
63 }
64
65 /// extract `MetaVariable` from a given source string
66 /// At runtime we need to use `expand_char`
67 fn extract_meta_var(&self, source: &str) -> Option<MetaVariable> {
68 extract_meta_var(source, self.expando_char())
69 }
70 /// Return the file language from path. Return None if the file type is not supported.
71 fn from_path<P: AsRef<Path>>(_path: P) -> Option<Self> {
72 // TODO: throw panic here if not implemented properly?
73 None
74 }
75
76 fn kind_to_id(&self, kind: &str) -> u16;
77 fn field_to_id(&self, field: &str) -> Option<u16>;
78 #[cfg(feature = "matching")]
79 fn build_pattern(&self, builder: &PatternBuilder) -> Result<Pattern, PatternError>;
80}
81
82#[cfg(test)]
83pub use test::*;
84
85#[cfg(test)]
86mod test {
87 use super::*;
88 use crate::tree_sitter::{LanguageExt, StrDoc, TSLanguage};
89
90 #[derive(Clone, Debug)]
91 pub struct Tsx;
92 impl Language for Tsx {
93 fn kind_to_id(&self, kind: &str) -> u16 {
94 let ts_lang: TSLanguage = tree_sitter_typescript::LANGUAGE_TSX.into();
95 ts_lang.id_for_node_kind(kind, /* named */ true)
96 }
97 fn field_to_id(&self, field: &str) -> Option<u16> {
98 self.get_ts_language()
99 .field_id_for_name(field)
100 .map(|f| f.get())
101 }
102 fn build_pattern(&self, builder: &PatternBuilder) -> Result<Pattern, PatternError> {
103 builder.build(|src| StrDoc::try_new(src, self.clone()))
104 }
105 }
106 impl LanguageExt for Tsx {
107 fn get_ts_language(&self) -> TSLanguage {
108 tree_sitter_typescript::LANGUAGE_TSX.into()
109 }
110 }
111}