123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- #!/usr/bin/env python3
- # SPDX-License-Identifier: GPL-2.0-only
- #
- # Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <[email protected]>
- #
- # 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
|