Skip to main content

xidl_parser/hir/annotation/
convert.rs

1use super::super::*;
2use super::params::{parse_raw_annotation_params, trim_annotation_quotes};
3use super::{Annotation, AnnotationParam, AnnotationParams, RenameRule};
4use crate::lint::{AnnotationDefinition, AnnotationKind};
5use convert_case::{Case, Casing};
6use std::collections::HashMap;
7
8pub fn annotation_name(annotation: &Annotation) -> Option<&str> {
9    let name = match annotation {
10        Annotation::Builtin { name, .. } => Some(name.as_str()),
11        Annotation::ScopedName { name, .. } => name.name.last().map(|value| value.as_str()),
12        _ => None,
13    }?;
14    Some(AnnotationDefinition::lookup(name).map_or(name, |entry| entry.name))
15}
16
17pub fn annotation_params(annotation: &Annotation) -> Option<&AnnotationParams> {
18    match annotation {
19        Annotation::Builtin { params, .. } => params.as_ref(),
20        Annotation::ScopedName { params, .. } => params.as_ref(),
21        _ => None,
22    }
23}
24
25pub fn normalize_annotation_params(params: &AnnotationParams) -> HashMap<String, String> {
26    let mut out = HashMap::new();
27    match params {
28        AnnotationParams::Raw(value) => {
29            let parsed = parse_raw_annotation_params(value);
30            if parsed.is_empty() {
31                out.insert(
32                    "value".to_string(),
33                    trim_annotation_quotes(value).unwrap_or_else(|| value.clone()),
34                );
35            }
36            for (key, value) in parsed {
37                out.insert(key.to_ascii_lowercase(), value);
38            }
39        }
40        AnnotationParams::Params(values) => {
41            for value in values {
42                let raw = value
43                    .value
44                    .as_ref()
45                    .map(render_annotation_const_expr)
46                    .unwrap_or_default();
47                out.insert(
48                    value.ident.to_ascii_lowercase(),
49                    trim_annotation_quotes(&raw).unwrap_or(raw),
50                );
51            }
52        }
53        AnnotationParams::ConstExpr(expr) => {
54            let rendered = render_annotation_const_expr(expr);
55            out.insert(
56                "value".to_string(),
57                trim_annotation_quotes(&rendered).unwrap_or(rendered),
58            );
59        }
60        AnnotationParams::Positional(values) => {
61            let rendered = values
62                .iter()
63                .map(render_annotation_const_expr)
64                .collect::<Vec<_>>()
65                .join(", ");
66            out.insert(
67                "value".to_string(),
68                trim_annotation_quotes(&rendered).unwrap_or(rendered),
69            );
70        }
71    }
72    out
73}
74
75pub fn is_skipped(annotations: &[Annotation]) -> bool {
76    annotations.iter().any(|a| matches!(a, Annotation::Skip))
77}
78
79pub fn field_rename(annotations: &[Annotation]) -> Option<String> {
80    annotations.iter().find_map(|a| {
81        if let Annotation::Rename { name } = a {
82            Some(name.clone())
83        } else {
84            None
85        }
86    })
87}
88
89pub fn rename_all(annotations: &[Annotation]) -> Option<RenameRule> {
90    annotations.iter().find_map(|a| {
91        if let Annotation::RenameAll { rule } = a {
92            Some(rule.clone())
93        } else {
94            None
95        }
96    })
97}
98
99pub fn effective_wire_name(
100    raw_name: &str,
101    annotations: &[Annotation],
102    container_annotations: &[Annotation],
103) -> String {
104    field_rename(annotations).unwrap_or_else(|| {
105        if let Some(rule) = rename_all(container_annotations) {
106            apply_rename_rule(raw_name, rule)
107        } else {
108            raw_name.to_string()
109        }
110    })
111}
112
113pub fn annotation_id_value(annotations: &[Annotation]) -> Option<u32> {
114    for annotation in annotations {
115        if let Annotation::Id { value } = annotation {
116            if let Ok(value) = value.parse::<u32>() {
117                return Some(value);
118            }
119        }
120    }
121    None
122}
123
124pub fn expand_annotations(values: Vec<crate::typed_ast::AnnotationAppl>) -> Vec<Annotation> {
125    let mut out = Vec::new();
126    for value in values {
127        push_annotation(&mut out, value);
128    }
129    out
130}
131
132fn push_annotation(out: &mut Vec<Annotation>, mut value: crate::typed_ast::AnnotationAppl) {
133    let extra = std::mem::take(&mut value.extra);
134    out.push(Annotation::from(value));
135    for item in extra {
136        push_annotation(out, item);
137    }
138}
139
140impl From<crate::typed_ast::AnnotationAppl> for Annotation {
141    fn from(mut value: crate::typed_ast::AnnotationAppl) -> Self {
142        match &mut value.name {
143            crate::typed_ast::AnnotationName::Builtin(name) => {
144                if let Some(definition) = AnnotationDefinition::lookup(name) {
145                    *name = definition.name.to_string();
146                }
147            }
148            crate::typed_ast::AnnotationName::ScopedName(name)
149                if name.scoped_name.is_none() && !name.node_text.starts_with("::") =>
150            {
151                if let Some(definition) = AnnotationDefinition::lookup(&name.identifier.0) {
152                    name.identifier.0 = definition.name.to_string();
153                }
154            }
155            _ => {}
156        }
157        let params = value.params.map(Into::into);
158
159        let name_ref = match &value.name {
160            crate::typed_ast::AnnotationName::ScopedName(name) => Some(name.identifier.0.as_str()),
161            crate::typed_ast::AnnotationName::Builtin(name) => Some(name.as_str()),
162        };
163
164        if let Some(name) = name_ref {
165            match AnnotationKind::from_name(name) {
166                Some(AnnotationKind::Rename | AnnotationKind::Name) => {
167                    if let Some(p) = &params {
168                        let normalized = normalize_annotation_params(p);
169                        if let Some(val) =
170                            normalized.get("value").or_else(|| normalized.get("name"))
171                        {
172                            return Self::Rename { name: val.clone() };
173                        }
174                    }
175                }
176                Some(AnnotationKind::RenameAll) => {
177                    if let Some(p) = &params {
178                        let normalized = normalize_annotation_params(p);
179                        if let Some(val) =
180                            normalized.get("rule").or_else(|| normalized.get("value"))
181                        {
182                            return Self::RenameAll {
183                                rule: val.parse().unwrap_or(RenameRule::None),
184                            };
185                        }
186                    }
187                }
188                Some(AnnotationKind::Skip) => return Self::Skip,
189                Some(AnnotationKind::Version) => {
190                    if params.is_none() {
191                        return Self::Version;
192                    }
193                    if let Some(parameter) = params
194                        .as_ref()
195                        .and_then(|params| super::VersionParameter::parse(params).ok())
196                    {
197                        return Self::VersionParameter(parameter);
198                    }
199                }
200                Some(AnnotationKind::Added | AnnotationKind::Removed) => {
201                    if let Some(AnnotationParams::ConstExpr(ConstExpr::ScopedName(version))) =
202                        params.as_ref()
203                    {
204                        let version = version.clone();
205                        return match AnnotationKind::from_name(name) {
206                            Some(AnnotationKind::Added) => Self::Added { version },
207                            _ => Self::Removed { version },
208                        };
209                    }
210                }
211                _ => {}
212            }
213        }
214
215        match value.name {
216            crate::typed_ast::AnnotationName::ScopedName(name) => Self::ScopedName {
217                name: name.into(),
218                params,
219            },
220            crate::typed_ast::AnnotationName::Builtin(name) => match value.builtin {
221                Some(builtin) => super::super::annotation_builtin::from_builtin_annotation(builtin)
222                    .unwrap_or(Self::Builtin { name, params }),
223                None => Self::Builtin { name, params },
224            },
225        }
226    }
227}
228
229impl From<crate::typed_ast::AnnotationParams> for AnnotationParams {
230    fn from(value: crate::typed_ast::AnnotationParams) -> Self {
231        match value {
232            crate::typed_ast::AnnotationParams::Params(params) => {
233                let mut positional = Vec::new();
234                let mut named = Vec::new();
235                for param in params {
236                    match param {
237                        crate::typed_ast::AnnotationApplParam::Positional(expr) => {
238                            positional.push(expr.into());
239                        }
240                        crate::typed_ast::AnnotationApplParam::Named { ident, value } => {
241                            named.push(AnnotationParam {
242                                ident: ident.0,
243                                value: Some(value.into()),
244                            });
245                        }
246                    }
247                }
248                if !positional.is_empty() && named.is_empty() {
249                    if positional.len() == 1 {
250                        Self::ConstExpr(positional.remove(0))
251                    } else {
252                        Self::Positional(positional)
253                    }
254                } else {
255                    Self::Params(named)
256                }
257            }
258            crate::typed_ast::AnnotationParams::Raw(value) => Self::Raw(value),
259        }
260    }
261}
262
263pub(super) fn apply_rename_rule(raw_name: &str, rule: RenameRule) -> String {
264    match rule {
265        RenameRule::None => raw_name.to_string(),
266        RenameRule::LowerCase => raw_name.to_case(Case::Flat),
267        RenameRule::UpperCase => raw_name.to_case(Case::UpperFlat),
268        RenameRule::PascalCase => raw_name.to_case(Case::Pascal),
269        RenameRule::CamelCase => raw_name.to_case(Case::Camel),
270        RenameRule::SnakeCase => raw_name.to_case(Case::Snake),
271        RenameRule::ScreamingSnakeCase => raw_name.to_case(Case::UpperSnake),
272        RenameRule::KebabCase => raw_name.to_case(Case::Kebab),
273        RenameRule::ScreamingKebabCase => raw_name.to_case(Case::Cobol),
274    }
275}
276
277pub(super) fn render_annotation_const_expr(expr: &ConstExpr) -> String {
278    match expr {
279        ConstExpr::ScopedName(value) => {
280            let prefix = if value.is_root { "::" } else { "" };
281            format!("{prefix}{}", value.name.join("::"))
282        }
283        ConstExpr::Literal(value) => render_annotation_literal(value),
284        ConstExpr::UnaryExpr(op, value) => {
285            let op = match op {
286                UnaryOperator::Add => "+",
287                UnaryOperator::Sub => "-",
288                UnaryOperator::Not => "~",
289            };
290            format!("({op}{})", render_annotation_const_expr(value))
291        }
292        ConstExpr::BinaryExpr(op, left, right) => {
293            let op = match op {
294                BinaryOperator::Or => "|",
295                BinaryOperator::Xor => "^",
296                BinaryOperator::And => "&",
297                BinaryOperator::LeftShift => "<<",
298                BinaryOperator::RightShift => ">>",
299                BinaryOperator::Add => "+",
300                BinaryOperator::Sub => "-",
301                BinaryOperator::Mult => "*",
302                BinaryOperator::Div => "/",
303                BinaryOperator::Mod => "%",
304            };
305            format!(
306                "({} {op} {})",
307                render_annotation_const_expr(left),
308                render_annotation_const_expr(right)
309            )
310        }
311    }
312}
313
314fn render_annotation_literal(value: &Literal) -> String {
315    match value {
316        Literal::IntegerLiteral(IntegerLiteral(value)) => value.clone(),
317        Literal::FloatingPtLiteral(value) => {
318            let sign = value.sign.as_ref().map(IntegerSign::as_str).unwrap_or("");
319            format!("{}{}.{}", sign, value.integer.0, value.fraction.0)
320        }
321        Literal::CharLiteral(value)
322        | Literal::WideCharacterLiteral(value)
323        | Literal::StringLiteral(value)
324        | Literal::WideStringLiteral(value) => value.clone(),
325        Literal::BooleanLiteral(value) => value.to_string(),
326    }
327}