⎇ 53dbdf0f1deea10d6fc567ea2b43e1e75d6d60cd
crates/modules/semantic_dht/minga-core/src/ast.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
chore: monorepo inicial con arje + minga + yahweh absorbidos
1use tree_sitter::Node;
2
3/// Nodo de AST normalizado: descarta posiciones, whitespace y trivia
4/// (comentarios marcados como `extra` en la gramática). Dos fragmentos de
5/// código semánticamente equivalentes producen árboles idénticos.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct SemanticNode {
8 pub kind: String,
9 pub field_name: Option<String>,
10 pub leaf_text: Option<Vec<u8>>,
11 pub children: Vec<SemanticNode>,
12}
13
14impl SemanticNode {
15 pub fn from_tree_sitter(node: Node<'_>, source: &[u8]) -> Self {
16 Self::build(node, source, None)
17 }
18
19 fn build(node: Node<'_>, source: &[u8], field_name: Option<String>) -> Self {
20 let kind = node.kind().to_string();
21 let mut children = Vec::new();
22
23 // Incluimos todos los hijos no-`extra`: nombrados (rules de la
24 // gramática) y anónimos (tokens literales como operadores y
25 // separadores). Lo único que descartamos son `extras` —
26 // comentarios y whitespace en gramáticas tree-sitter — que es
27 // exactamente la invariancia que queremos: dos formas con el
28 // mismo contenido y estructura producen el mismo árbol.
29 let mut cursor = node.walk();
30 if cursor.goto_first_child() {
31 loop {
32 let child = cursor.node();
33 if !child.is_extra() {
34 let field = cursor.field_name().map(|s| s.to_string());
35 children.push(Self::build(child, source, field));
36 }
37 if !cursor.goto_next_sibling() {
38 break;
39 }
40 }
41 }
42
43 let leaf_text = if children.is_empty() {
44 let range = node.byte_range();
45 Some(source[range].to_vec())
46 } else {
47 None
48 };
49
50 SemanticNode { kind, field_name, leaf_text, children }
51 }
52}