ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
hil_convergence_safety_ast.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Exact executable-AST helpers for HIL convergence structural checks."""
4
5from __future__ import annotations
6
7import ast
8
9
10def same_statement(node: ast.stmt, source: str) -> bool:
11 """Compare executable AST rather than source tokens or comments."""
12 expected = ast.parse(source).body[0]
13 return ast.dump(node, include_attributes=False) == ast.dump(expected, include_attributes=False)
14
15
16def function(tree: ast.Module, name: str) -> ast.FunctionDef | None:
17 """Return one uniquely named top-level function."""
18 matches = [
19 node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == name
20 ]
21 return matches[0] if len(matches) == 1 else None
22
23
24def statement_index(function_node: ast.FunctionDef, source: str) -> int:
25 """Find one exact executable top-level statement in a function."""
26 matches = [
27 index for index, node in enumerate(function_node.body) if same_statement(node, source)
28 ]
29 return matches[0] if len(matches) == 1 else -1
30
31
32def assignment(function_node: ast.FunctionDef, name: str) -> ast.expr | None:
33 """Return one exact top-level simple-assignment value."""
34 matches = [
35 node.value
36 for node in function_node.body
37 if isinstance(node, ast.Assign)
38 and len(node.targets) == 1
39 and isinstance(node.targets[0], ast.Name)
40 and node.targets[0].id == name
41 ]
42 return matches[0] if len(matches) == 1 else None
43
44
45def nested_assignment(function_node: ast.FunctionDef, name: str) -> ast.expr | None:
46 """Return one uniquely named simple assignment anywhere in a function."""
47 matches = []
48 for node in ast.walk(function_node):
49 if not isinstance(node, ast.Assign) or len(node.targets) != 1:
50 continue
51 target = node.targets[0]
52 if isinstance(target, ast.Name) and target.id == name:
53 matches.append(node.value)
54 return matches[0] if len(matches) == 1 else None
55
56
57def return_strings(function_node: ast.FunctionDef | None) -> set[str]:
58 """Return executable string literals from one unique return expression."""
59 if function_node is None:
60 return set()
61 returns = [node.value for node in function_node.body if isinstance(node, ast.Return)]
62 if len(returns) != 1:
63 return set()
64 return {
65 node.value
66 for node in ast.walk(returns[0])
67 if isinstance(node, ast.Constant) and isinstance(node.value, str)
68 }
69
70
71def module_assignment(tree: ast.Module, name: str) -> ast.expr | None:
72 """Return one exact top-level module assignment value."""
73 matches = [
74 node.value
75 for node in tree.body
76 if isinstance(node, ast.Assign)
77 and len(node.targets) == 1
78 and isinstance(node.targets[0], ast.Name)
79 and node.targets[0].id == name
80 ]
81 return matches[0] if len(matches) == 1 else None