Skip to main content

xidl_parser/typed_ast/
annotation.rs

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