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