From 0bcb8572004025bfe279378f2cbe50b838db24a1 Mon Sep 17 00:00:00 2001
From: Katherine Braught <braught2@illinois.edu>
Date: Mon, 11 Apr 2022 13:23:23 -0500
Subject: [PATCH] update main method to show easy usage

---
 pythonparser.py | 146 +++++++++++++++++++++++++-----------------------
 1 file changed, 75 insertions(+), 71 deletions(-)

diff --git a/pythonparser.py b/pythonparser.py
index c534a32e..2046d365 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)
+
+
+    
+    
 
-- 
GitLab