xidl_parser/hir/annotation/
convert.rs1use 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) = ¶ms {
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) = ¶ms {
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}