175 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #! /usr/bin/python3
 | |
| # SPDX-License-Identifier: GPL-2.0-only
 | |
| #
 | |
| # Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
 | |
| #
 | |
| # Automata object: parse an automata in dot file digraph format into a python object
 | |
| #
 | |
| # For further information, see:
 | |
| #   Documentation/trace/rv/deterministic_automata.rst
 | |
| 
 | |
| import ntpath
 | |
| 
 | |
| class Automata:
 | |
|     """Automata class: Reads a dot file and part it as an automata.
 | |
| 
 | |
|     Attributes:
 | |
|         dot_file: A dot file with an state_automaton definition.
 | |
|     """
 | |
| 
 | |
|     invalid_state_str = "INVALID_STATE"
 | |
| 
 | |
|     def __init__(self, file_path):
 | |
|         self.__dot_path = file_path
 | |
|         self.name = self.__get_model_name()
 | |
|         self.__dot_lines = self.__open_dot()
 | |
|         self.states, self.initial_state, self.final_states = self.__get_state_variables()
 | |
|         self.events = self.__get_event_variables()
 | |
|         self.function = self.__create_matrix()
 | |
| 
 | |
|     def __get_model_name(self):
 | |
|         basename = ntpath.basename(self.__dot_path)
 | |
|         if basename.endswith(".dot") == False:
 | |
|             print("not a dot file")
 | |
|             raise Exception("not a dot file: %s" % self.__dot_path)
 | |
| 
 | |
|         model_name = basename[0:-4]
 | |
|         if model_name.__len__() == 0:
 | |
|             raise Exception("not a dot file: %s" % self.__dot_path)
 | |
| 
 | |
|         return model_name
 | |
| 
 | |
|     def __open_dot(self):
 | |
|         cursor = 0
 | |
|         dot_lines = []
 | |
|         try:
 | |
|             dot_file = open(self.__dot_path)
 | |
|         except:
 | |
|             raise Exception("Cannot open the file: %s" % self.__dot_path)
 | |
| 
 | |
|         dot_lines = dot_file.read().splitlines()
 | |
|         dot_file.close()
 | |
| 
 | |
|         # checking the first line:
 | |
|         line = dot_lines[cursor].split()
 | |
| 
 | |
|         if (line[0] != "digraph") and (line[1] != "state_automaton"):
 | |
|             raise Exception("Not a valid .dot format: %s" % self.__dot_path)
 | |
|         else:
 | |
|             cursor += 1
 | |
|         return dot_lines
 | |
| 
 | |
|     def __get_cursor_begin_states(self):
 | |
|         cursor = 0
 | |
|         while self.__dot_lines[cursor].split()[0] != "{node":
 | |
|             cursor += 1
 | |
|         return cursor
 | |
| 
 | |
|     def __get_cursor_begin_events(self):
 | |
|         cursor = 0
 | |
|         while self.__dot_lines[cursor].split()[0] != "{node":
 | |
|            cursor += 1
 | |
|         while self.__dot_lines[cursor].split()[0] == "{node":
 | |
|            cursor += 1
 | |
|         # skip initial state transition
 | |
|         cursor += 1
 | |
|         return cursor
 | |
| 
 | |
|     def __get_state_variables(self):
 | |
|         # wait for node declaration
 | |
|         states = []
 | |
|         final_states = []
 | |
| 
 | |
|         has_final_states = False
 | |
|         cursor = self.__get_cursor_begin_states()
 | |
| 
 | |
|         # process nodes
 | |
|         while self.__dot_lines[cursor].split()[0] == "{node":
 | |
|             line = self.__dot_lines[cursor].split()
 | |
|             raw_state = line[-1]
 | |
| 
 | |
|             #  "enabled_fired"}; -> enabled_fired
 | |
|             state = raw_state.replace('"', '').replace('};', '').replace(',','_')
 | |
|             if state[0:7] == "__init_":
 | |
|                 initial_state = state[7:]
 | |
|             else:
 | |
|                 states.append(state)
 | |
|                 if self.__dot_lines[cursor].__contains__("doublecircle") == True:
 | |
|                     final_states.append(state)
 | |
|                     has_final_states = True
 | |
| 
 | |
|                 if self.__dot_lines[cursor].__contains__("ellipse") == True:
 | |
|                     final_states.append(state)
 | |
|                     has_final_states = True
 | |
| 
 | |
|             cursor += 1
 | |
| 
 | |
|         states = sorted(set(states))
 | |
|         states.remove(initial_state)
 | |
| 
 | |
|         # Insert the initial state at the bein og the states
 | |
|         states.insert(0, initial_state)
 | |
| 
 | |
|         if has_final_states == False:
 | |
|             final_states.append(initial_state)
 | |
| 
 | |
|         return states, initial_state, final_states
 | |
| 
 | |
|     def __get_event_variables(self):
 | |
|         # here we are at the begin of transitions, take a note, we will return later.
 | |
|         cursor = self.__get_cursor_begin_events()
 | |
| 
 | |
|         events = []
 | |
|         while self.__dot_lines[cursor][1] == '"':
 | |
|             # transitions have the format:
 | |
|             # "all_fired" -> "both_fired" [ label = "disable_irq" ];
 | |
|             #  ------------ event is here ------------^^^^^
 | |
|             if self.__dot_lines[cursor].split()[1] == "->":
 | |
|                 line = self.__dot_lines[cursor].split()
 | |
|                 event = line[-2].replace('"','')
 | |
| 
 | |
|                 # when a transition has more than one lables, they are like this
 | |
|                 # "local_irq_enable\nhw_local_irq_enable_n"
 | |
|                 # so split them.
 | |
| 
 | |
|                 event = event.replace("\\n", " ")
 | |
|                 for i in event.split():
 | |
|                     events.append(i)
 | |
|             cursor += 1
 | |
| 
 | |
|         return sorted(set(events))
 | |
| 
 | |
|     def __create_matrix(self):
 | |
|         # transform the array into a dictionary
 | |
|         events = self.events
 | |
|         states = self.states
 | |
|         events_dict = {}
 | |
|         states_dict = {}
 | |
|         nr_event = 0
 | |
|         for event in events:
 | |
|             events_dict[event] = nr_event
 | |
|             nr_event += 1
 | |
| 
 | |
|         nr_state = 0
 | |
|         for state in states:
 | |
|             states_dict[state] = nr_state
 | |
|             nr_state += 1
 | |
| 
 | |
|         # declare the matrix....
 | |
|         matrix = [[ self.invalid_state_str for x in range(nr_event)] for y in range(nr_state)]
 | |
| 
 | |
|         # and we are back! Let's fill the matrix
 | |
|         cursor = self.__get_cursor_begin_events()
 | |
| 
 | |
|         while self.__dot_lines[cursor][1] == '"':
 | |
|             if self.__dot_lines[cursor].split()[1] == "->":
 | |
|                 line = self.__dot_lines[cursor].split()
 | |
|                 origin_state = line[0].replace('"','').replace(',','_')
 | |
|                 dest_state = line[2].replace('"','').replace(',','_')
 | |
|                 possible_events = line[-2].replace('"','').replace("\\n", " ")
 | |
|                 for event in possible_events.split():
 | |
|                     matrix[states_dict[origin_state]][events_dict[event]] = dest_state
 | |
|             cursor += 1
 | |
| 
 | |
|         return matrix
 |