Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/src/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:rohd/rohd.dart';
import 'package:rohd/src/collections/traverseable_collection.dart';
import 'package:rohd/src/diagnostics/inspector_service.dart';
import 'package:rohd/src/utilities/config.dart';
import 'package:rohd/src/utilities/redundancy_handler.dart';
import 'package:rohd/src/utilities/sanitizer.dart';
import 'package:rohd/src/utilities/timestamper.dart';
import 'package:rohd/src/utilities/uniquifier.dart';
Expand Down Expand Up @@ -913,9 +914,9 @@ abstract class Module {
*/

''';
return synthHeader +
return RedundancyHandler.removeRedundancies(synthHeader +
SynthBuilder(this, SystemVerilogSynthesizer())
.getFileContents()
.join('\n\n////////////////////\n\n');
.join('\n\n////////////////////\n\n'));
}
}
67 changes: 67 additions & 0 deletions lib/src/utilities/redundancy_handler.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (C) 2021-2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// redundancy_handler.dart
// Removes redundant usage of parentheses from SystemVerilog code strings.
//
// 2025 March 4
// Author: Gustavo A. Bonilla Gonzalez <gustavo.bonilla.gonzalez@intel.com>
// Adan Baltazar Ortiz <adan.baltazar.ortiz@intel.com>

/// A utility for ensuring generated code doesn't contain redundant parentheses.
///
/// "Redundant" refers to parentheses usage that doesn't change the order of
/// operations in a SystemVerilog expression. This utility is useful for
/// ensuring generated code is concise and readable.
class RedundancyHandler {
/// Iteratively removes any unnecessary parentheses present in [svCode],
/// returning a refactored SystemVerilog code string.
static String removeRedundancies(String svCode) {
var newSvCode = svCode;
var bkpSvCode = svCode;

do {
bkpSvCode = newSvCode;
newSvCode = _removeRedundantParentheses(newSvCode);
} while (newSvCode != bkpSvCode);

return newSvCode;
}

/// Applies a set of patterns to remove redundant parentheses from [svCode],
/// returning a modified SystemVerilog code string.
static String _removeRedundantParentheses(String svCode) {
final parenthesesPatterns = [
RegExp(r'\((\w+)\s*([\+\-\*\/])\s*(\w+)\)'), // Arithmetic
RegExp(r'\((\w+)\s*(&&|\|\|)\s*(\w+)\)'), // Logical
RegExp(
r'\(\(([^()]+)\)\s*\?\s*\(([^()]+)\)\s*:\s*\(([^()]+)\)\)'), // Conditional
RegExp(r'\((\w+)\s*([&\|^~])\s*(\w+)\)'), // Bitwise
RegExp(r'\(\{([^}]+)\}\)'), // Concatenation & replication
RegExp(r'(\w+)\(\(([^)]+)\)\)'), // Function call
RegExp(r'(\w+)\s*=\s*\(\(([^)]+)\)\)'), // Assignment
RegExp(r'case\s*\(\(([^)]+)\)\)') // Case
];

final parenthesesReplacements = [
(Match match) => '${match[1]} ${match[2]} ${match[3]}', // Arithmetic
(Match match) => '${match[1]} ${match[2]} ${match[3]}', // Logical
(Match match) =>
'(${match[1]}) ? ${match[2]} : ${match[3]}', // Conditional
(Match match) => '${match[1]} ${match[2]} ${match[3]}', // Bitwise
(Match match) => '{${match[1]}}', // Concatenation & replication
(Match match) => '${match[1]}(${match[2]})', // Function call
(Match match) => '${match[1]} = ${match[2]}', // Assignment
(Match match) => 'case (${match[1]})' // Case
];

var newSvCode = svCode;

for (var i = 0; i < parenthesesPatterns.length; i++) {
newSvCode = newSvCode.replaceAllMapped(
parenthesesPatterns[i], parenthesesReplacements[i]);
}

return newSvCode;
}
}
78 changes: 78 additions & 0 deletions test/redundancy_handler_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (C) 2022-2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// redundancy_handler_test.dart
// Unit tests for redundancy handling.
//
// 2025 March 5
// Author: Gustavo A. Bonilla Gonzalez <gustavo.bonilla.gonzalez@intel.com>

import 'package:rohd/src/utilities/redundancy_handler.dart';
import 'package:test/test.dart';

void main() {
// Parameterized test cases for different types of redundant parentheses
final testCases = [
{
'type': 'Arithmetic',
'input': 'result = ((a + b));',
'expected': 'result = a + b;'
},
{
'type': 'Logical',
'input': 'result = ((a && b));',
'expected': 'result = a && b;'
},
{
'type': 'Conditional',
'input': 'result = ((a) ? (b) : (c));',
'expected': 'result = (a) ? b : c;'
},
{
'type': 'Custom 1',
'input': '''
my_mod inst1(
.a_0((a[0]))
);''',
'expected': '''
my_mod inst1(
.a_0(a[0])
);'''
},
{
'type': 'Custom 2',
'input': 'assign a = (b & (c & (d & e)));',
'expected': 'assign a = (b & (c & d & e));'
},
{
'type': 'Bitwise',
'input': 'result = ((a & b));',
'expected': 'result = a & b;'
},
{
'type': 'Concatenation',
'input': 'result = ({a, b});',
'expected': 'result = {a, b};'
},
{
'type': 'Function Call',
'input': 'result = func((a, b));',
'expected': 'result = func(a, b);'
},
{
'type': 'Assignment',
'input': 'result = ((a));',
'expected': 'result = a;'
},
{'type': 'Case', 'input': 'case ((a))', 'expected': 'case (a)'},
{'type': 'None', 'input': 'result = a + b;', 'expected': 'result = a + b;'},
];

for (final testCase in testCases) {
test('${testCase['type']} redundant parentheses: ${testCase['input']}',
() async {
final svCode = RedundancyHandler.removeRedundancies(testCase['input']!);
expect(svCode, equals(testCase['expected']));
});
}
}
Loading