rust_code_analysis/
ast.rs

1use serde::ser::{SerializeStruct, Serializer};
2use serde::{Deserialize, Serialize};
3
4use crate::*;
5
6/// Start and end positions of a node in a code in terms of rows and columns.
7///
8/// The first and second fields represent the row and column associated to
9/// the start position of a node.
10///
11/// The third and fourth fields represent the row and column associated to
12/// the end position of a node.
13pub type Span = Option<(usize, usize, usize, usize)>;
14
15/// The payload of an `Ast` request.
16#[derive(Debug, Deserialize, Serialize)]
17pub struct AstPayload {
18    /// The id associated to a request for an `AST`
19    pub id: String,
20    /// The filename associated to a source code file
21    pub file_name: String,
22    /// The code to be represented as an `AST`
23    pub code: String,
24    /// If `true`, nodes representing comments are ignored
25    pub comment: bool,
26    /// If `true`, the start and end positions of a node in a code
27    /// are considered
28    pub span: bool,
29}
30
31/// The response of an `AST` request.
32#[derive(Debug, Serialize)]
33pub struct AstResponse {
34    /// The id associated to a request for an `AST`
35    pub id: String,
36    /// The root node of an `AST`
37    ///
38    /// If `None`, an error has occurred
39    pub root: Option<AstNode>,
40}
41
42/// Information on an `AST` node.
43#[derive(Debug)]
44pub struct AstNode {
45    /// The type of node
46    pub r#type: &'static str,
47    /// The code associated to a node
48    pub value: String,
49    /// The start and end positions of a node in a code
50    pub span: Span,
51    /// The children of a node
52    pub children: Vec<AstNode>,
53}
54
55impl Serialize for AstNode {
56    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
57    where
58        S: Serializer,
59    {
60        let mut st = serializer.serialize_struct("Node", 4)?;
61        st.serialize_field("Type", &self.r#type)?;
62        st.serialize_field("TextValue", &self.value)?;
63        st.serialize_field("Span", &self.span)?;
64        st.serialize_field("Children", &self.children)?;
65        st.end()
66    }
67}
68
69impl AstNode {
70    pub fn new(r#type: &'static str, value: String, span: Span, children: Vec<AstNode>) -> Self {
71        Self {
72            r#type,
73            value,
74            span,
75            children,
76        }
77    }
78}
79
80fn build<T: ParserTrait>(parser: &T, span: bool, comment: bool) -> Option<AstNode> {
81    let code = parser.get_code();
82    let root = parser.get_root();
83    let mut cursor = root.object().walk();
84    let mut node_stack = Vec::new();
85    let mut child_stack = Vec::new();
86
87    node_stack.push(root);
88    child_stack.push(Vec::new());
89
90    /* To avoid Rc, RefCell and stuff like that (or use of unsafe)
91    the idea here is to build AstNode from bottom-to-top and from left-to-right.
92    So once we have built the array of children we can build the node itself until the root. */
93    loop {
94        let ts_node = node_stack.last().unwrap();
95        cursor.reset(ts_node.object());
96        if cursor.goto_first_child() {
97            let node = cursor.node();
98            child_stack.push(Vec::with_capacity(node.child_count()));
99            node_stack.push(Node::new(node));
100        } else {
101            loop {
102                let ts_node = node_stack.pop().unwrap();
103                if let Some(node) = T::Checker::get_ast_node(
104                    &ts_node,
105                    code,
106                    child_stack.pop().unwrap(),
107                    span,
108                    comment,
109                ) {
110                    if !child_stack.is_empty() {
111                        child_stack.last_mut().unwrap().push(node);
112                    } else {
113                        return Some(node);
114                    }
115                }
116                if let Some(next_node) = ts_node.object().next_sibling() {
117                    child_stack.push(Vec::with_capacity(next_node.child_count()));
118                    node_stack.push(Node::new(next_node));
119                    break;
120                }
121            }
122        }
123    }
124}
125
126pub struct AstCallback {
127    _guard: (),
128}
129
130/// Configuration options for retrieving the nodes of an `AST`.
131pub struct AstCfg {
132    /// The id associated to a request for an `AST`
133    pub id: String,
134    /// If `true`, nodes representing comments are ignored
135    pub comment: bool,
136    /// If `true`, the start and end positions of a node in a code
137    /// are considered
138    pub span: bool,
139}
140
141impl Callback for AstCallback {
142    type Res = AstResponse;
143    type Cfg = AstCfg;
144
145    fn call<T: ParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res {
146        AstResponse {
147            id: cfg.id,
148            root: build(parser, cfg.span, cfg.comment),
149        }
150    }
151}
152/* where T::Checker: Alterator*/