Skip to main content

xidl_parser/typed_ast/
annotation.rs

1use super::*;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct AnnotationAppl {
6    pub name: AnnotationName,
7    pub params: Option<AnnotationParams>,
8    pub builtin: Option<BuiltinAnnotation>,
9    pub is_extend: bool,
10    pub extra: Vec<AnnotationAppl>,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub enum AnnotationName {
15    ScopedName(ScopedName),
16    Builtin(String),
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub enum AnnotationParams {
21    Params(Vec<AnnotationApplParam>),
22    Raw(String),
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub enum AnnotationApplParam {
27    Positional(ConstExpr),
28    Named { ident: Identifier, value: ConstExpr },
29}
30
31impl<'a> crate::parser::FromTreeSitter<'a> for AnnotationAppl {
32    fn from_node(
33        node: tree_sitter::Node<'a>,
34        ctx: &mut crate::parser::ParseContext<'a>,
35    ) -> crate::error::ParserResult<Self> {
36        let kind_id = node.kind_id();
37        let is_extend = kind_id == xidl_parser_derive::node_id!("extend_annotation_appl");
38        if !is_extend {
39            assert_eq!(kind_id, xidl_parser_derive::node_id!("annotation_appl"));
40        }
41        let raw = ctx.node_text(&node)?.to_string();
42
43        let mut custom_body = None;
44        let mut builtin_body = None;
45        let mut extra = Vec::new();
46        for ch in node.children(&mut node.walk()) {
47            match ch.kind_id() {
48                xidl_parser_derive::node_id!("annotation_appl_custom_body") => {
49                    custom_body = Some(ch);
50                }
51                xidl_parser_derive::node_id!("annotation_appl_builtin_body") => {
52                    builtin_body = Some(ch);
53                }
54                xidl_parser_derive::node_id!("annotation_appl")
55                | xidl_parser_derive::node_id!("extend_annotation_appl") => {
56                    extra.push(AnnotationAppl::from_node(ch, ctx)?);
57                }
58                _ => {}
59            }
60        }
61
62        if requires_raw_annotation_parse(&raw) {
63            return parse_annotation_from_raw(&raw, is_extend, extra);
64        }
65
66        if let Some(custom_body) = custom_body {
67            let mut scoped_name = None;
68            let mut params = None;
69            for ch in custom_body.children(&mut custom_body.walk()) {
70                match ch.kind_id() {
71                    xidl_parser_derive::node_id!("scoped_name") => {
72                        scoped_name = Some(ScopedName::from_node(ch, ctx)?);
73                    }
74                    xidl_parser_derive::node_id!("annotation_appl_params") => {
75                        params = Some(AnnotationParams::from_node(ch, ctx)?);
76                    }
77                    _ => {}
78                }
79            }
80            let scoped_name = scoped_name.ok_or_else(|| {
81                crate::error::ParseError::UnexpectedNode(
82                    "annotation_appl_custom_body missing scoped_name".to_string(),
83                )
84            })?;
85            return Ok(Self {
86                name: AnnotationName::ScopedName(scoped_name),
87                params,
88                builtin: None,
89                is_extend,
90                extra,
91            });
92        }
93
94        let builtin = builtin_body
95            .map(|node| parse_builtin_annotation(node, ctx))
96            .transpose()?;
97        let source = builtin_body
98            .map(|node| ctx.node_text(&node))
99            .transpose()?
100            .unwrap_or(raw.as_str());
101        let source = source.trim().strip_prefix("//@").unwrap_or(source);
102        let raw = source.trim().strip_prefix('@').unwrap_or(source).trim();
103        let (name, args) = match raw.split_once('(') {
104            Some((name, rest)) => {
105                let args = rest.strip_suffix(')').unwrap_or(rest).trim();
106                (name.trim(), Some(args))
107            }
108            None => (raw, None),
109        };
110
111        Ok(Self {
112            name: AnnotationName::Builtin(name.to_string()),
113            params: args.map(|value| AnnotationParams::Raw(value.to_string())),
114            builtin,
115            is_extend,
116            extra,
117        })
118    }
119}
120
121fn requires_raw_annotation_parse(raw: &str) -> bool {
122    let source = raw.trim();
123    let source = source.strip_prefix("//@").unwrap_or(source);
124    let source = source.strip_prefix('@').unwrap_or(source);
125    let name = source
126        .split_once('(')
127        .map(|(name, _)| name)
128        .unwrap_or(source);
129    name.contains('-') || source.contains('[') || source.contains(']')
130}
131
132fn parse_annotation_from_raw(
133    raw: &str,
134    is_extend: bool,
135    extra: Vec<AnnotationAppl>,
136) -> crate::error::ParserResult<AnnotationAppl> {
137    let source = raw.trim();
138    let source = source.strip_prefix("//@").unwrap_or(source);
139    let source = source.strip_prefix('@').unwrap_or(source).trim();
140    let (name, args) = match source.split_once('(') {
141        Some((name, rest)) => {
142            let args = rest.strip_suffix(')').unwrap_or(rest).trim();
143            (name.trim(), Some(args))
144        }
145        None => (source, None),
146    };
147    Ok(AnnotationAppl {
148        name: AnnotationName::Builtin(name.to_string()),
149        params: args.map(|value| AnnotationParams::Raw(value.to_string())),
150        builtin: None,
151        is_extend,
152        extra,
153    })
154}
155
156impl AnnotationAppl {
157    pub fn doc(text: String) -> Self {
158        let escaped = escape_doc_text(&text);
159        Self {
160            name: AnnotationName::Builtin("doc".to_string()),
161            params: Some(AnnotationParams::Raw(format!("\"{}\"", escaped))),
162            builtin: None,
163            is_extend: false,
164            extra: Vec::new(),
165        }
166    }
167}
168
169fn escape_doc_text(text: &str) -> String {
170    let mut out = String::with_capacity(text.len());
171    for ch in text.chars() {
172        match ch {
173            '\\' => out.push_str("\\\\"),
174            '"' => out.push_str("\\\""),
175            '\n' => out.push_str("\\n"),
176            '\r' => {}
177            _ => out.push(ch),
178        }
179    }
180    out
181}
182
183impl<'a> crate::parser::FromTreeSitter<'a> for AnnotationParams {
184    fn from_node(
185        node: tree_sitter::Node<'a>,
186        ctx: &mut crate::parser::ParseContext<'a>,
187    ) -> crate::error::ParserResult<Self> {
188        assert_eq!(
189            node.kind_id(),
190            xidl_parser_derive::node_id!("annotation_appl_params")
191        );
192
193        let mut params = vec![];
194        for ch in node.children(&mut node.walk()) {
195            match ch.kind_id() {
196                xidl_parser_derive::node_id!("const_expr") => {
197                    return Ok(Self::Params(vec![AnnotationApplParam::Positional(
198                        ConstExpr::from_node(ch, ctx)?,
199                    )]));
200                }
201                xidl_parser_derive::node_id!("annotation_appl_param") => {
202                    params.push(AnnotationApplParam::from_node(ch, ctx)?);
203                }
204                _ => {}
205            }
206        }
207
208        if !params.is_empty() {
209            return Ok(Self::Params(params));
210        }
211
212        Err(crate::error::ParseError::UnexpectedNode(
213            "annotation_appl_params missing content".to_string(),
214        ))
215    }
216}
217
218impl<'a> crate::parser::FromTreeSitter<'a> for AnnotationApplParam {
219    fn from_node(
220        node: tree_sitter::Node<'a>,
221        ctx: &mut crate::parser::ParseContext<'a>,
222    ) -> crate::error::ParserResult<Self> {
223        assert_eq!(
224            node.kind_id(),
225            xidl_parser_derive::node_id!("annotation_appl_param")
226        );
227
228        let mut ident = None;
229        let mut value = None;
230        for ch in node.children(&mut node.walk()) {
231            match ch.kind_id() {
232                xidl_parser_derive::node_id!("identifier") => {
233                    ident = Some(Identifier::from_node(ch, ctx)?);
234                }
235                xidl_parser_derive::node_id!("const_expr") => {
236                    value = Some(ConstExpr::from_node(ch, ctx)?);
237                }
238                _ => {}
239            }
240        }
241
242        match (ident, value) {
243            (None, Some(value)) => Ok(Self::Positional(value)),
244            (Some(ident), Some(value)) => Ok(Self::Named { ident, value }),
245            _ => Err(crate::error::ParseError::UnexpectedNode(
246                "annotation_appl_param missing const_expr".to_string(),
247            )),
248        }
249    }
250}