From e24b47375794636114d0a5375852bbb9916e4518 Mon Sep 17 00:00:00 2001 From: yousifB Date: Tue, 2 Jun 2026 18:35:31 +0200 Subject: [PATCH 1/2] Fix parsing type equality --- .../behavioral_statements/case_statements.rs | 18 ++++++++- .../src/expressions/expressions.rs | 40 +++++++++++++++---- sv-parser-parser/src/tests.rs | 22 ++++++---- .../behavioral_statements/case_statements.rs | 10 ++++- .../src/expressions/expressions.rs | 10 ++++- sv-parser/src/lib.rs | 23 +++++++++++ 6 files changed, 102 insertions(+), 21 deletions(-) diff --git a/sv-parser-parser/src/behavioral_statements/case_statements.rs b/sv-parser-parser/src/behavioral_statements/case_statements.rs index a999c432..58792fc9 100644 --- a/sv-parser-parser/src/behavioral_statements/case_statements.rs +++ b/sv-parser-parser/src/behavioral_statements/case_statements.rs @@ -75,7 +75,14 @@ pub(crate) fn case_keyword(s: Span) -> IResult { #[tracable_parser] #[packrat_parser] pub(crate) fn case_expression(s: Span) -> IResult { - let (s, a) = expression(s)?; + let (s, a) = alt(( + map(expression, |x| { + CaseExpressionExpression::Expression(Box::new(x)) + }), + map(type_reference, |x| { + CaseExpressionExpression::TypeReference(Box::new(x)) + }), + ))(s)?; Ok((s, CaseExpression { nodes: (a,) })) } @@ -160,7 +167,14 @@ pub(crate) fn case_inside_item_nondefault(s: Span) -> IResult IResult { - let (s, a) = expression(s)?; + let (s, a) = alt(( + map(expression, |x| { + CaseExpressionExpression::Expression(Box::new(x)) + }), + map(type_reference, |x| { + CaseExpressionExpression::TypeReference(Box::new(x)) + }), + ))(s)?; Ok((s, CaseItemExpression { nodes: (a,) })) } diff --git a/sv-parser-parser/src/expressions/expressions.rs b/sv-parser-parser/src/expressions/expressions.rs index b77abc04..11089029 100644 --- a/sv-parser-parser/src/expressions/expressions.rs +++ b/sv-parser-parser/src/expressions/expressions.rs @@ -305,16 +305,40 @@ pub(crate) fn expression_operator_assignment(s: Span) -> IResult IResult { - let (s, a) = expression(s)?; + let (s, a) = alt(( + map(expression, |x| { + ExpressionBinaryOperand::Expression(Box::new(x)) + }), + map(type_reference, |x| { + ExpressionBinaryOperand::TypeReference(Box::new(x)) + }), + ))(s)?; + let is_equality = peek(alt((symbol("=="), symbol("!="))))(s).is_ok(); let (s, b) = binary_operator(s)?; let (s, c) = many0(attribute_instance)(s)?; - let (s, d) = expression(s)?; - Ok(( - s, - Expression::Binary(Box::new(ExpressionBinary { - nodes: (a, b, c, d), - })), - )) + let (s, d) = alt(( + map(expression, |x| { + ExpressionBinaryOperand::Expression(Box::new(x)) + }), + map(type_reference, |x| { + ExpressionBinaryOperand::TypeReference(Box::new(x)) + }), + ))(s)?; + + // Enforces Footnote (40) in IEEE STD 1800 - 2017 + let op1_is_type_ref = matches!(a, ExpressionBinaryOperand::TypeReference(_)); + let op2_is_type_ref = matches!(d, ExpressionBinaryOperand::TypeReference(_)); + if !(op1_is_type_ref || op2_is_type_ref) || (is_equality && op1_is_type_ref && op2_is_type_ref) + { + Ok(( + s, + Expression::Binary(Box::new(ExpressionBinary { + nodes: (a, b, c, d), + })), + )) + } else { + Err(Err::Error(make_error(s, ErrorKind::Fail))) + } } #[tracable_parser] diff --git a/sv-parser-parser/src/tests.rs b/sv-parser-parser/src/tests.rs index 0a53fab1..1c1f1b88 100644 --- a/sv-parser-parser/src/tests.rs +++ b/sv-parser-parser/src/tests.rs @@ -54,13 +54,13 @@ mod unit { #[test] fn test_data_declaration() { - // Implicit data_type is not allowed unless the `var` keyword is used. - test!(data_declaration, "logic x = 0;", Ok((_, _))); - test!(data_declaration, " x = 0;", Err(_)); - test!(data_declaration, "var logic x = 0;", Ok((_, _))); - test!(data_declaration, "var x = 0;", Ok((_, _))); - test!(data_declaration, "const logic x = 0;", Ok((_, _))); - test!(data_declaration, "const x = 0;", Err(_)); + // Implicit data_type is not allowed unless the `var` keyword is used. + test!(data_declaration, "logic x = 0;", Ok((_, _))); + test!(data_declaration, " x = 0;", Err(_)); + test!(data_declaration, "var logic x = 0;", Ok((_, _))); + test!(data_declaration, "var x = 0;", Ok((_, _))); + test!(data_declaration, "const logic x = 0;", Ok((_, _))); + test!(data_declaration, "const x = 0;", Err(_)); } #[test] @@ -332,6 +332,14 @@ mod unit { test!(expression, "(!a ? 0 : !b : 1 : c ? 0 : 1)", Ok((_, _))); } + #[test] + fn test_bin_op_expression() { + test!(expression, "type(logic) == type(logic)", Ok((_, _))); + test!(expression, "type(logic) != type(logic)", Ok((_, _))); + test!(expression, "type(logic) + type(logic)", Err(_)); + test!(expression, "type(logic) == a", Err(_)); + } + #[test] fn test_text_macro_definition() { test!(text_macro_definition, r##"`define a b c"##, Ok((_, _))); diff --git a/sv-parser-syntaxtree/src/behavioral_statements/case_statements.rs b/sv-parser-syntaxtree/src/behavioral_statements/case_statements.rs index 5d92e5d8..935c9b28 100644 --- a/sv-parser-syntaxtree/src/behavioral_statements/case_statements.rs +++ b/sv-parser-syntaxtree/src/behavioral_statements/case_statements.rs @@ -56,7 +56,13 @@ pub enum CaseKeyword { #[derive(Clone, Debug, PartialEq, Node)] pub struct CaseExpression { - pub nodes: (Expression,), + pub nodes: (CaseExpressionExpression,), +} + +#[derive(Clone, Debug, PartialEq, Node)] +pub enum CaseExpressionExpression { + Expression(Box), + TypeReference(Box), } #[derive(Clone, Debug, PartialEq, Node)] @@ -104,7 +110,7 @@ pub struct CaseInsideItemNondefault { #[derive(Clone, Debug, PartialEq, Node)] pub struct CaseItemExpression { - pub nodes: (Expression,), + pub nodes: (CaseExpressionExpression,), } #[derive(Clone, Debug, PartialEq, Node)] diff --git a/sv-parser-syntaxtree/src/expressions/expressions.rs b/sv-parser-syntaxtree/src/expressions/expressions.rs index 24b2f569..d88a5678 100644 --- a/sv-parser-syntaxtree/src/expressions/expressions.rs +++ b/sv-parser-syntaxtree/src/expressions/expressions.rs @@ -149,13 +149,19 @@ pub struct ExpressionOperatorAssignment { #[derive(Clone, Debug, PartialEq, Node)] pub struct ExpressionBinary { pub nodes: ( - Expression, + ExpressionBinaryOperand, BinaryOperator, Vec, - Expression, + ExpressionBinaryOperand, ), } +#[derive(Clone, Debug, PartialEq, Node)] +pub enum ExpressionBinaryOperand { + Expression(Box), + TypeReference(Box), +} + #[derive(Clone, Debug, PartialEq, Node)] pub struct TaggedUnionExpression { pub nodes: (Keyword, MemberIdentifier, Option), diff --git a/sv-parser/src/lib.rs b/sv-parser/src/lib.rs index 918e6bf6..e84700a7 100644 --- a/sv-parser/src/lib.rs +++ b/sv-parser/src/lib.rs @@ -451,4 +451,27 @@ endmodule"##; let ret = parse_sv_str(src, &path, &defines, &[""], false, false); assert!(ret.is_ok()); } + + #[test] + fn test_type_equality() { + let src = r##"module top #( parameter type T = type(logic[11:0]) ) + (); + initial begin + case (type(T)) + type(logic[11:0]) : ; + default : $stop; + endcase + if (type(T) == type(logic[12:0])) $stop; + if (type(T) != type(logic[11:0])) $stop; + if (type(T) === type(logic[12:0])) $stop; + if (type(T) !== type(logic[11:0])) $stop; + $finish; + end + endmodule"##; + + let path = PathBuf::from(""); + let defines = HashMap::new(); + let ret = parse_sv_str(src, &path, &defines, &[""], false, false); + assert!(ret.is_ok()); + } } From e0068559d92c8ac02e965d01be2beb473bee12eb Mon Sep 17 00:00:00 2001 From: yousifB Date: Thu, 4 Jun 2026 00:53:21 +0200 Subject: [PATCH 2/2] Fix only allow type comparisons if case statement is on types --- .../behavioral_statements/case_statements.rs | 42 +++++++++++++---- sv-parser-parser/src/tests.rs | 47 +++++++++++++------ 2 files changed, 66 insertions(+), 23 deletions(-) diff --git a/sv-parser-parser/src/behavioral_statements/case_statements.rs b/sv-parser-parser/src/behavioral_statements/case_statements.rs index 58792fc9..92371a89 100644 --- a/sv-parser-parser/src/behavioral_statements/case_statements.rs +++ b/sv-parser-parser/src/behavioral_statements/case_statements.rs @@ -18,14 +18,40 @@ pub(crate) fn case_statement_normal(s: Span) -> IResult { let (s, a) = opt(unique_priority)(s)?; let (s, b) = case_keyword(s)?; let (s, c) = paren(case_expression)(s)?; - let (s, d) = case_item(s)?; - let (s, (e, f)) = many_till(case_item, keyword("endcase"))(s)?; - Ok(( - s, - CaseStatement::Normal(Box::new(CaseStatementNormal { - nodes: (a, b, c, d, e, f), - })), - )) + if !matches!( + c.nodes.1.nodes.0, + CaseExpressionExpression::TypeReference(_) + ) { + let (s, d) = case_item(s)?; + let (s, (e, f)) = many_till(case_item, keyword("endcase"))(s)?; + Ok(( + s, + CaseStatement::Normal(Box::new(CaseStatementNormal { + nodes: (a, b, c, d, e, f), + })), + )) + } else { + let verify_case_item = |d: &CaseItem| { + if let CaseItem::NonDefault(non_def_item) = d { + matches!( + non_def_item.nodes.0.nodes.0.nodes.0, + CaseExpressionExpression::TypeReference(_) + ) && non_def_item.nodes.0.nodes.1.iter().all(|(_, cie)| { + matches!(cie.nodes.0, CaseExpressionExpression::TypeReference(_)) + }) + } else { + true + } + }; + let (s, d) = verify(case_item, verify_case_item)(s)?; + let (s, (e, f)) = many_till(verify(case_item, verify_case_item), keyword("endcase"))(s)?; + Ok(( + s, + CaseStatement::Normal(Box::new(CaseStatementNormal { + nodes: (a, b, c, d, e, f), + })), + )) + } } #[tracable_parser] diff --git a/sv-parser-parser/src/tests.rs b/sv-parser-parser/src/tests.rs index 1c1f1b88..94f41a59 100644 --- a/sv-parser-parser/src/tests.rs +++ b/sv-parser-parser/src/tests.rs @@ -817,11 +817,7 @@ mod spec { r##"a = add (* mode = "cla" *) (b, c);"##, Ok((_, _)) ); - test!( - statement, - r##"a = b ? (* no_glitch *) c : d;"##, - Ok((_, _)) - ); + test!(statement, r##"a = b ? (* no_glitch *) c : d;"##, Ok((_, _))); } #[test] @@ -2693,11 +2689,7 @@ mod spec { status = p.current_status();"##, Ok((_, _)) ); - test!( - statement, - r##"status = current_status(p);"##, - Ok((_, _)) - ); + test!(statement, r##"status = current_status(p);"##, Ok((_, _))); test!(many1(module_item), r##"Packet p = new;"##, Ok((_, _))); test!( many1(module_item), @@ -3520,11 +3512,7 @@ mod spec { end"##, Ok((_, _)) ); - test!( - statement, - r##"put_ref = new(); // illegal"##, - Ok((_, _)) - ); + test!(statement, r##"put_ref = new(); // illegal"##, Ok((_, _))); test!( many1(module_item), r##"interface class IntfBase1; @@ -15950,6 +15938,35 @@ mod spec { Ok((_, _)) ); } + + #[test] + fn test_case_statement_comparison() { + test!( + many1(case_statement), + r##"case (type(logic)) + type(logic[11:0]) : ; + type(logic) : ; + default : ; + endcase"##, + Ok((_, _)) + ); + test!( + many1(case_statement), + r##"case (type(logic)) + 1 : ; + default : ; + endcase"##, + Err(_) + ); + test!( + many1(case_statement), + r##"case (type(logic)) + x : ; + default : ; + endcase"##, + Err(_) + ); + } } mod error {