pythonparser.py 9.53 KiB
#parse python file
#REQUIRES PYTHON 3.8!
from cgitb import reset
#import clang.cindex
import typing
import json
import sys
from typing import List, Tuple
import re
import itertools
import ast
#import pycfg
#from cfg import CFG
#import clang.cfg
class Edge:
def __init__(self, source, dest, guards, resets):
self.source = source
self.dest = dest
self.guards = guards
self.resets = resets
class Statement:
def __init__(self, code, mode, modeType):
self.code = code
self.modeType = modeType
self.mode = mode
def print(self):
print(self.code)
class Guard(Statement):
def __init__(self, code, mode, modeType):
super().__init__(code, mode, modeType)
def isModeCheck(self):
return self.modeType != None
def parseGuard(node, code):
#assume guard is a strict comparision (modeType == mode)
if isinstance(node.test, ast.Compare):
if isinstance(node.test.comparators[0], ast.Attribute):
if ("Mode" in str(node.test.comparators[0].value.id)):
modeType = str(node.test.comparators[0].value.id)
mode = str(node.test.comparators[0].attr)
return Guard(ast.get_source_segment(code, node.test), mode, modeType)
else:
return Guard(ast.get_source_segment(code, node.test), None, None)
class Reset(Statement):
def __init__(self, code, mode, modeType):
super().__init__(code, mode, modeType)
def isModeUpdate(self):
return self.modeType != None
def parseReset(node, code):
#assume reset is modeType = newMode
if isinstance(node.value, ast.Attribute):
#print("resets " + str(node.value.value.id))
#print("resets " + str(node.value.attr))
if ("Mode" in str(node.value.value.id)):
modeType = str(node.value.value.id)
mode = str(node.value.attr)
return Reset(ast.get_source_segment(code, node), mode, modeType)
return Reset(ast.get_source_segment(code, node), None, None)
def walktree(code, tree):
vars = []
out = []
mode_dict = {}
for node in ast.walk(tree): #don't think we want to walk the whole thing because lose ordering/depth
if isinstance(node, ast.ClassDef):
if "Mode" in node.name:
modeType = str(node.name)
modes = []
for modeValue in node.body:
modes.append(str(modeValue.targets[0].id))
mode_dict[modeType] = modes
if isinstance(node, ast.FunctionDef):
if node.name == 'controller':
#print(node.body)
out = parsenodelist(code, node.body, False, [])
#print(type(node.args))
args = node.args.args
for arg in args:
if "mode" not in arg.arg:
vars.append(arg.arg)
#todo: what to add for return values
return [out, vars, mode_dict]
def parsenodelist(code, nodes, addResets, pathsToMe):
childrens_guards=[]
childrens_resets=[]
recoutput = []
for childnode in nodes:
if isinstance(childnode, ast.Assign) and addResets:
reset = Reset.parseReset(childnode, code)
#print("found reset: " + reset.code)
childrens_resets.append(reset)
if isinstance(childnode, ast.If):
guard = Guard.parseGuard(childnode, code)
childrens_guards.append(guard)
#print("found if statement: " + guard.code)
tempresults = parsenodelist(code, childnode.body, True, [])
for result in tempresults:
recoutput.append([result, guard])
pathsafterme = []
if len(recoutput) == 0 and len(childrens_resets) > 0:
pathsafterme.append(childrens_resets)
else:
for path,ifstatement in recoutput:
newpath = path.copy()
newpath.extend(childrens_resets)
newpath.append(ifstatement)
pathsafterme.append(newpath)
return pathsafterme
def resetString(resets):
outstr = ""
for reset in resets:
outstr+= reset.code + ";"
outstr = outstr.strip(";")
return outstr
def parseGuardCode(code):
#TODO: should be more general and handle or
parts = code.split("and")
out = code
if len(parts) > 1:
left = parseGuardCode(parts[0])
right = parseGuardCode(parts[1])
out = "And(" + left + "," + right + ")"
return out
def guardString(guards):
output = ""
first = True
for guard in guards:
#print(type(condition))
if first:
output+= parseGuardCode(guard.code)
else:
output = "And(" + parseGuardCode(guard.code) + ",(" + output + "))"
first = False
return output
#modes are the list of all modes in the current vertex
#vertices are all the vertexs
def getIndex(modes, vertices):
#TODO: will this work if ordering is lost, will ordering be lost?
return vertices.index(tuple(modes))
#for index in range(0, len(vertices)):
# allMatch = True
# for mode in modes:
# if not (mode in vertices[index]):
# allMatch = False
# if allMatch:
# return index
return -1
def createTransition(path, vertices, modes):
guards = []
resets = []
modeChecks = []
modeUpdates = []
for item in path:
if isinstance(item, Guard):
if not item.isModeCheck():
guards.append(item)
else:
modeChecks.append(item)
if isinstance(item, Reset):
if not item.isModeUpdate():
resets.append(item)
else:
modeUpdates.append(item)
unfoundSourceModeTypes = []
sourceModes = []
unfoundDestModeTypes = []
destModes = []
for modeType in modes.keys():
foundMode = False
for condition in modeChecks:
#print(condition.modeType)
#print(modeType)
if condition.modeType == modeType:
sourceModes.append(condition.mode)
foundMode = True
if foundMode == False:
unfoundSourceModeTypes.append(modeType)
foundMode = False
for condition in modeUpdates:
if condition.modeType == modeType:
destModes.append(condition.mode)
foundMode = True
if foundMode == False:
unfoundDestModeTypes.append(modeType)
unfoundModes = []
for modeType in unfoundSourceModeTypes:
unfoundModes.append(modes[modeType])
unfoundModeCombinations = itertools.product(*unfoundModes)
sourceVertices = []
for unfoundModeCombo in unfoundModeCombinations:
sourceVertex = sourceModes.copy()
sourceVertex.extend(unfoundModeCombo)
sourceVertices.append(sourceVertex)
unfoundModes = []
for modeType in unfoundDestModeTypes:
unfoundModes.append(modes[modeType])
unfoundModeCombinations = itertools.product(*unfoundModes)
destVertices = []
for unfoundModeCombo in unfoundModeCombinations:
destVertex = destModes.copy()
destVertex.extend(unfoundModeCombo)
destVertices.append(destVertex)
edges = []
for source in sourceVertices:
sourceindex = getIndex(source, vertices)
for dest in destVertices:
destindex = getIndex(dest, vertices)
edges.append(Edge(sourceindex, destindex, guards, resets))
return edges
##main code###
#print(sys.argv)
if __name__ == "__main__":
#if len(sys.argv) < 4:
# print("incorrect usage. call createGraph.py program inputfile outputfilename")
# quit()
input_code_name = 'toythermomini.py' #sys.argv[1]
input_file_name = 'billiard_input.json' #sys.argv[2]
output_file_name = 'out.json' #sys.argv[3]
with open(input_file_name) as in_json_file:
input_json = json.load(in_json_file)
output_dict = {
}
f = open(input_code_name,'r')
code = f.read()
tree = ast.parse(code)
#tree = ast.parse()
paths, variables, modes = walktree(code, tree)
#print("Paths found:")
#for result in paths:
# for item in result:
#item.print()
#print(item.mode)
#print(item.modeType)
# print()
#print("Modes found: ")
#print(modes)
output_dict.update(input_json)
#TODO: create graph!
vertices = []
vertexStrings = []
for vertex in itertools.product(*modes.values()):
vertices.append(vertex)
vertexstring = vertex[0]
for index in range(1,len(vertex)):
vertexstring += ";" + vertex[index]
vertexStrings.append(vertexstring)
edges = []
guards = []
resets = []
for path in paths:
transitions = createTransition(path, vertices, modes)
for edge in transitions:
edges.append([edge.source, edge.dest])
guards.append(guardString(edge.guards))
resets.append(resetString(edge.resets))
output_dict['vertex'] = vertexStrings
#print(vertices)
output_dict['variables'] = variables
# #add edge, transition(guards) and resets
output_dict['edge'] = edges
#print(len(edges))
output_dict['guards'] = guards
#print(len(guards))
output_dict['resets'] = resets
#print(len(resets))
output_json = json.dumps(output_dict, indent=4)
outfile = open(output_file_name, "w")
outfile.write(output_json)
outfile.close()
print("wrote json to " + output_file_name)