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}