Skip to main content

veryl_analyzer/
attribute.rs

1use std::cell::RefCell;
2use std::fmt;
3use strum::IntoEnumIterator;
4use strum_macros::EnumIter;
5use veryl_parser::resource_table::{self, StrId};
6use veryl_parser::veryl_token::Token;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub enum Attribute {
10    Ifdef(StrId),
11    Ifndef(StrId),
12    Elsif(StrId, Vec<StrId>, Vec<StrId>),
13    Else(Vec<StrId>, Vec<StrId>),
14    Sv(StrId),
15    Allow(AllowItem),
16    EnumEncoding(EnumEncodingItem),
17    EnumMemberPrefix(StrId),
18    Test(Token, Option<StrId>),
19    CondType(CondTypeItem),
20    Align(Vec<AlignItem>),
21    Format(Vec<FormatItem>),
22    Expand(Vec<ExpandItem>),
23    Ignore,
24}
25
26impl Attribute {
27    pub fn is_align(&self, item: AlignItem) -> bool {
28        if let Attribute::Align(x) = self {
29            x.contains(&item)
30        } else {
31            false
32        }
33    }
34
35    pub fn is_format(&self, item: FormatItem) -> bool {
36        if let Attribute::Format(x) = self {
37            x.contains(&item)
38        } else {
39            false
40        }
41    }
42
43    pub fn is_ifdef(&self) -> bool {
44        matches!(
45            self,
46            Attribute::Ifdef(_)
47                | Attribute::Ifndef(_)
48                | Attribute::Elsif(_, _, _)
49                | Attribute::Else(_, _)
50        )
51    }
52
53    pub fn is_expand(&self, item: ExpandItem) -> bool {
54        if let Attribute::Expand(x) = self {
55            x.contains(&item)
56        } else {
57            false
58        }
59    }
60}
61
62impl fmt::Display for Attribute {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        let text = match self {
65            Attribute::Ifdef(x) => format!("ifdef({x})"),
66            Attribute::Ifndef(x) => format!("ifndef({x})"),
67            Attribute::Elsif(x, _, _) => format!("elsif({x})"),
68            Attribute::Else(_, _) => String::from("else"),
69            Attribute::Sv(x) => format!("sv(\"{x}\")"),
70            Attribute::Allow(x) => format!("allow({x})"),
71            Attribute::EnumEncoding(x) => format!("enum_encoding({x})"),
72            Attribute::EnumMemberPrefix(x) => format!("enum_member_prefix({x})"),
73            Attribute::Test(x, _) => format!("test({})", x.text),
74            Attribute::CondType(x) => format!("cond_type({x})"),
75            Attribute::Align(x) => {
76                let mut arg = String::new();
77                for x in x {
78                    arg.push_str(&format!("{x}, "));
79                }
80                format!("align({arg})")
81            }
82            Attribute::Format(x) => {
83                let mut arg = String::new();
84                for x in x {
85                    arg.push_str(&format!("{x}, "));
86                }
87                format!("format({arg})")
88            }
89            Attribute::Expand(x) => {
90                let mut arg = String::new();
91                for x in x {
92                    arg.push_str(&format!("{x}, "));
93                }
94                format!("expand({arg})")
95            }
96            Attribute::Ignore => String::from("ignore"),
97        };
98        text.fmt(f)
99    }
100}
101
102#[derive(Clone, Debug)]
103pub enum AttributeError {
104    UnknownAttribute,
105    MismatchArgs(String),
106}
107
108fn get_arg_ident(
109    args: &Option<veryl_parser::veryl_grammar_trait::AttributeOpt>,
110    pos: usize,
111) -> Option<Token> {
112    use veryl_parser::veryl_grammar_trait as g;
113
114    if let Some(x) = args {
115        let args: Vec<_> = x.attribute_list.as_ref().into();
116        if args.len() <= pos {
117            None
118        } else if let g::AttributeItem::Identifier(x) = args[pos] {
119            Some(x.identifier.identifier_token.token)
120        } else {
121            None
122        }
123    } else {
124        None
125    }
126}
127
128fn get_arg_string(
129    args: &Option<veryl_parser::veryl_grammar_trait::AttributeOpt>,
130    pos: usize,
131) -> Option<Token> {
132    use veryl_parser::veryl_grammar_trait as g;
133
134    if let Some(x) = args {
135        let args: Vec<_> = x.attribute_list.as_ref().into();
136        if args.len() <= pos {
137            None
138        } else if let g::AttributeItem::StringLiteral(x) = &args[pos] {
139            Some(x.string_literal.string_literal_token.token)
140        } else {
141            None
142        }
143    } else {
144        None
145    }
146}
147
148fn get_args_ident(args: &Option<veryl_parser::veryl_grammar_trait::AttributeOpt>) -> Vec<Token> {
149    use veryl_parser::veryl_grammar_trait as g;
150
151    let mut ret = Vec::new();
152
153    if let Some(x) = args {
154        let args: Vec<_> = x.attribute_list.as_ref().into();
155        for arg in args {
156            if let g::AttributeItem::Identifier(x) = arg {
157                ret.push(x.identifier.identifier_token.token);
158            }
159        }
160    }
161    ret
162}
163
164struct Pattern {
165    pub ifdef: StrId,
166    pub ifndef: StrId,
167    pub elsif: StrId,
168    pub r#else: StrId,
169    pub sv: StrId,
170    pub allow: StrId,
171    pub missing_port: StrId,
172    pub missing_reset_statement: StrId,
173    pub unused_variable: StrId,
174    pub unassign_variable: StrId,
175    pub enum_encoding: StrId,
176    pub sequential: StrId,
177    pub onehot: StrId,
178    pub gray: StrId,
179    pub enum_member_prefix: StrId,
180    pub test: StrId,
181    pub cond_type: StrId,
182    pub unique: StrId,
183    pub unique0: StrId,
184    pub priority: StrId,
185    pub none: StrId,
186    pub align: StrId,
187    pub number: StrId,
188    pub identifier: StrId,
189    pub fmt: StrId,
190    pub compact: StrId,
191    pub skip: StrId,
192    pub expand: StrId,
193    pub modport: StrId,
194    pub ignore: StrId,
195}
196
197impl Pattern {
198    fn new() -> Self {
199        Self {
200            ifdef: resource_table::insert_str("ifdef"),
201            ifndef: resource_table::insert_str("ifndef"),
202            elsif: resource_table::insert_str("elsif"),
203            r#else: resource_table::insert_str("else"),
204            sv: resource_table::insert_str("sv"),
205            allow: resource_table::insert_str("allow"),
206            missing_port: resource_table::insert_str("missing_port"),
207            missing_reset_statement: resource_table::insert_str("missing_reset_statement"),
208            unused_variable: resource_table::insert_str("unused_variable"),
209            unassign_variable: resource_table::insert_str("unassign_variable"),
210            enum_encoding: resource_table::insert_str("enum_encoding"),
211            sequential: resource_table::insert_str("sequential"),
212            onehot: resource_table::insert_str("onehot"),
213            gray: resource_table::insert_str("gray"),
214            enum_member_prefix: resource_table::insert_str("enum_member_prefix"),
215            test: resource_table::insert_str("test"),
216            cond_type: resource_table::insert_str("cond_type"),
217            unique: resource_table::insert_str("unique"),
218            unique0: resource_table::insert_str("unique0"),
219            priority: resource_table::insert_str("priority"),
220            none: resource_table::insert_str("none"),
221            align: resource_table::insert_str("align"),
222            number: resource_table::insert_str("number"),
223            identifier: resource_table::insert_str("identifier"),
224            fmt: resource_table::insert_str("fmt"),
225            compact: resource_table::insert_str("compact"),
226            skip: resource_table::insert_str("skip"),
227            expand: resource_table::insert_str("expand"),
228            modport: resource_table::insert_str("modport"),
229            ignore: resource_table::insert_str("ignore"),
230        }
231    }
232}
233
234thread_local!(static PAT: RefCell<Pattern> = RefCell::new(Pattern::new()));
235
236impl TryFrom<&veryl_parser::veryl_grammar_trait::Attribute> for Attribute {
237    type Error = AttributeError;
238
239    fn try_from(value: &veryl_parser::veryl_grammar_trait::Attribute) -> Result<Self, Self::Error> {
240        PAT.with_borrow(|pat| match value.identifier.identifier_token.token.text {
241            x if x == pat.ifdef || x == pat.ifndef || x == pat.elsif || x == pat.r#else => {
242                let arg = get_arg_ident(&value.attribute_opt, 0);
243
244                if let Some(arg) = arg {
245                    match x {
246                        x if x == pat.ifdef => Ok(Attribute::Ifdef(arg.text)),
247                        x if x == pat.ifndef => Ok(Attribute::Ifndef(arg.text)),
248                        x if x == pat.elsif => {
249                            Ok(Attribute::Elsif(arg.text, Vec::new(), Vec::new()))
250                        }
251                        x if x == pat.r#else => {
252                            Err(AttributeError::MismatchArgs("no argument".to_string()))
253                        }
254                        _ => unreachable!(),
255                    }
256                } else if x == pat.r#else {
257                    Ok(Attribute::Else(Vec::new(), Vec::new()))
258                } else {
259                    Err(AttributeError::MismatchArgs(
260                        "single identifier".to_string(),
261                    ))
262                }
263            }
264            x if x == pat.sv => {
265                let arg = get_arg_string(&value.attribute_opt, 0);
266
267                if let Some(arg) = arg {
268                    Ok(Attribute::Sv(arg.text))
269                } else {
270                    Err(AttributeError::MismatchArgs("single string".to_string()))
271                }
272            }
273            x if x == pat.allow => {
274                let arg = get_arg_ident(&value.attribute_opt, 0);
275
276                let err =
277                    AttributeError::MismatchArgs(format!("rule: ({})", AllowItem::available()));
278
279                if let Some(arg) = arg {
280                    match arg.text {
281                        x if x == pat.missing_port => Ok(Attribute::Allow(AllowItem::MissingPort)),
282                        x if x == pat.missing_reset_statement => {
283                            Ok(Attribute::Allow(AllowItem::MissingResetStatement))
284                        }
285                        x if x == pat.unused_variable => {
286                            Ok(Attribute::Allow(AllowItem::UnusedVariable))
287                        }
288                        x if x == pat.unassign_variable => {
289                            Ok(Attribute::Allow(AllowItem::UnassignVariable))
290                        }
291                        _ => Err(err),
292                    }
293                } else {
294                    Err(err)
295                }
296            }
297            x if x == pat.enum_encoding => {
298                let arg = get_arg_ident(&value.attribute_opt, 0);
299
300                let err = AttributeError::MismatchArgs(format!(
301                    "encoding type: ({})",
302                    EnumEncodingItem::available()
303                ));
304
305                if let Some(arg) = arg {
306                    match arg.text {
307                        x if x == pat.sequential => {
308                            Ok(Attribute::EnumEncoding(EnumEncodingItem::Sequential))
309                        }
310                        x if x == pat.onehot => {
311                            Ok(Attribute::EnumEncoding(EnumEncodingItem::OneHot))
312                        }
313                        x if x == pat.gray => Ok(Attribute::EnumEncoding(EnumEncodingItem::Gray)),
314                        _ => Err(err),
315                    }
316                } else {
317                    Err(err)
318                }
319            }
320            x if x == pat.enum_member_prefix => {
321                let arg = get_arg_ident(&value.attribute_opt, 0);
322
323                if let Some(arg) = arg {
324                    Ok(Attribute::EnumMemberPrefix(arg.text))
325                } else {
326                    Err(AttributeError::MismatchArgs(
327                        "single identifier".to_string(),
328                    ))
329                }
330            }
331            x if x == pat.test => {
332                let arg = get_arg_ident(&value.attribute_opt, 0);
333                let top = get_arg_ident(&value.attribute_opt, 1);
334
335                if let Some(arg) = arg {
336                    Ok(Attribute::Test(arg, top.map(|x| x.text)))
337                } else {
338                    Err(AttributeError::MismatchArgs(
339                        "single identifier".to_string(),
340                    ))
341                }
342            }
343            x if x == pat.cond_type => {
344                let arg = get_arg_ident(&value.attribute_opt, 0);
345
346                let err = AttributeError::MismatchArgs(format!(
347                    "condition type: ({})",
348                    CondTypeItem::available()
349                ));
350
351                if let Some(arg) = arg {
352                    match arg.text {
353                        x if x == pat.unique => Ok(Attribute::CondType(CondTypeItem::Unique)),
354                        x if x == pat.unique0 => Ok(Attribute::CondType(CondTypeItem::Unique0)),
355                        x if x == pat.priority => Ok(Attribute::CondType(CondTypeItem::Priority)),
356                        x if x == pat.none => Ok(Attribute::CondType(CondTypeItem::None)),
357                        _ => Err(err),
358                    }
359                } else {
360                    Err(err)
361                }
362            }
363            x if x == pat.align => {
364                let args = get_args_ident(&value.attribute_opt);
365                let mut items = Vec::new();
366
367                let err = AttributeError::MismatchArgs(format!(
368                    "align type: ({})",
369                    AlignItem::available()
370                ));
371
372                for arg in &args {
373                    match arg.text {
374                        x if x == pat.number => items.push(AlignItem::Number),
375                        x if x == pat.identifier => items.push(AlignItem::Identifier),
376                        _ => return Err(err),
377                    }
378                }
379
380                if args.is_empty() {
381                    Err(err)
382                } else {
383                    Ok(Attribute::Align(items))
384                }
385            }
386            x if x == pat.fmt => {
387                let args = get_args_ident(&value.attribute_opt);
388                let mut items = Vec::new();
389
390                let err = AttributeError::MismatchArgs(format!(
391                    "format type: ({})",
392                    FormatItem::available()
393                ));
394
395                for arg in &args {
396                    match arg.text {
397                        x if x == pat.compact => items.push(FormatItem::Compact),
398                        x if x == pat.skip => items.push(FormatItem::Skip),
399                        _ => return Err(err),
400                    }
401                }
402
403                if args.is_empty() {
404                    Err(err)
405                } else {
406                    Ok(Attribute::Format(items))
407                }
408            }
409            x if x == pat.expand => {
410                let args = get_args_ident(&value.attribute_opt);
411                let mut items = Vec::new();
412
413                let err = AttributeError::MismatchArgs(format!(
414                    "expand type: ({})",
415                    ExpandItem::available()
416                ));
417
418                for arg in &args {
419                    match arg.text {
420                        x if x == pat.modport => items.push(ExpandItem::Modport),
421                        _ => return Err(err),
422                    }
423                }
424
425                if args.is_empty() {
426                    Err(err)
427                } else {
428                    Ok(Attribute::Expand(items))
429                }
430            }
431            x if x == pat.ignore => {
432                if value.attribute_opt.is_some() {
433                    Err(AttributeError::MismatchArgs("no argument".to_string()))
434                } else {
435                    Ok(Attribute::Ignore)
436                }
437            }
438            _ => Err(AttributeError::UnknownAttribute),
439        })
440    }
441}
442
443#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumIter)]
444pub enum AllowItem {
445    MissingPort,
446    MissingResetStatement,
447    UnusedVariable,
448    UnassignVariable,
449}
450
451impl AllowItem {
452    pub fn available() -> String {
453        let mut ret = String::new();
454        for (i, x) in Self::iter().enumerate() {
455            if i != 0 {
456                ret.push('|');
457            }
458            ret.push_str(&format!("{x}"));
459        }
460        ret
461    }
462}
463
464impl fmt::Display for AllowItem {
465    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466        let text = match self {
467            AllowItem::MissingPort => "missing_port",
468            AllowItem::MissingResetStatement => "missing_reset_statement",
469            AllowItem::UnusedVariable => "unused_variable",
470            AllowItem::UnassignVariable => "unassign_variable",
471        };
472        text.fmt(f)
473    }
474}
475
476#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, EnumIter)]
477pub enum EnumEncodingItem {
478    #[default]
479    Sequential,
480    OneHot,
481    Gray,
482}
483
484impl EnumEncodingItem {
485    pub fn available() -> String {
486        let mut ret = String::new();
487        for (i, x) in Self::iter().enumerate() {
488            if i != 0 {
489                ret.push('|');
490            }
491            ret.push_str(&format!("{x}"));
492        }
493        ret
494    }
495}
496
497impl fmt::Display for EnumEncodingItem {
498    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
499        let text = match self {
500            EnumEncodingItem::Sequential => "sequential",
501            EnumEncodingItem::OneHot => "one_hot",
502            EnumEncodingItem::Gray => "gray",
503        };
504        text.fmt(f)
505    }
506}
507
508#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumIter)]
509pub enum CondTypeItem {
510    Unique,
511    Unique0,
512    Priority,
513    None,
514}
515
516impl CondTypeItem {
517    pub fn available() -> String {
518        let mut ret = String::new();
519        for (i, x) in Self::iter().enumerate() {
520            if i != 0 {
521                ret.push('|');
522            }
523            ret.push_str(&format!("{x}"));
524        }
525        ret
526    }
527}
528
529impl fmt::Display for CondTypeItem {
530    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
531        let text = match self {
532            CondTypeItem::Unique => "unique",
533            CondTypeItem::Unique0 => "unique0",
534            CondTypeItem::Priority => "priority",
535            CondTypeItem::None => "none",
536        };
537        text.fmt(f)
538    }
539}
540
541#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumIter)]
542pub enum AlignItem {
543    Number,
544    Identifier,
545}
546
547impl AlignItem {
548    pub fn available() -> String {
549        let mut ret = String::new();
550        for (i, x) in Self::iter().enumerate() {
551            if i != 0 {
552                ret.push('|');
553            }
554            ret.push_str(&format!("{x}"));
555        }
556        ret
557    }
558}
559
560impl fmt::Display for AlignItem {
561    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
562        let text = match self {
563            AlignItem::Number => "number",
564            AlignItem::Identifier => "identifier",
565        };
566        text.fmt(f)
567    }
568}
569
570#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumIter)]
571pub enum FormatItem {
572    Compact,
573    Skip,
574}
575
576impl FormatItem {
577    pub fn available() -> String {
578        let mut ret = String::new();
579        for (i, x) in Self::iter().enumerate() {
580            if i != 0 {
581                ret.push('|');
582            }
583            ret.push_str(&format!("{x}"));
584        }
585        ret
586    }
587}
588
589impl fmt::Display for FormatItem {
590    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
591        let text = match self {
592            FormatItem::Compact => "compact",
593            FormatItem::Skip => "skip",
594        };
595        text.fmt(f)
596    }
597}
598
599#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumIter)]
600pub enum ExpandItem {
601    Modport,
602}
603
604impl ExpandItem {
605    pub fn available() -> String {
606        let mut ret = String::new();
607        for (i, x) in Self::iter().enumerate() {
608            if i != 0 {
609                ret.push('|');
610            }
611            ret.push_str(&format!("{x}"));
612        }
613        ret
614    }
615}
616
617impl fmt::Display for ExpandItem {
618    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
619        let text = match self {
620            ExpandItem::Modport => "modport",
621        };
622        text.fmt(f)
623    }
624}