Skip to content

Instantly share code, notes, and snippets.

@zegervdv
Created June 26, 2023 06:50
Show Gist options
  • Save zegervdv/8dfcf02d82b7a1ecf71aa91b4bf8d944 to your computer and use it in GitHub Desktop.
Save zegervdv/8dfcf02d82b7a1ecf71aa91b4bf8d944 to your computer and use it in GitHub Desktop.
verilog halstead code metrics
import sys
import math
from collections import Counter, deque
from typing import NamedTuple
import verible_verilog_syntax
from rich.table import Table
from rich import print
class HalsteadMetrics(NamedTuple):
operands: Counter
operators: Counter
def unique_operators(self) -> int:
return len(self.operators.keys())
def total_operators(self) -> int:
return sum(self.operators.values())
def unique_operands(self) -> int:
return len(self.operands.keys())
def total_operands(self) -> int:
return sum(self.operands.values())
def vocabulary(self) -> int:
return self.unique_operands() + self.unique_operands()
def length(self) -> int:
return self.total_operands() + self.total_operators()
def estimated_length(self) -> float:
n_1 = self.unique_operators()
n_2 = self.unique_operands()
return n_1 * math.log2(n_1) + n_2 * math.log2(n_2)
def volume(self) -> float:
return self.length() * math.log2(self.vocabulary())
def difficulty(self) -> float:
return (self.unique_operators() / 2) * (self.total_operands() / self.unique_operands())
def effort(self) -> float:
return self.difficulty() * self.volume()
def time(self) -> float:
return self.effort() / 18
def bugs(self) -> float:
return math.pow(self.effort(), 2/3) / 3000
def table(self) -> Table:
table = Table()
table.add_column('Metric')
table.add_column()
table.add_row('Unique Operators', f'{self.unique_operators()}')
table.add_row('Unique Operands', f'{self.unique_operands()}')
table.add_row('Total Operators', f'{self.total_operators()}')
table.add_row('Total Operands', f'{self.total_operands()}')
table.add_row('Vocabulary', f'{self.vocabulary()}')
table.add_row('Length', f'{self.length()}')
table.add_row('Estim. Length', f'{self.estimated_length()}')
table.add_row('Volume', f'{self.volume()}')
table.add_row('Difficulty', f'{self.difficulty()}')
table.add_row('Effort', f'{self.effort()}')
table.add_row('Time', f'{self.time()}')
table.add_row('Bugs', f'{self.bugs()}')
return table
def metrics(n_1, n_2, N_1, N_2):
print(f"Distinct Operators: {n_1}")
print(f"Distinct Operands : {n_2}")
print(f"Total Operators : {N_1}")
print(f"Total Operands : {N_2}")
print()
n = n_1 + n_2
N = N_1 + N_2
print(f"Vocabulary : {n}")
print(f"Length : {N}")
N_caret = n_1 * math.log2(n_1) + n_2 * math.log2(n_2)
print(f"Estimated Length : {N_caret}")
V = N * math.log2(n)
print(f"Volume : {V}")
D = n_1 / 2 * N_2 / n_2
print(f"Difficulty : {D}")
E = D * V
print(f"Effort : {E}")
T = E / 18
print(f"Time to program[s]: {T}")
B = math.pow(E, 2/3) / 3000
print(f"Estim. Bugs : {B}")
def main():
parser = verible_verilog_syntax.VeribleVerilogSyntax()
data = parser.parse_files(['test.sv'])
for file, tree in data.items():
operators = Counter()
operands = Counter()
nodes = deque(tree.tree.children)
while nodes:
node = nodes.popleft()
if hasattr(node, 'tag') and node.tag in {'kNumber', 'SymbolIdentifier'}:
operands[node.text] += 1
elif isinstance(node, verible_verilog_syntax.TokenNode):
operators[node.text] += 1
else:
nodes.extend(node.children)
del operators[')']
del operators[']']
del operators['end']
del operators['endmodule']
print(operators)
print(operands)
# metrics(n_1=len(operators.keys()), n_2=len(operands.keys()), N_1=sum(operators.values()), N_2=sum(operands.values()))
metric = HalsteadMetrics(operands=operands, operators=operators)
print(metric.table())
# print(f"Cyclomatic: {operators['if'] + operators['for'] + 1}")
if __name__ == '__main__':
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment