added fallthrough support 🤮

This commit is contained in:
Dominic Höglinger 2022-11-21 22:16:03 +01:00
parent 801b60ce33
commit 2ca5091658
2 changed files with 116 additions and 23 deletions

View File

@ -14,6 +14,10 @@ if __name__ == "__main__":
argparser.add_argument('filename',
nargs='?',
help='name of file to parse')
argparser.add_argument('-I', '--includedirs', nargs='+', default=[])
argparser.add_argument('-D', '--defines', nargs='+', default=[])
argparser.add_argument('-p', '--preprocess', help='output preprocessor file')
argparser.add_argument('--func', help='process function')
argparser.add_argument('--enum', help='state enum')
argparser.add_argument('--initial', help='initial state')
@ -29,19 +33,29 @@ if __name__ == "__main__":
p = Preprocessor()
p.add_path('/usr/lib/gcc/x86_64-linux-gnu/12/include/')
for inc in args.includedirs:
print(f"Include-Dir: {inc}")
p.add_path(inc)
for define in args.defines:
name,value = define.split('=')
print(f"Define: {name}={value}")
p.define(f"{name} {value}")
p.parse(source)
oh = io.StringIO()
p.write(oh)
prep_source = oh.getvalue()
prep_source = oh.getvalue()#unicode(oh.getvalue(), errors='ignore')
#print(prep_source)
#exit()
if args.preprocess is not None:
with open(args.preprocess, "wt") as f:
n = f.write(prep_source)
parser = CParser()
ast = parser.parse(prep_source)
#ast = parse_file(args.filename, use_cpp=False)
#ast.show()
assign_table = []
state_enums = []
func_table = {}
@ -57,21 +71,26 @@ if __name__ == "__main__":
etv = EnumTypedefVisitor()
etv.visit(ast)
enum_table = etv.enums
states = []
if not(args.func in func_table):
raise Exception(f"Function name '{args.func}' not found!")
else:
proc_func = func_table[args.func]
fsm_funcs.append(proc_func)
func_table[args.func].show()
fcv = FuncCallVisitor()
fcv.visit(proc_func)
fsm_funcs += [ func_table[x] for x in fcv.func_calls ]
for fc in fcv.func_calls:
if fc in func_table:
fsm_funcs.append(func_table[fc])
#fsm_funcs += [ func_table[x] for x in fcv.func_calls ]
print("Enum Table")
if args.enum in enum_table:
ev = EnumVisitor()
ev.visit(enum_table[args.enum])
for ename in ev.enum_names:
print(" - ",ename)
states.append(ename)
for f in fsm_funcs:
sav = StateAssignmentVisitor(ename)
sav.visit(f)
@ -89,7 +108,7 @@ if __name__ == "__main__":
for sa in state_asmts:
for f in fsm_funcs:
sctv = SwitchCaseTranVisitor(sa[0])
sctv = SwitchCaseTranVisitor(sa[0], states, sa[2])
sctv.visit(f)
tran_table += sctv.tran_table
@ -98,13 +117,15 @@ if __name__ == "__main__":
for t in tran_table:
print(f"{t[0]}->{t[1]}")
if t[0] in comp_tt:
comp_tt[t[0]].append(t[1])
if t[1] not in comp_tt[t[0]]:
comp_tt[t[0]].append(t[1])
else:
comp_tt[t[0]] = [t[1]]
print("")
pure_sa = [x[0] for x in state_asmts]
states = comp_tt.keys()
#todo recomment once fixed
#states = comp_tt.keys()
print("States: ", ",".join(states))
print("")
@ -156,13 +177,15 @@ if __name__ == "__main__":
g = gv.Digraph('G')
for state in states:
shape = 'circle'
if state == args.initial:
shape = 'doublecircle'
if state in props_by_state:
pstr = ",".join(props_by_state[state])
if state == args.initial:
g.attr('node', shape='doublecircle')
else:
g.attr('node', shape='circle')
g.node(state, label=state, xlabel=f"{{{pstr}}}")
g.node(state, label=state, xlabel=f"{{{pstr}}}", shape=shape)
else:
g.node(state, label=state, shape=shape)

View File

@ -1,4 +1,43 @@
from pycparser import c_ast
from itertools import pairwise
def path_to_str(p):
return "->".join([str(n.__class__.__name__) for n in p])
def path_select_last(classname, p):
for n in reversed(p):
if n.__class__.__name__ == classname:
return n
return None
def path_select_parent(child, p):
parent = None
for n in p:
if n == child:
return parent
parent = n
return None
class NodeVisitorWithParent(object):
def __init__(self):
self.current_parent = None
def visit(self, node):
""" Visit a node.
"""
method = 'visit_' + node.__class__.__name__
visitor = getattr(self, method, self.generic_visit)
return visitor(node)
def generic_visit(self, node):
""" Called if no explicit visitor function exists for a
node. Implements preorder visiting of the node.
"""
oldparent = self.current_parent
self.current_parent = node
for c in node.children():
self.visit(c)
self.current_parent = oldparent
class FuncDefVisitor(c_ast.NodeVisitor):
def __init__(self):
@ -46,12 +85,38 @@ class StateAssignmentVisitor(c_ast.NodeVisitor):
self.visit(c, path)
def visit_Assignment(self, n, path):
n.show()
case_node = path_select_last('Case', path)
fallthrough_case_names = []
if case_node is not None:
case_parent = path_select_parent(case_node, path) #path_select_last('Compound', path)
siblings = [x for x in case_parent.block_items if not isinstance(x, c_ast.Default)]
sibling_names = [x.expr.name for x in siblings]
sibling_empty = [(len(x.stmts) == 0) for x in siblings]
in_fallthrough = False
slice_start = None
slice_end = None
for i,sibling in enumerate(zip(reversed(sibling_names),reversed(sibling_empty), reversed(siblings))):
if sibling[0] == self.state:
slice_start = i + 1
if (slice_start is not None) and sibling[1] and not(in_fallthrough) and (slice_start == i):
in_fallthrough = True
if in_fallthrough:
if sibling[1]:
slice_end = i
else:
in_fallthrough = False
if (slice_start is not None) and (slice_end is not None):
slice_start_temp = slice_start
slice_start = len(siblings) - slice_end
slice_end = len(siblings) - slice_start_temp
fallthrough_case_names = sibling_names[slice_start-1:slice_end]
rval_str = ''
if not isinstance(n.rvalue, c_ast.Constant):
if not(isinstance(n.rvalue, c_ast.Constant) or isinstance(n.rvalue, c_ast.BinaryOp)):
rval_str = n.rvalue.name
if rval_str == self.state:
self.assignments.append((n,path))
self.assignments.append((n,path,fallthrough_case_names))
class EnumDefVisitor(c_ast.NodeVisitor):
def __init__(self, name):
@ -91,16 +156,21 @@ class SwitchCaseTermVisitor(c_ast.NodeVisitor):
self.hit = True
class SwitchCaseTranVisitor(c_ast.NodeVisitor):
def __init__(self, asm_node):
def __init__(self, asm_node, states, fallthrough_states):
super().__init__()
self.states = states
self.fallthrough_states = fallthrough_states
self._asm_node = asm_node
self.tran_table = []
def visit_Case(self, node):
state_from = node.children()[0][1].name
sctv = SwitchCaseTermVisitor(self._asm_node)
sctv.visit(node)
if sctv.hit:
self.tran_table.append((node.children()[0][1].name, self._asm_node.rvalue.name))
if sctv.hit and (state_from in self.states):
self.tran_table.append((state_from, self._asm_node.rvalue.name))
for ft in self.fallthrough_states:
self.tran_table.append((ft, self._asm_node.rvalue.name))
class SwitchCasePropertyVisitor(c_ast.NodeVisitor):
def __init__(self, state_asmts):
@ -115,7 +185,7 @@ class SwitchCasePropertyVisitor(c_ast.NodeVisitor):
if isinstance(node.lvalue, c_ast.StructRef):
lvalue = f"{node.lvalue.children()[0][1].name}->{node.lvalue.children()[1][1].name}";
else:
lvalue = f"{node.lvalue.name}<={node.rvalue.name}"
lvalue = node.lvalue.name
if isinstance(node.rvalue, c_ast.Constant):
rvalue = f"{node.rvalue.type }({node.rvalue.value})"