Skip to main content

comindly_core/lang/
mod.rs

1//! Per-language syntax adapters. The generic walk in [`crate::parse`] asks each question of the
2//! file's language; adding a language means implementing [`LangSpec`] for its grammar.
3
4pub mod go;
5pub mod java;
6pub mod kotlin;
7pub mod python;
8pub mod rust;
9pub mod spring;
10pub mod typescript;
11
12use tree_sitter::Node;
13
14use crate::facts::{Args, Import, RefKind, TypeSource, ValueExpr};
15use crate::model::{Language, SymbolKind};
16
17/// What an assignment's left-hand side names.
18pub enum Target {
19    /// A local or module variable.
20    Var(String),
21    /// An attribute on the instance (`self.x` / `this.x`).
22    Attr(String),
23}
24
25/// Service-boundary syntax found at a node; the walk fills in scope ids.
26pub enum RawContract {
27    Route {
28        handler: String,
29        receiver: Vec<String>,
30        method: String,
31        path: ValueExpr,
32        client: bool,
33    },
34    Handle {
35        receiver: Vec<String>,
36        method: String,
37        path: ValueExpr,
38        handler: Vec<String>,
39    },
40    Construct {
41        target: Target,
42        ctor: Vec<String>,
43        args: Args,
44    },
45    Op {
46        receiver: Vec<String>,
47        method: String,
48        args: Args,
49    },
50    TableName(String),
51}
52
53pub trait LangSpec: Sync {
54    /// Receiver name inside methods (`self`, `this`).
55    fn receiver(&self) -> &'static str;
56
57    /// A definition node: its kind and name. `in_class` is true directly inside a class body.
58    fn def(&self, node: Node, src: &[u8], in_class: bool) -> Option<(SymbolKind, String)>;
59
60    /// A base declared apart from the type (Rust `impl Trait for Type`): (type name, kind, base).
61    fn owned_bases(&self, _node: Node, _src: &[u8]) -> Option<(String, RefKind, Vec<String>)> {
62        None
63    }
64
65    /// Contract syntax at this node (routes, client construction, service operations).
66    fn contract(&self, _node: Node, _src: &[u8]) -> Option<RawContract> {
67        None
68    }
69
70    /// Every contract at this node (a router line can register several verbs).
71    fn contracts(&self, node: Node, src: &[u8]) -> Vec<RawContract> {
72        self.contract(node, src).into_iter().collect()
73    }
74
75    /// Parameter names of a callable in order, receiver excluded.
76    fn param_names(&self, _def: Node, _src: &[u8]) -> Vec<String> {
77        Vec::new()
78    }
79
80    /// A call with at least one statically readable argument: callee path and arguments.
81    fn call(&self, _node: Node, _src: &[u8]) -> Option<(Vec<String>, Args)> {
82        None
83    }
84
85    /// `name = <statically known value>` at module or class level.
86    fn value(&self, _node: Node, _src: &[u8]) -> Option<(String, ValueExpr)> {
87        None
88    }
89
90    /// The package a file declares (JVM); `None` derives it from the path instead.
91    fn package(&self, _root: Node, _src: &[u8]) -> Option<String> {
92        None
93    }
94
95    /// The type a definition declared outside it belongs to (Go method receivers).
96    fn owner(&self, _def: Node, _src: &[u8]) -> Option<String> {
97        None
98    }
99
100    /// A module-level variable binding's name (only asked at module scope).
101    fn variable(&self, node: Node, src: &[u8]) -> Option<String>;
102
103    /// `Some` for an import/re-export statement (not descended into), possibly empty.
104    fn import(&self, node: Node, src: &[u8]) -> Option<Vec<Import>>;
105
106    /// A call or construction and the dotted path of its callee.
107    fn reference(&self, node: Node, src: &[u8]) -> Option<(RefKind, Vec<String>)>;
108
109    /// An assignment (or field declaration) whose target type can be inferred.
110    fn assignment(&self, node: Node, src: &[u8]) -> Option<(Target, TypeSource)>;
111
112    /// Base classes / implemented interfaces of a class-like definition.
113    fn bases(&self, def: Node, src: &[u8]) -> Vec<(RefKind, Vec<String>)>;
114
115    /// Declared return type of a callable.
116    fn returns(&self, def: Node, src: &[u8]) -> Option<Vec<String>>;
117
118    /// Typed parameters of a callable; `true` marks constructor parameter properties (TS).
119    fn params(&self, def: Node, src: &[u8]) -> Vec<(String, Vec<String>, bool)>;
120}
121
122pub fn spec(lang: &Language) -> Option<&'static dyn LangSpec> {
123    match lang {
124        Language::Python => Some(&python::Python),
125        Language::TypeScript | Language::JavaScript => Some(&typescript::TypeScript),
126        Language::Go => Some(&go::Go),
127        Language::Java => Some(&java::Java),
128        Language::Kotlin => Some(&kotlin::Kotlin),
129        Language::Rust => Some(&rust::Rust),
130        _ => None,
131    }
132}
133
134pub(crate) fn text<'a>(node: Node, src: &'a [u8]) -> &'a str {
135    node.utf8_text(src).unwrap_or("")
136}
137
138pub(crate) fn field_text<'a>(node: Node, field: &str, src: &'a [u8]) -> Option<&'a str> {
139    node.child_by_field_name(field).map(|n| text(n, src))
140}
141
142pub(crate) fn named_children(node: Node) -> Vec<Node> {
143    let mut c = node.walk();
144    node.named_children(&mut c).collect()
145}
146
147pub(crate) fn line(node: Node) -> u32 {
148    node.start_position().row as u32 + 1
149}