diff --git a/pythonparser.py b/pythonparser.py index c534a32ea93d1dffdea47886607d190114e2d588..2046d365edd2a01ec13ec3238a7b69f0dad2e6e6 100644 --- a/pythonparser.py +++ b/pythonparser.py @@ -244,16 +244,45 @@ def createTransition(path, vertices, modes): return edges class controller_ast(): + ''' + Initalizing function for a controller_ast object. + Reads in the code and parses it to a python ast and statement tree. + Statement tree is a tree of nodes that contain a list in their data. The list contains a single guard or a list of resets. + Variables (inputs to the controller) are collected. + Modes are collected from all enums that have the word "mode" in them. + Vertices are generated by taking the products of mode types. + ''' def __init__(self, code): self.code = code self.tree = ast.parse(code) - #statement tree is a tree of nodes that contain a list in their data. The list contains a single guard or a list of resets - self.statementtree, self.variables, self.modes = self.initalwalktree(code, tree) + self.statementtree, self.variables, self.modes = self.initalwalktree(code, self.tree) self.vertices = [] - + self.vertexStrings = [] + for vertex in itertools.product(*self.modes.values()): + self.vertices.append(vertex) + vertexstring = vertex[0] + for index in range(1,len(vertex)): + vertexstring += ";" + vertex[index] + self.vertexStrings.append(vertexstring) + self.paths = None + + + ''' + Function to populate paths variable with all paths of the controller. + ''' + def getAllPaths(self): + currentModes = [] + for modeTypes in self.modes.values(): + currentModes.extend(modeTypes) + self.paths = self.getNextModes(currentModes) + return self.paths - #assume that currentModes is a list of all node types! - #TODO: should we not force all modes be listed? Or rerun for each unknown/don't care node? Or add them all to the list + ''' + getNextModes takes in a list of current modes. It should include all modes. + getNextModes returns a list of paths that can be followed when in the given mode. + A path is a list of statements, all guards and resets along the path. They are in the order they are encountered in the code. + TODO: should we not force all modes be listed? Or rerun for each unknown/don't care node? Or add them all to the list + ''' def getNextModes(self, currentModes): #walk the tree and capture all paths that have modes that are listed. Path is a list of statements paths = [] @@ -263,6 +292,10 @@ class controller_ast(): return paths + ''' + Helper function to walk the statement tree from parentnode and find paths that are allowed in the currentMode. + Returns a list of paths. + ''' def walkstatements(self, parentnode, currentModes): nextsPaths = [] @@ -292,7 +325,15 @@ class controller_ast(): return nextsPaths - def create_json(input_file_name, output_file_name): + ''' + Function to create a json of the full graph. + Requires that paths class variables has been set. + ''' + def create_json(self, input_file_name, output_file_name): + if not self.paths: + print("Cannot call create_json without calling getAllPaths") + return + with open(input_file_name) as in_json_file: input_json = json.load(in_json_file) @@ -305,16 +346,16 @@ class controller_ast(): guards = [] resets = [] - for path in paths: - transitions = createTransition(path, vertices, modes) + for path in self.paths: + transitions = createTransition(path, self.vertices, self.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 + output_dict['vertex'] = self.vertexStrings #print(vertices) - output_dict['variables'] = variables + output_dict['variables'] = self.variables # #add edge, transition(guards) and resets output_dict['edge'] = edges #print(len(edges)) @@ -331,6 +372,10 @@ class controller_ast(): print("wrote json to " + output_file_name) #inital tree walk, parse into a tree of resets/modes + ''' + Function called by init function. Walks python ast and parses to a statement tree. + Returns a statement tree (nodes contain a list of either a single guard or muliple resets), the variables, and a mode dictionary + ''' def initalwalktree(self, code, tree): vars = [] out = [] @@ -346,17 +391,19 @@ class controller_ast(): if isinstance(node, ast.FunctionDef): if node.name == 'controller': #print(node.body) - out = self.parsenodelist(code, node.body, False, Tree(), None) + statementtree = self.parsenodelist(code, node.body, False, Tree(), None) #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] - + return [statementtree, vars, mode_dict] + ''' + Helper function for initalwalktree which parses the statements in the controller function into a statement tree + ''' def parsenodelist(self, code, nodes, addResets, tree, parent): childrens_guards=[] childrens_resets=[] @@ -407,80 +454,37 @@ if __name__ == "__main__": 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 = { } + #read in the controler code f = open(input_code_name,'r') code = f.read() - tree = ast.parse(code) - #tree = ast.parse() - #paths, variables, modes = walktree(code, tree) - test = controller_ast(code) - paths = test.getNextModes("NormalA;Normal3") - variables = test.variables - modes = test.modes + #parse the controller code into our controller ast objct + controller_obj = controller_ast(code) + #demonstrate you can check getNextModes after only initalizing + paths = controller_obj.getNextModes("NormalA;Normal3") + print("Results") for path in paths: for item in path: print(item.code) print() print("Done") - - #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) - - 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 = [] + #attempt to write to json, fail because we haven't populated paths yet + controller_obj.create_json(input_file_name, output_file_name) - 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) + controller_obj.getAllPaths() + + controller_obj.create_json(input_file_name, output_file_name) + + + +